Skip to content

Commit

Permalink
Add teams to repo on collaboration page. (#8045)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
davidsvantesson authored and lafriks committed Sep 23, 2019
1 parent 63ff616 commit a0e88df
Show file tree
Hide file tree
Showing 30 changed files with 575 additions and 79 deletions.
17 changes: 17 additions & 0 deletions models/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -1370,6 +1370,23 @@ func (err ErrTeamAlreadyExist) Error() string {
return fmt.Sprintf("team already exists [org_id: %d, name: %s]", err.OrgID, err.Name)
}

// ErrTeamNotExist represents a "TeamNotExist" error
type ErrTeamNotExist struct {
OrgID int64
TeamID int64
Name string
}

// IsErrTeamNotExist checks if an error is a ErrTeamNotExist.
func IsErrTeamNotExist(err error) bool {
_, ok := err.(ErrTeamNotExist)
return ok
}

func (err ErrTeamNotExist) Error() string {
return fmt.Sprintf("team does not exist [org_id %d, team_id %d, name: %s]", err.OrgID, err.TeamID, err.Name)
}

//
// Two-factor authentication
//
Expand Down
11 changes: 11 additions & 0 deletions models/fixtures/repository.yml
Original file line number Diff line number Diff line change
Expand Up @@ -508,4 +508,15 @@
num_stars: 0
num_forks: 0
num_issues: 0
is_mirror: false

-
id: 43
owner_id: 26
lower_name: repo26
name: repo26
is_private: true
num_stars: 0
num_forks: 0
num_issues: 0
is_mirror: false
9 changes: 9 additions & 0 deletions models/fixtures/team.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,12 @@
authorize: 1 # owner
num_repos: 0
num_members: 1

-
id: 11
org_id: 26
lower_name: team11
name: team11
authorize: 1 # read
num_repos: 0
num_members: 0
18 changes: 18 additions & 0 deletions models/fixtures/user.yml
Original file line number Diff line number Diff line change
Expand Up @@ -410,3 +410,21 @@
num_repos: 0
num_members: 1
num_teams: 1

-
id: 26
lower_name: org26
name: org26
full_name: "Org26"
email: org26@example.com
email_notifications_preference: onmention
passwd: 7d93daa0d1e6f2305cc8fa496847d61dc7320bb16262f9c55dd753480207234cdd96a93194e408341971742f4701772a025a # password
type: 1 # organization
salt: ZogKvWdyEx
is_admin: false
avatar: avatar26
avatar_email: org26@example.com
num_repos: 1
num_members: 0
num_teams: 1
repo_admin_change_team_access: true
2 changes: 2 additions & 0 deletions models/migrations/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,8 @@ var migrations = []Migration{
NewMigration("add table columns for cross referencing issues", addCrossReferenceColumns),
// v96 -> v97
NewMigration("delete orphaned attachments", deleteOrphanedAttachments),
// v97 -> v98
NewMigration("add repo_admin_change_team_access to user", addRepoAdminChangeTeamAccessColumnForUser),
}

// Migrate database to current version
Expand Down
15 changes: 15 additions & 0 deletions models/migrations/v97.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package migrations

import "github.com/go-xorm/xorm"

func addRepoAdminChangeTeamAccessColumnForUser(x *xorm.Engine) error {
type User struct {
RepoAdminChangeTeamAccess bool `xorm:"NOT NULL DEFAULT false"`
}

return x.Sync2(new(User))
}
8 changes: 1 addition & 7 deletions models/org.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
package models

import (
"errors"
"fmt"
"os"
"strings"
Expand All @@ -20,11 +19,6 @@ import (
"xorm.io/builder"
)

var (
// ErrTeamNotExist team does not exist
ErrTeamNotExist = errors.New("Team does not exist")
)

// IsOwnedBy returns true if given user is in the owner team.
func (org *User) IsOwnedBy(uid int64) (bool, error) {
return IsOrganizationOwner(org.ID, uid)
Expand Down Expand Up @@ -304,7 +298,7 @@ type OrgUser struct {
func isOrganizationOwner(e Engine, orgID, uid int64) (bool, error) {
ownerTeam, err := getOwnerTeam(e, orgID)
if err != nil {
if err == ErrTeamNotExist {
if IsErrTeamNotExist(err) {
log.Error("Organization does not have owner team: %d", orgID)
return false, nil
}
Expand Down
4 changes: 2 additions & 2 deletions models/org_team.go
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ func getTeam(e Engine, orgID int64, name string) (*Team, error) {
if err != nil {
return nil, err
} else if !has {
return nil, ErrTeamNotExist
return nil, ErrTeamNotExist{orgID, 0, name}
}
return t, nil
}
Expand All @@ -373,7 +373,7 @@ func getTeamByID(e Engine, teamID int64) (*Team, error) {
if err != nil {
return nil, err
} else if !has {
return nil, ErrTeamNotExist
return nil, ErrTeamNotExist{0, teamID, ""}
}
return t, nil
}
Expand Down
6 changes: 3 additions & 3 deletions models/org_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,11 @@ func TestUser_GetTeam(t *testing.T) {
assert.Equal(t, "team1", team.LowerName)

_, err = org.GetTeam("does not exist")
assert.Equal(t, ErrTeamNotExist, err)
assert.True(t, IsErrTeamNotExist(err))

nonOrg := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
_, err = nonOrg.GetTeam("team")
assert.Equal(t, ErrTeamNotExist, err)
assert.True(t, IsErrTeamNotExist(err))
}

func TestUser_GetOwnerTeam(t *testing.T) {
Expand All @@ -80,7 +80,7 @@ func TestUser_GetOwnerTeam(t *testing.T) {

nonOrg := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
_, err = nonOrg.GetOwnerTeam()
assert.Equal(t, ErrTeamNotExist, err)
assert.True(t, IsErrTeamNotExist(err))
}

func TestUser_GetTeams(t *testing.T) {
Expand Down
28 changes: 14 additions & 14 deletions models/repo_collaboration.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,6 @@ type Collaboration struct {
Mode AccessMode `xorm:"DEFAULT 2 NOT NULL"`
}

// ModeI18nKey returns the collaboration mode I18n Key
func (c *Collaboration) ModeI18nKey() string {
switch c.Mode {
case AccessModeRead:
return "repo.settings.collaboration.read"
case AccessModeWrite:
return "repo.settings.collaboration.write"
case AccessModeAdmin:
return "repo.settings.collaboration.admin"
default:
return "repo.settings.collaboration.undefined"
}
}

// AddCollaborator adds new collaboration to a repository with default access mode.
func (repo *Repository) AddCollaborator(u *User) error {
collaboration := &Collaboration{
Expand Down Expand Up @@ -183,3 +169,17 @@ func (repo *Repository) DeleteCollaboration(uid int64) (err error) {

return sess.Commit()
}

func (repo *Repository) getRepoTeams(e Engine) (teams []*Team, err error) {
return teams, e.
Join("INNER", "team_repo", "team_repo.team_id = team.id").
Where("team.org_id = ?", repo.OwnerID).
And("team_repo.repo_id=?", repo.ID).
OrderBy("CASE WHEN name LIKE '" + ownerTeamName + "' THEN '' ELSE name END").
Find(&teams)
}

// GetRepoTeams gets the list of teams that has access to the repository
func (repo *Repository) GetRepoTeams() ([]*Team, error) {
return repo.getRepoTeams(x)
}
11 changes: 0 additions & 11 deletions models/repo_collaboration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,6 @@ import (
"github.com/stretchr/testify/assert"
)

func TestCollaboration_ModeI18nKey(t *testing.T) {
assert.Equal(t, "repo.settings.collaboration.read",
(&Collaboration{Mode: AccessModeRead}).ModeI18nKey())
assert.Equal(t, "repo.settings.collaboration.write",
(&Collaboration{Mode: AccessModeWrite}).ModeI18nKey())
assert.Equal(t, "repo.settings.collaboration.admin",
(&Collaboration{Mode: AccessModeAdmin}).ModeI18nKey())
assert.Equal(t, "repo.settings.collaboration.undefined",
(&Collaboration{Mode: AccessModeNone}).ModeI18nKey())
}

func TestRepository_AddCollaborator(t *testing.T) {
assert.NoError(t, PrepareTestDatabase())

Expand Down
13 changes: 7 additions & 6 deletions models/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,12 +147,13 @@ type User struct {
NumRepos int

// For organization
NumTeams int
NumMembers int
Teams []*Team `xorm:"-"`
Members UserList `xorm:"-"`
MembersIsPublic map[int64]bool `xorm:"-"`
Visibility structs.VisibleType `xorm:"NOT NULL DEFAULT 0"`
NumTeams int
NumMembers int
Teams []*Team `xorm:"-"`
Members UserList `xorm:"-"`
MembersIsPublic map[int64]bool `xorm:"-"`
Visibility structs.VisibleType `xorm:"NOT NULL DEFAULT 0"`
RepoAdminChangeTeamAccess bool `xorm:"NOT NULL DEFAULT false"`

// Preferences
DiffViewStyle string `xorm:"NOT NULL DEFAULT ''"`
Expand Down
5 changes: 4 additions & 1 deletion models/user_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,10 @@ func TestSearchUsers(t *testing.T) {
testOrgSuccess(&SearchUserOptions{OrderBy: "id ASC", Page: 3, PageSize: 2},
[]int64{19, 25})

testOrgSuccess(&SearchUserOptions{Page: 4, PageSize: 2},
testOrgSuccess(&SearchUserOptions{OrderBy: "id ASC", Page: 4, PageSize: 2},
[]int64{26})

testOrgSuccess(&SearchUserOptions{Page: 5, PageSize: 2},
[]int64{})

// test users
Expand Down
2 changes: 1 addition & 1 deletion models/userlist.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func (users UserList) loadOrganizationOwners(e Engine, orgID int64) (map[int64]*
}
ownerTeam, err := getOwnerTeam(e, orgID)
if err != nil {
if err == ErrTeamNotExist {
if IsErrTeamNotExist(err) {
log.Error("Organization does not have owner team: %d", orgID)
return nil, nil
}
Expand Down
15 changes: 8 additions & 7 deletions modules/auth/org.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,14 @@ func (f *CreateOrgForm) Validate(ctx *macaron.Context, errs binding.Errors) bind

// UpdateOrgSettingForm form for updating organization settings
type UpdateOrgSettingForm struct {
Name string `binding:"Required;AlphaDashDot;MaxSize(40)" locale:"org.org_name_holder"`
FullName string `binding:"MaxSize(100)"`
Description string `binding:"MaxSize(255)"`
Website string `binding:"ValidUrl;MaxSize(255)"`
Location string `binding:"MaxSize(50)"`
Visibility structs.VisibleType
MaxRepoCreation int
Name string `binding:"Required;AlphaDashDot;MaxSize(40)" locale:"org.org_name_holder"`
FullName string `binding:"MaxSize(100)"`
Description string `binding:"MaxSize(255)"`
Website string `binding:"ValidUrl;MaxSize(255)"`
Location string `binding:"MaxSize(50)"`
Visibility structs.VisibleType
MaxRepoCreation int
RepoAdminChangeTeamAccess bool
}

// Validate validates the fields
Expand Down
23 changes: 13 additions & 10 deletions modules/structs/org.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ package structs

// Organization represents an organization
type Organization struct {
ID int64 `json:"id"`
UserName string `json:"username"`
FullName string `json:"full_name"`
AvatarURL string `json:"avatar_url"`
Description string `json:"description"`
Website string `json:"website"`
Location string `json:"location"`
Visibility string `json:"visibility"`
ID int64 `json:"id"`
UserName string `json:"username"`
FullName string `json:"full_name"`
AvatarURL string `json:"avatar_url"`
Description string `json:"description"`
Website string `json:"website"`
Location string `json:"location"`
Visibility string `json:"visibility"`
RepoAdminChangeTeamAccess bool `json:"repo_admin_change_team_access"`
}

// CreateOrgOption options for creating an organization
Expand All @@ -26,7 +27,8 @@ type CreateOrgOption struct {
Location string `json:"location"`
// possible values are `public` (default), `limited` or `private`
// enum: public,limited,private
Visibility string `json:"visibility" binding:"In(,public,limited,private)"`
Visibility string `json:"visibility" binding:"In(,public,limited,private)"`
RepoAdminChangeTeamAccess bool `json:"repo_admin_change_team_access"`
}

// EditOrgOption options for editing an organization
Expand All @@ -37,5 +39,6 @@ type EditOrgOption struct {
Location string `json:"location"`
// possible values are `public`, `limited` or `private`
// enum: public,limited,private
Visibility string `json:"visibility" binding:"In(,public,limited,private)"`
Visibility string `json:"visibility" binding:"In(,public,limited,private)"`
RepoAdminChangeTeamAccess bool `json:"repo_admin_change_team_access"`
}
9 changes: 9 additions & 0 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ enterred_invalid_repo_name = The repository name you entered is incorrect.
enterred_invalid_owner_name = The new owner name is not valid.
enterred_invalid_password = The password you entered is incorrect.
user_not_exist = The user does not exist.
team_not_exist = The team does not exist.
last_org_owner = You cannot remove the last user from the 'owners' team. There must be at least one owner in any given team.
cannot_add_org_to_team = An organization cannot be added as a team member.

Expand Down Expand Up @@ -1136,6 +1137,7 @@ settings.collaboration = Collaborators
settings.collaboration.admin = Administrator
settings.collaboration.write = Write
settings.collaboration.read = Read
settings.collaboration.owner = Owner
settings.collaboration.undefined = Undefined
settings.hooks = Webhooks
settings.githooks = Git Hooks
Expand Down Expand Up @@ -1217,6 +1219,11 @@ settings.collaborator_deletion_desc = Removing a collaborator will revoke their
settings.remove_collaborator_success = The collaborator has been removed.
settings.search_user_placeholder = Search user…
settings.org_not_allowed_to_be_collaborator = Organizations cannot be added as a collaborator.
settings.change_team_access_not_allowed = Changing team access for repository has been restricted to organization owner
settings.team_not_in_organization = The team is not in the same organization as the repository
settings.add_team_duplicate = Team already has the repository
settings.add_team_success = The team now have access to the repository.
settings.remove_team_success = The team's access to the repository has been removed.
settings.add_webhook = Add Webhook
settings.add_webhook.invalid_channel_name = Webhook channel name cannot be empty and cannot contain only a # character.
settings.hooks_desc = Webhooks automatically make HTTP POST requests to a server when certain Gitea events trigger. Read more in the <a target="_blank" rel="noopener noreferrer" href="%s">webhooks guide</a>.
Expand Down Expand Up @@ -1475,6 +1482,8 @@ settings.options = Organization
settings.full_name = Full Name
settings.website = Website
settings.location = Location
settings.permission = Permissions
settings.repoadminchangeteam = Repository admin can add and remove access for teams
settings.visibility = Visibility
settings.visibility.public = Public
settings.visibility.limited = Limited (Visible to logged in users only)
Expand Down
3 changes: 3 additions & 0 deletions public/css/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,8 @@ footer .ui.left,footer .ui.right{line-height:40px}
.repository.settings.collaboration .collaborator.list>.item:not(:last-child){border-bottom:1px solid #ddd}
.repository.settings.collaboration #repo-collab-form #search-user-box .results{left:7px}
.repository.settings.collaboration #repo-collab-form .ui.button{margin-left:5px;margin-top:-3px}
.repository.settings.collaboration #repo-collab-team-form #search-team-box .results{left:7px}
.repository.settings.collaboration #repo-collab-team-form .ui.button{margin-left:5px;margin-top:-3px}
.repository.settings.branches .protected-branches .selection.dropdown{width:300px}
.repository.settings.branches .protected-branches .item{border:1px solid #eaeaea;padding:10px 15px}
.repository.settings.branches .protected-branches .item:not(:last-child){border-bottom:0}
Expand Down Expand Up @@ -783,6 +785,7 @@ footer .ui.left,footer .ui.right{line-height:40px}
.user-cards .list .item .meta{margin-top:5px}
#search-repo-box .results .result .image,#search-user-box .results .result .image{float:left;margin-right:8px;width:2em;height:2em}
#search-repo-box .results .result .content,#search-user-box .results .result .content{margin:6px 0}
#search-team-box .results .result .content{margin:6px 0}
#issue-filters.hide{display:none}
#issue-actions{margin-top:-1rem!important}
#issue-actions.hide{display:none}
Expand Down
Loading

0 comments on commit a0e88df

Please sign in to comment.