mirror of
https://github.com/gogs/gogs.git
synced 2025-05-24 08:22:34 +00:00
db: migrate org.go
to orgs.go
with GORM
This commit is contained in:
parent
25fdeaac49
commit
10fd9e9e38
10
.github/workflows/go.yml
vendored
10
.github/workflows/go.yml
vendored
@ -61,7 +61,7 @@ jobs:
|
|||||||
name: Test
|
name: Test
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go-version: [ 1.20.x, 1.21.x ]
|
go-version: [ 1.21.x ]
|
||||||
platform: [ ubuntu-latest, macos-latest ]
|
platform: [ ubuntu-latest, macos-latest ]
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
steps:
|
steps:
|
||||||
@ -101,7 +101,7 @@ jobs:
|
|||||||
name: Test Windows
|
name: Test Windows
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go-version: [ 1.20.x, 1.21.x ]
|
go-version: [ 1.21.x ]
|
||||||
platform: [ windows-latest ]
|
platform: [ windows-latest ]
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
steps:
|
steps:
|
||||||
@ -139,7 +139,7 @@ jobs:
|
|||||||
name: Postgres
|
name: Postgres
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go-version: [ 1.20.x, 1.21.x ]
|
go-version: [ 1.21.x ]
|
||||||
platform: [ ubuntu-latest ]
|
platform: [ ubuntu-latest ]
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
services:
|
services:
|
||||||
@ -175,7 +175,7 @@ jobs:
|
|||||||
name: MySQL
|
name: MySQL
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go-version: [ 1.20.x, 1.21.x ]
|
go-version: [ 1.21.x ]
|
||||||
platform: [ ubuntu-20.04 ]
|
platform: [ ubuntu-20.04 ]
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
steps:
|
steps:
|
||||||
@ -200,7 +200,7 @@ jobs:
|
|||||||
name: SQLite - Go
|
name: SQLite - Go
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go-version: [ 1.20.x, 1.21.x ]
|
go-version: [ 1.21.x ]
|
||||||
platform: [ ubuntu-latest ]
|
platform: [ ubuntu-latest ]
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
steps:
|
steps:
|
||||||
|
@ -129,3 +129,22 @@ Primary keys: id
|
|||||||
Primary keys: id
|
Primary keys: id
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# Table "org_user"
|
||||||
|
|
||||||
|
```
|
||||||
|
FIELD | COLUMN | POSTGRESQL | MYSQL | SQLITE3
|
||||||
|
-----------+-----------+--------------------------------+--------------------------------+---------------------------------
|
||||||
|
ID | id | BIGSERIAL | BIGINT AUTO_INCREMENT | INTEGER
|
||||||
|
UserID | uid | BIGINT NOT NULL | BIGINT NOT NULL | INTEGER NOT NULL
|
||||||
|
OrgID | org_id | BIGINT NOT NULL | BIGINT NOT NULL | INTEGER NOT NULL
|
||||||
|
IsPublic | is_public | BOOLEAN NOT NULL DEFAULT FALSE | BOOLEAN NOT NULL DEFAULT FALSE | NUMERIC NOT NULL DEFAULT FALSE
|
||||||
|
IsOwner | is_owner | BOOLEAN NOT NULL DEFAULT FALSE | BOOLEAN NOT NULL DEFAULT FALSE | NUMERIC NOT NULL DEFAULT FALSE
|
||||||
|
NumTeams | num_teams | BIGINT NOT NULL DEFAULT 0 | BIGINT NOT NULL DEFAULT 0 | INTEGER NOT NULL DEFAULT 0
|
||||||
|
|
||||||
|
Primary keys: id
|
||||||
|
Indexes:
|
||||||
|
"idx_org_user_org_id" (org_id)
|
||||||
|
"idx_org_user_user_id" (uid)
|
||||||
|
"org_user_user_org_unique" UNIQUE (uid, org_id)
|
||||||
|
```
|
||||||
|
|
||||||
|
@ -73,8 +73,8 @@ func HandleOrgAssignment(c *Context, args ...bool) {
|
|||||||
c.Org.IsMember = true
|
c.Org.IsMember = true
|
||||||
c.Org.IsTeamMember = true
|
c.Org.IsTeamMember = true
|
||||||
c.Org.IsTeamAdmin = true
|
c.Org.IsTeamAdmin = true
|
||||||
} else if org.IsOrgMember(c.User.ID) {
|
} else {
|
||||||
c.Org.IsMember = true
|
c.Org.IsMember, _ = db.Orgs.HasMember(c.Req.Context(), org.ID, c.User.ID)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Fake data.
|
// Fake data.
|
||||||
|
@ -154,7 +154,7 @@ func (db *accessTokens) GetBySHA1(ctx context.Context, sha1 string) (*AccessToke
|
|||||||
token := new(AccessToken)
|
token := new(AccessToken)
|
||||||
err := db.WithContext(ctx).Where("sha256 = ?", sha256).First(token).Error
|
err := db.WithContext(ctx).Where("sha256 = ?", sha256).First(token).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == gorm.ErrRecordNotFound {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return nil, ErrAccessTokenNotExist{args: errutil.Args{"sha": sha1}}
|
return nil, ErrAccessTokenNotExist{args: errutil.Args{"sha": sha1}}
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -86,17 +86,18 @@ func (db *actions) listByOrganization(ctx context.Context, orgID, actorID, after
|
|||||||
/*
|
/*
|
||||||
Equivalent SQL for PostgreSQL:
|
Equivalent SQL for PostgreSQL:
|
||||||
|
|
||||||
SELECT * FROM "action"
|
<SELECT * FROM "action">
|
||||||
WHERE
|
WHERE
|
||||||
user_id = @userID
|
user_id = @userID
|
||||||
AND (@skipAfter OR id < @afterID)
|
AND (@skipAfter OR id < @afterID)
|
||||||
AND repo_id IN (
|
AND repo_id IN (
|
||||||
SELECT repository.id FROM "repository"
|
SELECT repository.id FROM "repository"
|
||||||
JOIN team_repo ON repository.id = team_repo.repo_id
|
JOIN team_repo ON repository.id = team_repo.repo_id
|
||||||
WHERE team_repo.team_id IN (
|
|
||||||
SELECT team_id FROM "team_user"
|
|
||||||
WHERE
|
WHERE
|
||||||
team_user.org_id = @orgID AND uid = @actorID)
|
team_repo.team_id IN (
|
||||||
|
SELECT team_id FROM "team_user"
|
||||||
|
WHERE team_user.org_id = @orgID AND uid = @actorID)
|
||||||
|
)
|
||||||
OR (repository.is_private = FALSE AND repository.is_unlisted = FALSE)
|
OR (repository.is_private = FALSE AND repository.is_unlisted = FALSE)
|
||||||
)
|
)
|
||||||
ORDER BY id DESC
|
ORDER BY id DESC
|
||||||
@ -120,8 +121,8 @@ func (db *actions) listByOrganization(ctx context.Context, orgID, actorID, after
|
|||||||
).
|
).
|
||||||
Or("repository.is_private = ? AND repository.is_unlisted = ?", false, false),
|
Or("repository.is_private = ? AND repository.is_unlisted = ?", false, false),
|
||||||
).
|
).
|
||||||
Limit(conf.UI.User.NewsFeedPagingNum).
|
Order("id DESC").
|
||||||
Order("id DESC")
|
Limit(conf.UI.User.NewsFeedPagingNum)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *actions) ListByOrganization(ctx context.Context, orgID, actorID, afterID int64) ([]*Action, error) {
|
func (db *actions) ListByOrganization(ctx context.Context, orgID, actorID, afterID int64) ([]*Action, error) {
|
||||||
@ -133,7 +134,7 @@ func (db *actions) listByUser(ctx context.Context, userID, actorID, afterID int6
|
|||||||
/*
|
/*
|
||||||
Equivalent SQL for PostgreSQL:
|
Equivalent SQL for PostgreSQL:
|
||||||
|
|
||||||
SELECT * FROM "action"
|
<SELECT * FROM "action">
|
||||||
WHERE
|
WHERE
|
||||||
user_id = @userID
|
user_id = @userID
|
||||||
AND (@skipAfter OR id < @afterID)
|
AND (@skipAfter OR id < @afterID)
|
||||||
@ -153,8 +154,8 @@ func (db *actions) listByUser(ctx context.Context, userID, actorID, afterID int6
|
|||||||
Where("?", !isProfile || actorID == userID).
|
Where("?", !isProfile || actorID == userID).
|
||||||
Or("is_private = ? AND act_user_id = ?", false, userID),
|
Or("is_private = ? AND act_user_id = ?", false, userID),
|
||||||
).
|
).
|
||||||
Limit(conf.UI.User.NewsFeedPagingNum).
|
Order("id DESC").
|
||||||
Order("id DESC")
|
Limit(conf.UI.User.NewsFeedPagingNum)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *actions) ListByUser(ctx context.Context, userID, actorID, afterID int64, isProfile bool) ([]*Action, error) {
|
func (db *actions) ListByUser(ctx context.Context, userID, actorID, afterID int64, isProfile bool) ([]*Action, error) {
|
||||||
|
@ -31,7 +31,7 @@ func TestDumpAndImport(t *testing.T) {
|
|||||||
}
|
}
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
const wantTables = 8
|
const wantTables = 9
|
||||||
if len(Tables) != wantTables {
|
if len(Tables) != wantTables {
|
||||||
t.Fatalf("New table has added (want %d got %d), please add new tests for the table and update this check", wantTables, len(Tables))
|
t.Fatalf("New table has added (want %d got %d), please add new tests for the table and update this check", wantTables, len(Tables))
|
||||||
}
|
}
|
||||||
@ -197,6 +197,23 @@ func setupDBToDump(t *testing.T, db *gorm.DB) {
|
|||||||
Description: "This is a notice",
|
Description: "This is a notice",
|
||||||
CreatedUnix: 1588568886,
|
CreatedUnix: 1588568886,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
&OrgUser{
|
||||||
|
ID: 1,
|
||||||
|
UserID: 1,
|
||||||
|
OrgID: 11,
|
||||||
|
IsPublic: true,
|
||||||
|
IsOwner: true,
|
||||||
|
NumTeams: 3,
|
||||||
|
},
|
||||||
|
&OrgUser{
|
||||||
|
ID: 2,
|
||||||
|
UserID: 2,
|
||||||
|
OrgID: 11,
|
||||||
|
IsPublic: false,
|
||||||
|
IsOwner: false,
|
||||||
|
NumTeams: 0,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, val := range vals {
|
for _, val := range vals {
|
||||||
err := db.Create(val).Error
|
err := db.Create(val).Error
|
||||||
|
@ -46,6 +46,7 @@ var Tables = []any{
|
|||||||
new(Follow),
|
new(Follow),
|
||||||
new(LFSObject), new(LoginSource),
|
new(LFSObject), new(LoginSource),
|
||||||
new(Notice),
|
new(Notice),
|
||||||
|
new(OrgUser),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init initializes the database with given logger.
|
// Init initializes the database with given logger.
|
||||||
|
@ -133,26 +133,6 @@ func (err ErrDeployKeyNameAlreadyUsed) Error() string {
|
|||||||
return fmt.Sprintf("public key already exists [repo_id: %d, name: %s]", err.RepoID, err.Name)
|
return fmt.Sprintf("public key already exists [repo_id: %d, name: %s]", err.RepoID, err.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ________ .__ __ .__
|
|
||||||
// \_____ \_______ _________ ____ |__|____________ _/ |_|__| ____ ____
|
|
||||||
// / | \_ __ \/ ___\__ \ / \| \___ /\__ \\ __\ |/ _ \ / \
|
|
||||||
// / | \ | \/ /_/ > __ \| | \ |/ / / __ \| | | ( <_> ) | \
|
|
||||||
// \_______ /__| \___ (____ /___| /__/_____ \(____ /__| |__|\____/|___| /
|
|
||||||
// \/ /_____/ \/ \/ \/ \/ \/
|
|
||||||
|
|
||||||
type ErrLastOrgOwner struct {
|
|
||||||
UID int64
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsErrLastOrgOwner(err error) bool {
|
|
||||||
_, ok := err.(ErrLastOrgOwner)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrLastOrgOwner) Error() string {
|
|
||||||
return fmt.Sprintf("user is the last member of owner team [uid: %d]", err.UID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// __________ .__ __
|
// __________ .__ __
|
||||||
// \______ \ ____ ______ ____ _____|__|/ |_ ___________ ___.__.
|
// \______ \ ____ ______ ____ _____|__|/ |_ ___________ ___.__.
|
||||||
// | _// __ \\____ \ / _ \/ ___/ \ __\/ _ \_ __ < | |
|
// | _// __ \\____ \ / _ \/ ___/ \ __\/ _ \_ __ < | |
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"gogs.io/gogs/internal/errutil"
|
"gogs.io/gogs/internal/errutil"
|
||||||
@ -75,7 +76,7 @@ func (db *lfs) GetObjectByOID(ctx context.Context, repoID int64, oid lfsutil.OID
|
|||||||
object := new(LFSObject)
|
object := new(LFSObject)
|
||||||
err := db.WithContext(ctx).Where("repo_id = ? AND oid = ?", repoID, oid).First(object).Error
|
err := db.WithContext(ctx).Where("repo_id = ? AND oid = ?", repoID, oid).First(object).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == gorm.ErrRecordNotFound {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return nil, ErrLFSObjectNotExist{args: errutil.Args{"repoID": repoID, "oid": oid}}
|
return nil, ErrLFSObjectNotExist{args: errutil.Args{"repoID": repoID, "oid": oid}}
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -264,7 +264,7 @@ func (db *loginSources) GetByID(ctx context.Context, id int64) (*LoginSource, er
|
|||||||
source := new(LoginSource)
|
source := new(LoginSource)
|
||||||
err := db.WithContext(ctx).Where("id = ?", id).First(source).Error
|
err := db.WithContext(ctx).Where("id = ?", id).First(source).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == gorm.ErrRecordNotFound {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return db.files.GetByID(id)
|
return db.files.GetByID(id)
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -80,7 +80,7 @@ func Migrate(db *gorm.DB) error {
|
|||||||
|
|
||||||
var current Version
|
var current Version
|
||||||
err := db.Where("id = ?", 1).First(¤t).Error
|
err := db.Where("id = ?", 1).First(¤t).Error
|
||||||
if err == gorm.ErrRecordNotFound {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
err = db.Create(
|
err = db.Create(
|
||||||
&Version{
|
&Version{
|
||||||
ID: 1,
|
ID: 1,
|
||||||
|
@ -57,7 +57,7 @@ func init() {
|
|||||||
new(Label), new(IssueLabel), new(Milestone),
|
new(Label), new(IssueLabel), new(Milestone),
|
||||||
new(Mirror), new(Release), new(Webhook), new(HookTask),
|
new(Mirror), new(Release), new(Webhook), new(HookTask),
|
||||||
new(ProtectBranch), new(ProtectBranchWhitelist),
|
new(ProtectBranch), new(ProtectBranchWhitelist),
|
||||||
new(Team), new(OrgUser), new(TeamUser), new(TeamRepo),
|
new(Team), new(TeamUser), new(TeamRepo),
|
||||||
)
|
)
|
||||||
|
|
||||||
gonicNames := []string{"SSL"}
|
gonicNames := []string{"SSL"}
|
||||||
@ -211,7 +211,7 @@ type Statistic struct {
|
|||||||
|
|
||||||
func GetStatistic(ctx context.Context) (stats Statistic) {
|
func GetStatistic(ctx context.Context) (stats Statistic) {
|
||||||
stats.Counter.User = Users.Count(ctx)
|
stats.Counter.User = Users.Count(ctx)
|
||||||
stats.Counter.Org = CountOrganizations()
|
stats.Counter.Org = Orgs.Count(ctx)
|
||||||
stats.Counter.PublicKey, _ = x.Count(new(PublicKey))
|
stats.Counter.PublicKey, _ = x.Count(new(PublicKey))
|
||||||
stats.Counter.Repo = CountRepositories(true)
|
stats.Counter.Repo = CountRepositories(true)
|
||||||
stats.Counter.Watch, _ = x.Count(new(Watch))
|
stats.Counter.Watch, _ = x.Count(new(Watch))
|
||||||
|
@ -6,7 +6,6 @@ package db
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@ -19,87 +18,6 @@ import (
|
|||||||
"gogs.io/gogs/internal/userutil"
|
"gogs.io/gogs/internal/userutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrOrgNotExist = errors.New("Organization does not exist")
|
|
||||||
|
|
||||||
// IsOwnedBy returns true if given user is in the owner team.
|
|
||||||
func (org *User) IsOwnedBy(userID int64) bool {
|
|
||||||
return IsOrganizationOwner(org.ID, userID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsOrgMember returns true if given user is member of organization.
|
|
||||||
func (org *User) IsOrgMember(uid int64) bool {
|
|
||||||
return org.IsOrganization() && IsOrganizationMember(org.ID, uid)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (org *User) getTeam(e Engine, name string) (*Team, error) {
|
|
||||||
return getTeamOfOrgByName(e, org.ID, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTeamOfOrgByName returns named team of organization.
|
|
||||||
func (org *User) GetTeam(name string) (*Team, error) {
|
|
||||||
return org.getTeam(x, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (org *User) getOwnerTeam(e Engine) (*Team, error) {
|
|
||||||
return org.getTeam(e, OWNER_TEAM)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetOwnerTeam returns owner team of organization.
|
|
||||||
func (org *User) GetOwnerTeam() (*Team, error) {
|
|
||||||
return org.getOwnerTeam(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (org *User) getTeams(e Engine) (err error) {
|
|
||||||
org.Teams, err = getTeamsByOrgID(e, org.ID)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTeams returns all teams that belong to organization.
|
|
||||||
func (org *User) GetTeams() error {
|
|
||||||
return org.getTeams(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TeamsHaveAccessToRepo returns all teams that have given access level to the repository.
|
|
||||||
func (org *User) TeamsHaveAccessToRepo(repoID int64, mode AccessMode) ([]*Team, error) {
|
|
||||||
return GetTeamsHaveAccessToRepo(org.ID, repoID, mode)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetMembers returns all members of organization.
|
|
||||||
func (org *User) GetMembers(limit int) error {
|
|
||||||
ous, err := GetOrgUsersByOrgID(org.ID, limit)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
org.Members = make([]*User, len(ous))
|
|
||||||
for i, ou := range ous {
|
|
||||||
org.Members[i], err = Users.GetByID(context.TODO(), ou.Uid)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddMember adds new member to organization.
|
|
||||||
func (org *User) AddMember(uid int64) error {
|
|
||||||
return AddOrgUser(org.ID, uid)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveMember removes member from organization.
|
|
||||||
func (org *User) RemoveMember(uid int64) error {
|
|
||||||
return RemoveOrgUser(org.ID, uid)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (org *User) removeOrgRepo(e Engine, repoID int64) error {
|
|
||||||
return removeOrgRepo(e, org.ID, repoID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveOrgRepo removes all team-repository relations of organization.
|
|
||||||
func (org *User) RemoveOrgRepo(repoID int64) error {
|
|
||||||
return org.removeOrgRepo(x, repoID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateOrganization creates record of a new organization.
|
// CreateOrganization creates record of a new organization.
|
||||||
func CreateOrganization(org, owner *User) (err error) {
|
func CreateOrganization(org, owner *User) (err error) {
|
||||||
if err = isUsernameAllowed(org.Name); err != nil {
|
if err = isUsernameAllowed(org.Name); err != nil {
|
||||||
@ -139,7 +57,7 @@ func CreateOrganization(org, owner *User) (err error) {
|
|||||||
|
|
||||||
// Add initial creator to organization and owner team.
|
// Add initial creator to organization and owner team.
|
||||||
if _, err = sess.Insert(&OrgUser{
|
if _, err = sess.Insert(&OrgUser{
|
||||||
Uid: owner.ID,
|
UserID: owner.ID,
|
||||||
OrgID: org.ID,
|
OrgID: org.ID,
|
||||||
IsOwner: true,
|
IsOwner: true,
|
||||||
NumTeams: 1,
|
NumTeams: 1,
|
||||||
@ -150,8 +68,8 @@ func CreateOrganization(org, owner *User) (err error) {
|
|||||||
// Create default owner team.
|
// Create default owner team.
|
||||||
t := &Team{
|
t := &Team{
|
||||||
OrgID: org.ID,
|
OrgID: org.ID,
|
||||||
LowerName: strings.ToLower(OWNER_TEAM),
|
LowerName: strings.ToLower(TeamNameOwners),
|
||||||
Name: OWNER_TEAM,
|
Name: TeamNameOwners,
|
||||||
Authorize: AccessModeOwner,
|
Authorize: AccessModeOwner,
|
||||||
NumMembers: 1,
|
NumMembers: 1,
|
||||||
}
|
}
|
||||||
@ -174,30 +92,6 @@ func CreateOrganization(org, owner *User) (err error) {
|
|||||||
return sess.Commit()
|
return sess.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOrgByName returns organization by given name.
|
|
||||||
func GetOrgByName(name string) (*User, error) {
|
|
||||||
if name == "" {
|
|
||||||
return nil, ErrOrgNotExist
|
|
||||||
}
|
|
||||||
u := &User{
|
|
||||||
LowerName: strings.ToLower(name),
|
|
||||||
Type: UserTypeOrganization,
|
|
||||||
}
|
|
||||||
has, err := x.Get(u)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else if !has {
|
|
||||||
return nil, ErrOrgNotExist
|
|
||||||
}
|
|
||||||
return u, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CountOrganizations returns number of organizations.
|
|
||||||
func CountOrganizations() int64 {
|
|
||||||
count, _ := x.Where("type=1").Count(new(User))
|
|
||||||
return count
|
|
||||||
}
|
|
||||||
|
|
||||||
// Organizations returns number of organizations in given page.
|
// Organizations returns number of organizations in given page.
|
||||||
func Organizations(page, pageSize int) ([]*User, error) {
|
func Organizations(page, pageSize int) ([]*User, error) {
|
||||||
orgs := make([]*User, 0, pageSize)
|
orgs := make([]*User, 0, pageSize)
|
||||||
@ -237,41 +131,6 @@ func DeleteOrganization(org *User) error {
|
|||||||
return sess.Commit()
|
return sess.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ________ ____ ___
|
|
||||||
// \_____ \_______ ____ | | \______ ___________
|
|
||||||
// / | \_ __ \/ ___\| | / ___// __ \_ __ \
|
|
||||||
// / | \ | \/ /_/ > | /\___ \\ ___/| | \/
|
|
||||||
// \_______ /__| \___ /|______//____ >\___ >__|
|
|
||||||
// \/ /_____/ \/ \/
|
|
||||||
|
|
||||||
// OrgUser represents relations of organizations and their members.
|
|
||||||
type OrgUser struct {
|
|
||||||
ID int64 `gorm:"primaryKey"`
|
|
||||||
Uid int64 `xorm:"INDEX UNIQUE(s)" gorm:"uniqueIndex:org_user_user_org_unique;index;not null"`
|
|
||||||
OrgID int64 `xorm:"INDEX UNIQUE(s)" gorm:"uniqueIndex:org_user_user_org_unique;index;not null"`
|
|
||||||
IsPublic bool `gorm:"not null;default:FALSE"`
|
|
||||||
IsOwner bool `gorm:"not null;default:FALSE"`
|
|
||||||
NumTeams int `gorm:"not null;default:0"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsOrganizationOwner returns true if given user is in the owner team.
|
|
||||||
func IsOrganizationOwner(orgID, userID int64) bool {
|
|
||||||
has, _ := x.Where("is_owner = ?", true).And("uid = ?", userID).And("org_id = ?", orgID).Get(new(OrgUser))
|
|
||||||
return has
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsOrganizationMember returns true if given user is member of organization.
|
|
||||||
func IsOrganizationMember(orgId, uid int64) bool {
|
|
||||||
has, _ := x.Where("uid=?", uid).And("org_id=?", orgId).Get(new(OrgUser))
|
|
||||||
return has
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsPublicMembership returns true if given user public his/her membership.
|
|
||||||
func IsPublicMembership(orgId, uid int64) bool {
|
|
||||||
has, _ := x.Where("uid=?", uid).And("org_id=?", orgId).And("is_public=?", true).Get(new(OrgUser))
|
|
||||||
return has
|
|
||||||
}
|
|
||||||
|
|
||||||
func getOrgsByUserID(sess *xorm.Session, userID int64, showAll bool) ([]*User, error) {
|
func getOrgsByUserID(sess *xorm.Session, userID int64, showAll bool) ([]*User, error) {
|
||||||
orgs := make([]*User, 0, 10)
|
orgs := make([]*User, 0, 10)
|
||||||
if !showAll {
|
if !showAll {
|
||||||
@ -287,18 +146,13 @@ func GetOrgsByUserID(userID int64, showAll bool) ([]*User, error) {
|
|||||||
return getOrgsByUserID(x.NewSession(), userID, showAll)
|
return getOrgsByUserID(x.NewSession(), userID, showAll)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getOwnedOrgsByUserID returns a list of organizations are owned by given user ID.
|
||||||
func getOwnedOrgsByUserID(sess *xorm.Session, userID int64) ([]*User, error) {
|
func getOwnedOrgsByUserID(sess *xorm.Session, userID int64) ([]*User, error) {
|
||||||
orgs := make([]*User, 0, 10)
|
orgs := make([]*User, 0, 10)
|
||||||
return orgs, sess.Where("`org_user`.uid=?", userID).And("`org_user`.is_owner=?", true).
|
return orgs, sess.Where("`org_user`.uid=?", userID).And("`org_user`.is_owner=?", true).
|
||||||
Join("INNER", "`org_user`", "`org_user`.org_id=`user`.id").Find(&orgs)
|
Join("INNER", "`org_user`", "`org_user`.org_id=`user`.id").Find(&orgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOwnedOrgsByUserID returns a list of organizations are owned by given user ID.
|
|
||||||
func GetOwnedOrgsByUserID(userID int64) ([]*User, error) {
|
|
||||||
sess := x.NewSession()
|
|
||||||
return getOwnedOrgsByUserID(sess, userID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetOwnedOrganizationsByUserIDDesc returns a list of organizations are owned by
|
// GetOwnedOrganizationsByUserIDDesc returns a list of organizations are owned by
|
||||||
// given user ID, ordered descending by the given condition.
|
// given user ID, ordered descending by the given condition.
|
||||||
func GetOwnedOrgsByUserIDDesc(userID int64, desc string) ([]*User, error) {
|
func GetOwnedOrgsByUserIDDesc(userID int64, desc string) ([]*User, error) {
|
||||||
@ -306,6 +160,7 @@ func GetOwnedOrgsByUserIDDesc(userID int64, desc string) ([]*User, error) {
|
|||||||
return getOwnedOrgsByUserID(sess.Desc(desc), userID)
|
return getOwnedOrgsByUserID(sess.Desc(desc), userID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getOrgUsersByOrgID returns all organization-user relations by organization ID.
|
||||||
func getOrgUsersByOrgID(e Engine, orgID int64, limit int) ([]*OrgUser, error) {
|
func getOrgUsersByOrgID(e Engine, orgID int64, limit int) ([]*OrgUser, error) {
|
||||||
orgUsers := make([]*OrgUser, 0, 10)
|
orgUsers := make([]*OrgUser, 0, 10)
|
||||||
|
|
||||||
@ -316,221 +171,9 @@ func getOrgUsersByOrgID(e Engine, orgID int64, limit int) ([]*OrgUser, error) {
|
|||||||
return orgUsers, sess.Find(&orgUsers)
|
return orgUsers, sess.Find(&orgUsers)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOrgUsersByOrgID returns all organization-user relations by organization ID.
|
|
||||||
func GetOrgUsersByOrgID(orgID int64, limit int) ([]*OrgUser, error) {
|
|
||||||
return getOrgUsersByOrgID(x, orgID, limit)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChangeOrgUserStatus changes public or private membership status.
|
|
||||||
func ChangeOrgUserStatus(orgID, uid int64, public bool) error {
|
|
||||||
ou := new(OrgUser)
|
|
||||||
has, err := x.Where("uid=?", uid).And("org_id=?", orgID).Get(ou)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
} else if !has {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ou.IsPublic = public
|
|
||||||
_, err = x.Id(ou.ID).AllCols().Update(ou)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddOrgUser adds new user to given organization.
|
|
||||||
func AddOrgUser(orgID, uid int64) error {
|
|
||||||
if IsOrganizationMember(orgID, uid) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
sess := x.NewSession()
|
|
||||||
defer sess.Close()
|
|
||||||
if err := sess.Begin(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ou := &OrgUser{
|
|
||||||
Uid: uid,
|
|
||||||
OrgID: orgID,
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := sess.Insert(ou); err != nil {
|
|
||||||
return err
|
|
||||||
} else if _, err = sess.Exec("UPDATE `user` SET num_members = num_members + 1 WHERE id = ?", orgID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return sess.Commit()
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveOrgUser removes user from given organization.
|
|
||||||
func RemoveOrgUser(orgID, userID int64) error {
|
|
||||||
ou := new(OrgUser)
|
|
||||||
|
|
||||||
has, err := x.Where("uid=?", userID).And("org_id=?", orgID).Get(ou)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("get org-user: %v", err)
|
|
||||||
} else if !has {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
user, err := Users.GetByID(context.TODO(), userID)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("GetUserByID [%d]: %v", userID, err)
|
|
||||||
}
|
|
||||||
org, err := Users.GetByID(context.TODO(), orgID)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("GetUserByID [%d]: %v", orgID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: only need to get IDs here, not all fields of repository.
|
|
||||||
repos, _, err := org.GetUserRepositories(user.ID, 1, org.NumRepos)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("GetUserRepositories [%d]: %v", user.ID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the user to delete is the last member in owner team.
|
|
||||||
if IsOrganizationOwner(orgID, userID) {
|
|
||||||
t, err := org.GetOwnerTeam()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if t.NumMembers == 1 {
|
|
||||||
return ErrLastOrgOwner{UID: userID}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sess := x.NewSession()
|
|
||||||
defer sess.Close()
|
|
||||||
if err := sess.Begin(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := sess.ID(ou.ID).Delete(ou); err != nil {
|
|
||||||
return err
|
|
||||||
} else if _, err = sess.Exec("UPDATE `user` SET num_members=num_members-1 WHERE id=?", orgID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete all repository accesses and unwatch them.
|
|
||||||
repoIDs := make([]int64, len(repos))
|
|
||||||
for i := range repos {
|
|
||||||
repoIDs = append(repoIDs, repos[i].ID)
|
|
||||||
if err = watchRepo(sess, user.ID, repos[i].ID, false); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(repoIDs) > 0 {
|
|
||||||
if _, err = sess.Where("user_id = ?", user.ID).In("repo_id", repoIDs).Delete(new(Access)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete member in his/her teams.
|
|
||||||
teams, err := getUserTeams(sess, org.ID, user.ID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, t := range teams {
|
|
||||||
if err = removeTeamMember(sess, org.ID, t.ID, user.ID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return sess.Commit()
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeOrgRepo(e Engine, orgID, repoID int64) error {
|
|
||||||
_, err := e.Delete(&TeamRepo{
|
|
||||||
OrgID: orgID,
|
|
||||||
RepoID: repoID,
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveOrgRepo removes all team-repository relations of given organization.
|
|
||||||
func RemoveOrgRepo(orgID, repoID int64) error {
|
|
||||||
return removeOrgRepo(x, orgID, repoID)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (org *User) getUserTeams(e Engine, userID int64, cols ...string) ([]*Team, error) {
|
|
||||||
teams := make([]*Team, 0, org.NumTeams)
|
|
||||||
return teams, e.Where("team_user.org_id = ?", org.ID).
|
|
||||||
And("team_user.uid = ?", userID).
|
|
||||||
Join("INNER", "team_user", "team_user.team_id = team.id").
|
|
||||||
Cols(cols...).Find(&teams)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetUserTeamIDs returns of all team IDs of the organization that user is member of.
|
|
||||||
func (org *User) GetUserTeamIDs(userID int64) ([]int64, error) {
|
|
||||||
teams, err := org.getUserTeams(x, userID, "team.id")
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("getUserTeams [%d]: %v", userID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
teamIDs := make([]int64, len(teams))
|
|
||||||
for i := range teams {
|
|
||||||
teamIDs[i] = teams[i].ID
|
|
||||||
}
|
|
||||||
return teamIDs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTeams returns all teams that belong to organization,
|
|
||||||
// and that the user has joined.
|
|
||||||
func (org *User) GetUserTeams(userID int64) ([]*Team, error) {
|
|
||||||
return org.getUserTeams(x, userID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetUserRepositories returns a range of repositories in organization which the user has access to,
|
|
||||||
// and total number of records based on given condition.
|
|
||||||
func (org *User) GetUserRepositories(userID int64, page, pageSize int) ([]*Repository, int64, error) {
|
|
||||||
teamIDs, err := org.GetUserTeamIDs(userID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, 0, fmt.Errorf("GetUserTeamIDs: %v", err)
|
|
||||||
}
|
|
||||||
if len(teamIDs) == 0 {
|
|
||||||
// user has no team but "IN ()" is invalid SQL
|
|
||||||
teamIDs = []int64{-1} // there is no team with id=-1
|
|
||||||
}
|
|
||||||
|
|
||||||
var teamRepoIDs []int64
|
|
||||||
if err = x.Table("team_repo").In("team_id", teamIDs).Distinct("repo_id").Find(&teamRepoIDs); err != nil {
|
|
||||||
return nil, 0, fmt.Errorf("get team repository IDs: %v", err)
|
|
||||||
}
|
|
||||||
if len(teamRepoIDs) == 0 {
|
|
||||||
// team has no repo but "IN ()" is invalid SQL
|
|
||||||
teamRepoIDs = []int64{-1} // there is no repo with id=-1
|
|
||||||
}
|
|
||||||
|
|
||||||
if page <= 0 {
|
|
||||||
page = 1
|
|
||||||
}
|
|
||||||
repos := make([]*Repository, 0, pageSize)
|
|
||||||
if err = x.Where("owner_id = ?", org.ID).
|
|
||||||
And(builder.Or(
|
|
||||||
builder.And(builder.Expr("is_private = ?", false), builder.Expr("is_unlisted = ?", false)),
|
|
||||||
builder.In("id", teamRepoIDs))).
|
|
||||||
Desc("updated_unix").
|
|
||||||
Limit(pageSize, (page-1)*pageSize).
|
|
||||||
Find(&repos); err != nil {
|
|
||||||
return nil, 0, fmt.Errorf("get user repositories: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
repoCount, err := x.Where("owner_id = ?", org.ID).
|
|
||||||
And(builder.Or(
|
|
||||||
builder.Expr("is_private = ?", false),
|
|
||||||
builder.In("id", teamRepoIDs))).
|
|
||||||
Count(new(Repository))
|
|
||||||
if err != nil {
|
|
||||||
return nil, 0, fmt.Errorf("count user repositories: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return repos, repoCount, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetUserMirrorRepositories returns mirror repositories of the organization which the user has access to.
|
// GetUserMirrorRepositories returns mirror repositories of the organization which the user has access to.
|
||||||
func (org *User) GetUserMirrorRepositories(userID int64) ([]*Repository, error) {
|
func (u *User) GetUserMirrorRepositories(userID int64) ([]*Repository, error) {
|
||||||
teamIDs, err := org.GetUserTeamIDs(userID)
|
teamIDs, err := u.GetUserTeamIDs(userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("GetUserTeamIDs: %v", err)
|
return nil, fmt.Errorf("GetUserTeamIDs: %v", err)
|
||||||
}
|
}
|
||||||
@ -549,7 +192,7 @@ func (org *User) GetUserMirrorRepositories(userID int64) ([]*Repository, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
repos := make([]*Repository, 0, 10)
|
repos := make([]*Repository, 0, 10)
|
||||||
if err = x.Where("owner_id = ?", org.ID).
|
if err = x.Where("owner_id = ?", u.ID).
|
||||||
And("is_private = ?", false).
|
And("is_private = ?", false).
|
||||||
Or(builder.In("id", teamRepoIDs)).
|
Or(builder.In("id", teamRepoIDs)).
|
||||||
And("is_mirror = ?", true). // Don't move up because it's an independent condition
|
And("is_mirror = ?", true). // Don't move up because it's an independent condition
|
||||||
|
@ -9,24 +9,24 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
|
|
||||||
"gogs.io/gogs/internal/db/errors"
|
dberrors "gogs.io/gogs/internal/db/errors"
|
||||||
"gogs.io/gogs/internal/errutil"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const OWNER_TEAM = "Owners"
|
const TeamNameOwners = "Owners"
|
||||||
|
|
||||||
// Team represents a organization team.
|
// Team represents a organization team.
|
||||||
type Team struct {
|
type Team struct {
|
||||||
ID int64
|
ID int64 `gorm:"primaryKey"`
|
||||||
OrgID int64 `xorm:"INDEX"`
|
OrgID int64 `xorm:"INDEX" gorm:"index"`
|
||||||
LowerName string
|
LowerName string
|
||||||
Name string
|
Name string
|
||||||
Description string
|
Description string
|
||||||
Authorize AccessMode
|
Authorize AccessMode
|
||||||
Repos []*Repository `xorm:"-" json:"-"`
|
Repos []*Repository `xorm:"-" json:"-" gorm:"-"`
|
||||||
Members []*User `xorm:"-" json:"-"`
|
Members []*User `xorm:"-" json:"-" gorm:"-"`
|
||||||
NumRepos int
|
NumRepos int
|
||||||
NumMembers int
|
NumMembers int
|
||||||
}
|
}
|
||||||
@ -43,7 +43,7 @@ func (t *Team) AfterSet(colName string, _ xorm.Cell) {
|
|||||||
|
|
||||||
// IsOwnerTeam returns true if team is owner team.
|
// IsOwnerTeam returns true if team is owner team.
|
||||||
func (t *Team) IsOwnerTeam() bool {
|
func (t *Team) IsOwnerTeam() bool {
|
||||||
return t.Name == OWNER_TEAM
|
return t.Name == TeamNameOwners
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasWriteAccess returns true if team has at least write level access mode.
|
// HasWriteAccess returns true if team has at least write level access mode.
|
||||||
@ -136,7 +136,7 @@ func (t *Team) addRepository(e Engine, repo *Repository) (err error) {
|
|||||||
// AddRepository adds new repository to team of organization.
|
// AddRepository adds new repository to team of organization.
|
||||||
func (t *Team) AddRepository(repo *Repository) (err error) {
|
func (t *Team) AddRepository(repo *Repository) (err error) {
|
||||||
if repo.OwnerID != t.OrgID {
|
if repo.OwnerID != t.OrgID {
|
||||||
return errors.New("Repository does not belong to organization")
|
return dberrors.New("Repository does not belong to organization")
|
||||||
} else if t.HasRepository(repo.ID) {
|
} else if t.HasRepository(repo.ID) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -259,9 +259,9 @@ func IsUsableTeamName(name string) error {
|
|||||||
// It's caller's responsibility to assign organization ID.
|
// It's caller's responsibility to assign organization ID.
|
||||||
func NewTeam(t *Team) error {
|
func NewTeam(t *Team) error {
|
||||||
if t.Name == "" {
|
if t.Name == "" {
|
||||||
return errors.New("empty team name")
|
return dberrors.New("empty team name")
|
||||||
} else if t.OrgID == 0 {
|
} else if t.OrgID == 0 {
|
||||||
return errors.New("OrgID is not assigned")
|
return dberrors.New("OrgID is not assigned")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := IsUsableTeamName(t.Name); err != nil {
|
if err := IsUsableTeamName(t.Name); err != nil {
|
||||||
@ -272,7 +272,7 @@ func NewTeam(t *Team) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else if !has {
|
} else if !has {
|
||||||
return ErrOrgNotExist
|
return errors.New("organization does not exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
t.LowerName = strings.ToLower(t.Name)
|
t.LowerName = strings.ToLower(t.Name)
|
||||||
@ -301,44 +301,6 @@ func NewTeam(t *Team) error {
|
|||||||
return sess.Commit()
|
return sess.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ errutil.NotFound = (*ErrTeamNotExist)(nil)
|
|
||||||
|
|
||||||
type ErrTeamNotExist struct {
|
|
||||||
args map[string]any
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsErrTeamNotExist(err error) bool {
|
|
||||||
_, ok := err.(ErrTeamNotExist)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (err ErrTeamNotExist) Error() string {
|
|
||||||
return fmt.Sprintf("team does not exist: %v", err.args)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ErrTeamNotExist) NotFound() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func getTeamOfOrgByName(e Engine, orgID int64, name string) (*Team, error) {
|
|
||||||
t := &Team{
|
|
||||||
OrgID: orgID,
|
|
||||||
LowerName: strings.ToLower(name),
|
|
||||||
}
|
|
||||||
has, err := e.Get(t)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else if !has {
|
|
||||||
return nil, ErrTeamNotExist{args: map[string]any{"orgID": orgID, "name": name}}
|
|
||||||
}
|
|
||||||
return t, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTeamOfOrgByName returns team by given team name and organization.
|
|
||||||
func GetTeamOfOrgByName(orgID int64, name string) (*Team, error) {
|
|
||||||
return getTeamOfOrgByName(x, orgID, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getTeamByID(e Engine, teamID int64) (*Team, error) {
|
func getTeamByID(e Engine, teamID int64) (*Team, error) {
|
||||||
t := new(Team)
|
t := new(Team)
|
||||||
has, err := e.ID(teamID).Get(t)
|
has, err := e.ID(teamID).Get(t)
|
||||||
@ -355,20 +317,16 @@ func GetTeamByID(teamID int64) (*Team, error) {
|
|||||||
return getTeamByID(x, teamID)
|
return getTeamByID(x, teamID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getTeamsByOrgID returns all teams belong to given organization.
|
||||||
func getTeamsByOrgID(e Engine, orgID int64) ([]*Team, error) {
|
func getTeamsByOrgID(e Engine, orgID int64) ([]*Team, error) {
|
||||||
teams := make([]*Team, 0, 3)
|
teams := make([]*Team, 0, 3)
|
||||||
return teams, e.Where("org_id = ?", orgID).Find(&teams)
|
return teams, e.Where("org_id = ?", orgID).Find(&teams)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTeamsByOrgID returns all teams belong to given organization.
|
|
||||||
func GetTeamsByOrgID(orgID int64) ([]*Team, error) {
|
|
||||||
return getTeamsByOrgID(x, orgID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateTeam updates information of team.
|
// UpdateTeam updates information of team.
|
||||||
func UpdateTeam(t *Team, authChanged bool) (err error) {
|
func UpdateTeam(t *Team, authChanged bool) (err error) {
|
||||||
if t.Name == "" {
|
if t.Name == "" {
|
||||||
return errors.New("empty team name")
|
return dberrors.New("empty team name")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(t.Description) > 255 {
|
if len(t.Description) > 255 {
|
||||||
@ -528,7 +486,7 @@ func AddTeamMember(orgID, teamID, userID int64) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := AddOrgUser(orgID, userID); err != nil {
|
if err := Orgs.AddMember(context.TODO(), orgID, userID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -583,8 +541,8 @@ func AddTeamMember(orgID, teamID, userID int64) error {
|
|||||||
return sess.Commit()
|
return sess.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeTeamMember(e Engine, orgID, teamID, uid int64) error {
|
func removeTeamMember(e Engine, orgID, teamID, userID int64) error {
|
||||||
if !isTeamMember(e, orgID, teamID, uid) {
|
if !isTeamMember(e, orgID, teamID, userID) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -596,7 +554,7 @@ func removeTeamMember(e Engine, orgID, teamID, uid int64) error {
|
|||||||
|
|
||||||
// Check if the user to delete is the last member in owner team.
|
// Check if the user to delete is the last member in owner team.
|
||||||
if t.IsOwnerTeam() && t.NumMembers == 1 {
|
if t.IsOwnerTeam() && t.NumMembers == 1 {
|
||||||
return ErrLastOrgOwner{UID: uid}
|
return ErrLastOrgOwner{args: map[string]any{"orgID": orgID, "userID": userID}}
|
||||||
}
|
}
|
||||||
|
|
||||||
t.NumMembers--
|
t.NumMembers--
|
||||||
@ -612,7 +570,7 @@ func removeTeamMember(e Engine, orgID, teamID, uid int64) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tu := &TeamUser{
|
tu := &TeamUser{
|
||||||
UID: uid,
|
UID: userID,
|
||||||
OrgID: orgID,
|
OrgID: orgID,
|
||||||
TeamID: teamID,
|
TeamID: teamID,
|
||||||
}
|
}
|
||||||
@ -631,7 +589,7 @@ func removeTeamMember(e Engine, orgID, teamID, uid int64) error {
|
|||||||
|
|
||||||
// This must exist.
|
// This must exist.
|
||||||
ou := new(OrgUser)
|
ou := new(OrgUser)
|
||||||
_, err = e.Where("uid = ?", uid).And("org_id = ?", org.ID).Get(ou)
|
_, err = e.Where("uid = ?", userID).And("org_id = ?", org.ID).Get(ou)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -673,16 +631,13 @@ type TeamRepo struct {
|
|||||||
RepoID int64 `xorm:"UNIQUE(s)"`
|
RepoID int64 `xorm:"UNIQUE(s)"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hasTeamRepo returns true if given team has access to the repository of the organization.
|
||||||
func hasTeamRepo(e Engine, orgID, teamID, repoID int64) bool {
|
func hasTeamRepo(e Engine, orgID, teamID, repoID int64) bool {
|
||||||
has, _ := e.Where("org_id = ?", orgID).And("team_id = ?", teamID).And("repo_id = ?", repoID).Get(new(TeamRepo))
|
has, _ := e.Where("org_id = ?", orgID).And("team_id = ?", teamID).And("repo_id = ?", repoID).Get(new(TeamRepo))
|
||||||
return has
|
return has
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasTeamRepo returns true if given team has access to the repository of the organization.
|
// addTeamRepo adds new repository relation to team.
|
||||||
func HasTeamRepo(orgID, teamID, repoID int64) bool {
|
|
||||||
return hasTeamRepo(x, orgID, teamID, repoID)
|
|
||||||
}
|
|
||||||
|
|
||||||
func addTeamRepo(e Engine, orgID, teamID, repoID int64) error {
|
func addTeamRepo(e Engine, orgID, teamID, repoID int64) error {
|
||||||
_, err := e.InsertOne(&TeamRepo{
|
_, err := e.InsertOne(&TeamRepo{
|
||||||
OrgID: orgID,
|
OrgID: orgID,
|
||||||
@ -692,11 +647,7 @@ func addTeamRepo(e Engine, orgID, teamID, repoID int64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddTeamRepo adds new repository relation to team.
|
// removeTeamRepo deletes repository relation to team.
|
||||||
func AddTeamRepo(orgID, teamID, repoID int64) error {
|
|
||||||
return addTeamRepo(x, orgID, teamID, repoID)
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeTeamRepo(e Engine, teamID, repoID int64) error {
|
func removeTeamRepo(e Engine, teamID, repoID int64) error {
|
||||||
_, err := e.Delete(&TeamRepo{
|
_, err := e.Delete(&TeamRepo{
|
||||||
TeamID: teamID,
|
TeamID: teamID,
|
||||||
@ -705,11 +656,6 @@ func removeTeamRepo(e Engine, teamID, repoID int64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveTeamRepo deletes repository relation to team.
|
|
||||||
func RemoveTeamRepo(teamID, repoID int64) error {
|
|
||||||
return removeTeamRepo(x, teamID, repoID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTeamsHaveAccessToRepo returns all teams in an organization that have given access level to the repository.
|
// GetTeamsHaveAccessToRepo returns all teams in an organization that have given access level to the repository.
|
||||||
func GetTeamsHaveAccessToRepo(orgID, repoID int64, mode AccessMode) ([]*Team, error) {
|
func GetTeamsHaveAccessToRepo(orgID, repoID int64, mode AccessMode) ([]*Team, error) {
|
||||||
teams := make([]*Team, 0, 5)
|
teams := make([]*Team, 0, 5)
|
||||||
@ -719,3 +665,46 @@ func GetTeamsHaveAccessToRepo(orgID, repoID int64, mode AccessMode) ([]*Team, er
|
|||||||
And("team_repo.repo_id = ?", repoID).
|
And("team_repo.repo_id = ?", repoID).
|
||||||
Find(&teams)
|
Find(&teams)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *User) getTeams(e Engine) (err error) {
|
||||||
|
u.Teams, err = getTeamsByOrgID(e, u.ID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTeams returns all teams that belong to organization.
|
||||||
|
func (u *User) GetTeams() error {
|
||||||
|
return u.getTeams(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TeamsHaveAccessToRepo returns all teams that have given access level to the repository.
|
||||||
|
func (u *User) TeamsHaveAccessToRepo(repoID int64, mode AccessMode) ([]*Team, error) {
|
||||||
|
return GetTeamsHaveAccessToRepo(u.ID, repoID, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *User) getUserTeams(e Engine, userID int64, cols ...string) ([]*Team, error) {
|
||||||
|
teams := make([]*Team, 0, u.NumTeams)
|
||||||
|
return teams, e.Where("team_user.org_id = ?", u.ID).
|
||||||
|
And("team_user.uid = ?", userID).
|
||||||
|
Join("INNER", "team_user", "team_user.team_id = team.id").
|
||||||
|
Cols(cols...).Find(&teams)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserTeamIDs returns of all team IDs of the organization that user is member of.
|
||||||
|
func (u *User) GetUserTeamIDs(userID int64) ([]int64, error) {
|
||||||
|
teams, err := u.getUserTeams(x, userID, "team.id")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("getUserTeams [%d]: %v", userID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
teamIDs := make([]int64, len(teams))
|
||||||
|
for i := range teams {
|
||||||
|
teamIDs[i] = teams[i].ID
|
||||||
|
}
|
||||||
|
return teamIDs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTeams returns all teams that belong to organization,
|
||||||
|
// and that the user has joined.
|
||||||
|
func (u *User) GetUserTeams(userID int64) ([]*Team, error) {
|
||||||
|
return u.getUserTeams(x, userID)
|
||||||
|
}
|
||||||
|
@ -6,26 +6,57 @@ package db
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"gogs.io/gogs/internal/dbutil"
|
"gogs.io/gogs/internal/dbutil"
|
||||||
|
"gogs.io/gogs/internal/errutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// OrgsStore is the persistent interface for organizations.
|
// OrgsStore is the persistent interface for organizations.
|
||||||
type OrgsStore interface {
|
type OrgsStore interface {
|
||||||
// List returns a list of organizations filtered by options.
|
// AddMember adds a new member to the given organization.
|
||||||
List(ctx context.Context, opts ListOrgsOptions) ([]*Organization, error)
|
AddMember(ctx context.Context, orgID, userID int64) error
|
||||||
|
// RemoveMember removes a member from the given organization.
|
||||||
|
RemoveMember(ctx context.Context, orgID, userID int64) error
|
||||||
|
// HasMember returns whether the given user is a member of the organization
|
||||||
|
// (first), and whether the organization membership is public (second).
|
||||||
|
HasMember(ctx context.Context, orgID, userID int64) (bool, bool)
|
||||||
|
// ListMembers returns all members of the given organization, and sorted by the
|
||||||
|
// given order (e.g. "id ASC").
|
||||||
|
ListMembers(ctx context.Context, orgID int64, opts ListOrgMembersOptions) ([]*User, error)
|
||||||
|
// IsOwnedBy returns true if the given user is an owner of the organization.
|
||||||
|
IsOwnedBy(ctx context.Context, orgID, userID int64) bool
|
||||||
|
// SetMemberVisibility sets the visibility of the given user in the organization.
|
||||||
|
SetMemberVisibility(ctx context.Context, orgID, userID int64, public bool) error
|
||||||
|
|
||||||
|
// GetByName returns the organization with given name.
|
||||||
|
GetByName(ctx context.Context, name string) (*Organization, error)
|
||||||
// SearchByName returns a list of organizations whose username or full name
|
// SearchByName returns a list of organizations whose username or full name
|
||||||
// matches the given keyword case-insensitively. Results are paginated by given
|
// matches the given keyword case-insensitively. Results are paginated by given
|
||||||
// page and page size, and sorted by the given order (e.g. "id DESC"). A total
|
// page and page size, and sorted by the given order (e.g. "id DESC"). A total
|
||||||
// count of all results is also returned. If the order is not given, it's up to
|
// count of all results is also returned. If the order is not given, it's up to
|
||||||
// the database to decide.
|
// the database to decide.
|
||||||
SearchByName(ctx context.Context, keyword string, page, pageSize int, orderBy string) ([]*Organization, int64, error)
|
SearchByName(ctx context.Context, keyword string, page, pageSize int, orderBy string) ([]*Organization, int64, error)
|
||||||
|
// List returns a list of organizations filtered by options.
|
||||||
|
List(ctx context.Context, opts ListOrgsOptions) ([]*Organization, error)
|
||||||
// CountByUser returns the number of organizations the user is a member of.
|
// CountByUser returns the number of organizations the user is a member of.
|
||||||
CountByUser(ctx context.Context, userID int64) (int64, error)
|
CountByUser(ctx context.Context, userID int64) (int64, error)
|
||||||
|
// Count returns the total number of organizations.
|
||||||
|
Count(ctx context.Context) int64
|
||||||
|
|
||||||
|
// GetTeamByName returns the team with given name under the given organization.
|
||||||
|
// It returns ErrTeamNotExist whe not found.
|
||||||
|
GetTeamByName(ctx context.Context, orgID int64, name string) (*Team, error)
|
||||||
|
|
||||||
|
// AccessibleRepositoriesByUser returns a range of repositories in the
|
||||||
|
// organization that the user has access to and the total number of it. Results
|
||||||
|
// are paginated by given page and page size, and sorted by the given order
|
||||||
|
// (e.g. "updated_unix DESC").
|
||||||
|
AccessibleRepositoriesByUser(ctx context.Context, orgID, userID int64, page, pageSize int, opts AccessibleRepositoriesByUserOptions) ([]*Repository, int64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
var Orgs OrgsStore
|
var Orgs OrgsStore
|
||||||
@ -42,6 +73,260 @@ func NewOrgsStore(db *gorm.DB) OrgsStore {
|
|||||||
return &orgs{DB: db}
|
return &orgs{DB: db}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (*orgs) recountMembers(tx *gorm.DB, orgID int64) error {
|
||||||
|
/*
|
||||||
|
Equivalent SQL for PostgreSQL:
|
||||||
|
|
||||||
|
UPDATE "user"
|
||||||
|
SET num_members = (
|
||||||
|
SELECT COUNT(*) FROM org_user WHERE org_id = @orgID
|
||||||
|
)
|
||||||
|
WHERE id = @orgID
|
||||||
|
*/
|
||||||
|
err := tx.Model(&User{}).
|
||||||
|
Where("id = ?", orgID).
|
||||||
|
Update(
|
||||||
|
"num_members",
|
||||||
|
tx.Model(&OrgUser{}).Select("COUNT(*)").Where("org_id = ?", orgID),
|
||||||
|
).
|
||||||
|
Error
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, `update "user.num_members"`)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *orgs) AddMember(ctx context.Context, orgID, userID int64) error {
|
||||||
|
return db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||||
|
ou := &OrgUser{
|
||||||
|
UserID: userID,
|
||||||
|
OrgID: orgID,
|
||||||
|
}
|
||||||
|
result := tx.FirstOrCreate(ou, ou)
|
||||||
|
if result.Error != nil {
|
||||||
|
return errors.Wrap(result.Error, "upsert")
|
||||||
|
} else if result.RowsAffected <= 0 {
|
||||||
|
return nil // Relation already exists
|
||||||
|
}
|
||||||
|
return db.recountMembers(tx, orgID)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type ErrLastOrgOwner struct {
|
||||||
|
args map[string]any
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsErrLastOrgOwner(err error) bool {
|
||||||
|
return errors.As(err, &ErrLastOrgOwner{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrLastOrgOwner) Error() string {
|
||||||
|
return fmt.Sprintf("user is the last owner of the organization: %v", err.args)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *orgs) RemoveMember(ctx context.Context, orgID, userID int64) error {
|
||||||
|
ou, err := db.getOrgUser(ctx, orgID, userID)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil // Not a member
|
||||||
|
}
|
||||||
|
return errors.Wrap(err, "check organization membership")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the member to remove is the last owner.
|
||||||
|
if ou.IsOwner {
|
||||||
|
t, err := db.GetTeamByName(ctx, orgID, TeamNameOwners)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "get owners team")
|
||||||
|
} else if t.NumMembers == 1 {
|
||||||
|
return ErrLastOrgOwner{args: map[string]any{"orgID": orgID, "userID": userID}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||||
|
repoIDsConds := db.accessibleRepositoriesByUser(tx, orgID, userID, accessibleRepositoriesByUserOptions{}).Select("repository.id")
|
||||||
|
|
||||||
|
err := tx.Where("user_id = ? AND repo_id IN (?)", userID, repoIDsConds).Delete(&Watch{}).Error
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "unwatch repositories")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Table("repository").
|
||||||
|
Where("id IN (?)", repoIDsConds).
|
||||||
|
UpdateColumn("num_watches", gorm.Expr("num_watches - 1")).
|
||||||
|
Error
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, `decrease "repository.num_watches"`)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Where("user_id = ? AND repo_id IN (?)", userID, repoIDsConds).Delete(&Access{}).Error
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "delete repository accesses")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Where("user_id = ? AND repo_id IN (?)", userID, repoIDsConds).Delete(&Collaboration{}).Error
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "delete repository collaborations")
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Equivalent SQL for PostgreSQL:
|
||||||
|
|
||||||
|
UPDATE "team"
|
||||||
|
SET num_members = num_members - 1
|
||||||
|
WHERE id IN (
|
||||||
|
SELECT team_id FROM "team_user"
|
||||||
|
WHERE team_user.org_id = @orgID AND uid = @userID)
|
||||||
|
)
|
||||||
|
*/
|
||||||
|
err = tx.Table("team").
|
||||||
|
Where(`id IN (?)`, tx.
|
||||||
|
Select("team_id").
|
||||||
|
Table("team_user").
|
||||||
|
Where("org_id = ? AND uid = ?", orgID, userID),
|
||||||
|
).
|
||||||
|
UpdateColumn("num_members", gorm.Expr("num_members - 1")).
|
||||||
|
Error
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, `decrease "team.num_members"`)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Where("uid = ? AND org_id = ?", userID, orgID).Delete(&TeamUser{}).Error
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "delete team membership")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Where("uid = ? AND org_id = ?", userID, orgID).Delete(&OrgUser{}).Error
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "delete organization membership")
|
||||||
|
}
|
||||||
|
return db.recountMembers(tx, orgID)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type accessibleRepositoriesByUserOptions struct {
|
||||||
|
orderBy string
|
||||||
|
page int
|
||||||
|
pageSize int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*orgs) accessibleRepositoriesByUser(tx *gorm.DB, orgID, userID int64, opts accessibleRepositoriesByUserOptions) *gorm.DB {
|
||||||
|
/*
|
||||||
|
Equivalent SQL for PostgreSQL:
|
||||||
|
|
||||||
|
<SELECT * FROM "repository">
|
||||||
|
JOIN team_repo ON repository.id = team_repo.repo_id
|
||||||
|
WHERE
|
||||||
|
owner_id = @orgID
|
||||||
|
AND (
|
||||||
|
team_repo.team_id IN (
|
||||||
|
SELECT team_id FROM "team_user"
|
||||||
|
WHERE team_user.org_id = @orgID AND uid = @userID)
|
||||||
|
)
|
||||||
|
OR (repository.is_private = FALSE AND repository.is_unlisted = FALSE)
|
||||||
|
)
|
||||||
|
[ORDER BY updated_unix DESC]
|
||||||
|
[LIMIT @limit OFFSET @offset]
|
||||||
|
*/
|
||||||
|
conds := tx.
|
||||||
|
Joins("JOIN team_repo ON repository.id = team_repo.repo_id").
|
||||||
|
Where("owner_id = ? AND (?)", orgID, tx.
|
||||||
|
Where("team_repo.team_id IN (?)", tx.
|
||||||
|
Select("team_id").
|
||||||
|
Table("team_user").
|
||||||
|
Where("team_user.org_id = ? AND uid = ?", orgID, userID),
|
||||||
|
).
|
||||||
|
Or("repository.is_private = ? AND repository.is_unlisted = ?", false, false),
|
||||||
|
)
|
||||||
|
if opts.orderBy != "" {
|
||||||
|
conds.Order(opts.orderBy)
|
||||||
|
}
|
||||||
|
if opts.page > 0 && opts.pageSize > 0 {
|
||||||
|
conds.Limit(opts.pageSize).Offset((opts.page - 1) * opts.pageSize)
|
||||||
|
}
|
||||||
|
return conds
|
||||||
|
}
|
||||||
|
|
||||||
|
type AccessibleRepositoriesByUserOptions struct {
|
||||||
|
// Whether to skip counting the total number of repositories.
|
||||||
|
SkipCount bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *orgs) AccessibleRepositoriesByUser(ctx context.Context, orgID, userID int64, page, pageSize int, opts AccessibleRepositoriesByUserOptions) ([]*Repository, int64, error) {
|
||||||
|
conds := db.accessibleRepositoriesByUser(
|
||||||
|
db.DB,
|
||||||
|
orgID,
|
||||||
|
userID,
|
||||||
|
accessibleRepositoriesByUserOptions{
|
||||||
|
orderBy: "updated_unix DESC",
|
||||||
|
page: page,
|
||||||
|
pageSize: pageSize,
|
||||||
|
},
|
||||||
|
).WithContext(ctx)
|
||||||
|
|
||||||
|
repos := make([]*Repository, 0, pageSize)
|
||||||
|
err := conds.Find(&repos).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, errors.Wrap(err, "list repositories")
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.SkipCount {
|
||||||
|
return repos, 0, nil
|
||||||
|
}
|
||||||
|
var count int64
|
||||||
|
err = conds.Model(&Repository{}).Count(&count).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, errors.Wrap(err, "count repositories")
|
||||||
|
}
|
||||||
|
return repos, count, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *orgs) getOrgUser(ctx context.Context, orgID, userID int64) (*OrgUser, error) {
|
||||||
|
var ou OrgUser
|
||||||
|
return &ou, db.WithContext(ctx).Where("org_id = ? AND uid = ?", orgID, userID).First(&ou).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *orgs) IsOwnedBy(ctx context.Context, orgID, userID int64) bool {
|
||||||
|
ou, err := db.getOrgUser(ctx, orgID, userID)
|
||||||
|
return err == nil && ou.IsOwner
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *orgs) SetMemberVisibility(ctx context.Context, orgID, userID int64, public bool) error {
|
||||||
|
return db.Table("org_user").Where("org_id = ? AND uid = ?", orgID, userID).UpdateColumn("is_public", public).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *orgs) HasMember(ctx context.Context, orgID, userID int64) (bool, bool) {
|
||||||
|
ou, err := db.getOrgUser(ctx, orgID, userID)
|
||||||
|
return err == nil, ou != nil && ou.IsPublic
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListOrgMembersOptions struct {
|
||||||
|
// The maximum number of members to return.
|
||||||
|
Limit int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *orgs) ListMembers(ctx context.Context, orgID int64, opts ListOrgMembersOptions) ([]*User, error) {
|
||||||
|
/*
|
||||||
|
Equivalent SQL for PostgreSQL:
|
||||||
|
|
||||||
|
SELECT * FROM "user"
|
||||||
|
JOIN org_user ON org_user.uid = user.id
|
||||||
|
WHERE
|
||||||
|
org_user.org_id = @orgID
|
||||||
|
ORDER BY user.id ASC
|
||||||
|
[LIMIT @limit]
|
||||||
|
*/
|
||||||
|
conds := db.WithContext(ctx).
|
||||||
|
Joins(dbutil.Quote("JOIN org_user ON org_user.uid = %s.id", "user")).
|
||||||
|
Where("org_user.org_id = ?", orgID).
|
||||||
|
Order(dbutil.Quote("%s.id ASC", "user"))
|
||||||
|
if opts.Limit > 0 {
|
||||||
|
conds.Limit(opts.Limit)
|
||||||
|
}
|
||||||
|
var users []*User
|
||||||
|
return users, conds.Find(&users).Error
|
||||||
|
}
|
||||||
|
|
||||||
type ListOrgsOptions struct {
|
type ListOrgsOptions struct {
|
||||||
// Filter by the membership with the given user ID.
|
// Filter by the membership with the given user ID.
|
||||||
MemberID int64
|
MemberID int64
|
||||||
@ -57,23 +342,54 @@ func (db *orgs) List(ctx context.Context, opts ListOrgsOptions) ([]*Organization
|
|||||||
/*
|
/*
|
||||||
Equivalent SQL for PostgreSQL:
|
Equivalent SQL for PostgreSQL:
|
||||||
|
|
||||||
SELECT * FROM "org"
|
SELECT * FROM "user"
|
||||||
JOIN org_user ON org_user.org_id = org.id
|
JOIN org_user ON org_user.org_id = user.id
|
||||||
WHERE
|
WHERE
|
||||||
org_user.uid = @memberID
|
org_user.uid = @memberID
|
||||||
[AND org_user.is_public = @includePrivateMembers]
|
[AND org_user.is_public = @includePrivateMembers]
|
||||||
ORDER BY org.id ASC
|
ORDER BY user.id ASC
|
||||||
*/
|
*/
|
||||||
tx := db.WithContext(ctx).
|
conds := db.WithContext(ctx).
|
||||||
Joins(dbutil.Quote("JOIN org_user ON org_user.org_id = %s.id", "user")).
|
Joins(dbutil.Quote("JOIN org_user ON org_user.org_id = %s.id", "user")).
|
||||||
Where("org_user.uid = ?", opts.MemberID).
|
Where("org_user.uid = ?", opts.MemberID).
|
||||||
Order(dbutil.Quote("%s.id ASC", "user"))
|
Order(dbutil.Quote("%s.id ASC", "user"))
|
||||||
if !opts.IncludePrivateMembers {
|
if !opts.IncludePrivateMembers {
|
||||||
tx = tx.Where("org_user.is_public = ?", true)
|
conds.Where("org_user.is_public = ?", true)
|
||||||
}
|
}
|
||||||
|
|
||||||
var orgs []*Organization
|
var orgs []*Organization
|
||||||
return orgs, tx.Find(&orgs).Error
|
return orgs, conds.Find(&orgs).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ errutil.NotFound = (*ErrUserNotExist)(nil)
|
||||||
|
|
||||||
|
type ErrOrganizationNotExist struct {
|
||||||
|
args errutil.Args
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsErrOrganizationNotExist returns true if the underlying error has the type
|
||||||
|
// ErrOrganizationNotExist.
|
||||||
|
func IsErrOrganizationNotExist(err error) bool {
|
||||||
|
return errors.As(err, &ErrOrganizationNotExist{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrOrganizationNotExist) Error() string {
|
||||||
|
return fmt.Sprintf("organization does not exist: %v", err.args)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ErrOrganizationNotExist) NotFound() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *orgs) GetByName(ctx context.Context, name string) (*Organization, error) {
|
||||||
|
org, err := getUserByUsername(ctx, db.DB, UserTypeOrganization, name)
|
||||||
|
if err != nil {
|
||||||
|
if IsErrUserNotExist(err) {
|
||||||
|
return nil, ErrOrganizationNotExist{args: map[string]any{"name": name}}
|
||||||
|
}
|
||||||
|
return nil, errors.Wrap(err, "get organization by name")
|
||||||
|
}
|
||||||
|
return org, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *orgs) SearchByName(ctx context.Context, keyword string, page, pageSize int, orderBy string) ([]*Organization, int64, error) {
|
func (db *orgs) SearchByName(ctx context.Context, keyword string, page, pageSize int, orderBy string) ([]*Organization, int64, error) {
|
||||||
@ -85,8 +401,62 @@ func (db *orgs) CountByUser(ctx context.Context, userID int64) (int64, error) {
|
|||||||
return count, db.WithContext(ctx).Model(&OrgUser{}).Where("uid = ?", userID).Count(&count).Error
|
return count, db.WithContext(ctx).Model(&OrgUser{}).Where("uid = ?", userID).Count(&count).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *orgs) Count(ctx context.Context) int64 {
|
||||||
|
var count int64
|
||||||
|
db.WithContext(ctx).Model(&User{}).Where("type = ?", UserTypeOrganization).Count(&count)
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ errutil.NotFound = (*ErrTeamNotExist)(nil)
|
||||||
|
|
||||||
|
type ErrTeamNotExist struct {
|
||||||
|
args map[string]any
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsErrTeamNotExist(err error) bool {
|
||||||
|
return errors.As(err, &ErrTeamNotExist{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrTeamNotExist) Error() string {
|
||||||
|
return fmt.Sprintf("team does not exist: %v", err.args)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ErrTeamNotExist) NotFound() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *orgs) GetTeamByName(ctx context.Context, orgID int64, name string) (*Team, error) {
|
||||||
|
var team Team
|
||||||
|
err := db.WithContext(ctx).Where("org_id = ? AND lower_name = ?", orgID, strings.ToLower(name)).First(&team).Error
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, ErrTeamNotExist{args: map[string]any{"orgID": orgID, "name": name}}
|
||||||
|
}
|
||||||
|
return nil, errors.Wrap(err, "get team by name")
|
||||||
|
}
|
||||||
|
return &team, nil
|
||||||
|
}
|
||||||
|
|
||||||
type Organization = User
|
type Organization = User
|
||||||
|
|
||||||
func (o *Organization) TableName() string {
|
func (u *Organization) TableName() string {
|
||||||
return "user"
|
return "user"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsOwnedBy returns true if the given user is an owner of the organization.
|
||||||
|
//
|
||||||
|
// TODO(unknwon): This is also used in templates, which should be fixed by
|
||||||
|
// having a dedicated type `template.Organization`.
|
||||||
|
func (u *Organization) IsOwnedBy(userID int64) bool {
|
||||||
|
return Orgs.IsOwnedBy(context.TODO(), u.ID, userID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrgUser represents relations of organizations and their members.
|
||||||
|
type OrgUser struct {
|
||||||
|
ID int64 `gorm:"primaryKey"`
|
||||||
|
UserID int64 `xorm:"uid INDEX UNIQUE(s)" gorm:"column:uid;uniqueIndex:org_user_user_org_unique;index;not null" json:"Uid"`
|
||||||
|
OrgID int64 `xorm:"INDEX UNIQUE(s)" gorm:"uniqueIndex:org_user_user_org_unique;index;not null"`
|
||||||
|
IsPublic bool `gorm:"not null;default:FALSE"`
|
||||||
|
IsOwner bool `gorm:"not null;default:FALSE"`
|
||||||
|
NumTeams int `gorm:"not null;default:0"`
|
||||||
|
}
|
||||||
|
@ -798,7 +798,7 @@ func MigrateRepository(doer, owner *User, opts MigrateRepoOptions) (*Repository,
|
|||||||
wikiPath := WikiPath(owner.Name, opts.Name)
|
wikiPath := WikiPath(owner.Name, opts.Name)
|
||||||
|
|
||||||
if owner.IsOrganization() {
|
if owner.IsOrganization() {
|
||||||
t, err := owner.GetOwnerTeam()
|
t, err := Orgs.GetTeamByName(context.TODO(), owner.ID, TeamNameOwners)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -1149,7 +1149,24 @@ func createRepository(e *xorm.Session, doer, owner *User, repo *Repository) (err
|
|||||||
|
|
||||||
// Give access to all members in owner team.
|
// Give access to all members in owner team.
|
||||||
if owner.IsOrganization() {
|
if owner.IsOrganization() {
|
||||||
t, err := owner.getOwnerTeam(e)
|
// FIXME: This is identical to Orgs.GetTeamByName but we are not yet able to
|
||||||
|
// wrap transaction with different ORM objects, should delete this once migrated
|
||||||
|
// to GORM for this part of logic.
|
||||||
|
getTeamOfOrgByName := func(e Engine, orgID int64, name string) (*Team, error) {
|
||||||
|
t := &Team{
|
||||||
|
OrgID: orgID,
|
||||||
|
LowerName: strings.ToLower(name),
|
||||||
|
}
|
||||||
|
has, err := e.Get(t)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if !has {
|
||||||
|
return nil, ErrTeamNotExist{args: map[string]any{"orgID": orgID, "name": name}}
|
||||||
|
}
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
t, err := getTeamOfOrgByName(e, owner.ID, TeamNameOwners)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("getOwnerTeam: %v", err)
|
return fmt.Errorf("getOwnerTeam: %v", err)
|
||||||
} else if err = t.addRepository(e, repo); err != nil {
|
} else if err = t.addRepository(e, repo); err != nil {
|
||||||
@ -1393,7 +1410,8 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error
|
|||||||
collaboration := &Collaboration{RepoID: repo.ID}
|
collaboration := &Collaboration{RepoID: repo.ID}
|
||||||
for _, c := range collaborators {
|
for _, c := range collaborators {
|
||||||
collaboration.UserID = c.ID
|
collaboration.UserID = c.ID
|
||||||
if c.ID == newOwner.ID || newOwner.IsOrgMember(c.ID) {
|
if c.ID == newOwner.ID ||
|
||||||
|
(newOwner.IsOrganization() && func() bool { member, _ := Orgs.HasMember(context.TODO(), newOwner.ID, c.ID); return member }()) {
|
||||||
if _, err = sess.Delete(collaboration); err != nil {
|
if _, err = sess.Delete(collaboration); err != nil {
|
||||||
return fmt.Errorf("remove collaborator '%d': %v", c.ID, err)
|
return fmt.Errorf("remove collaborator '%d': %v", c.ID, err)
|
||||||
}
|
}
|
||||||
@ -1416,13 +1434,34 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = owner.removeOrgRepo(sess, repo.ID); err != nil {
|
_, err := sess.Delete(&TeamRepo{
|
||||||
|
OrgID: owner.ID,
|
||||||
|
RepoID: repo.ID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
return fmt.Errorf("removeOrgRepo: %v", err)
|
return fmt.Errorf("removeOrgRepo: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if newOwner.IsOrganization() {
|
if newOwner.IsOrganization() {
|
||||||
t, err := newOwner.getOwnerTeam(sess)
|
// FIXME: This is identical to Orgs.GetTeamByName but we are not yet able to
|
||||||
|
// wrap transaction with different ORM objects, should delete this once migrated
|
||||||
|
// to GORM for this part of logic.
|
||||||
|
getTeamOfOrgByName := func(e Engine, orgID int64, name string) (*Team, error) {
|
||||||
|
t := &Team{
|
||||||
|
OrgID: orgID,
|
||||||
|
LowerName: strings.ToLower(name),
|
||||||
|
}
|
||||||
|
has, err := e.Get(t)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if !has {
|
||||||
|
return nil, ErrTeamNotExist{args: map[string]any{"orgID": orgID, "name": name}}
|
||||||
|
}
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
t, err := getTeamOfOrgByName(sess, owner.ID, TeamNameOwners)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("getOwnerTeam: %v", err)
|
return fmt.Errorf("getOwnerTeam: %v", err)
|
||||||
} else if err = t.addRepository(sess, repo); err != nil {
|
} else if err = t.addRepository(sess, repo); err != nil {
|
||||||
|
@ -263,8 +263,7 @@ type ErrRepoNotExist struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func IsErrRepoNotExist(err error) bool {
|
func IsErrRepoNotExist(err error) bool {
|
||||||
_, ok := err.(ErrRepoNotExist)
|
return errors.As(err, &ErrRepoNotExist{})
|
||||||
return ok
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (err ErrRepoNotExist) Error() string {
|
func (err ErrRepoNotExist) Error() string {
|
||||||
@ -279,7 +278,7 @@ func (db *repos) GetByID(ctx context.Context, id int64) (*Repository, error) {
|
|||||||
repo := new(Repository)
|
repo := new(Repository)
|
||||||
err := db.WithContext(ctx).Where("id = ?", id).First(repo).Error
|
err := db.WithContext(ctx).Where("id = ?", id).First(repo).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == gorm.ErrRecordNotFound {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return nil, ErrRepoNotExist{errutil.Args{"repoID": id}}
|
return nil, ErrRepoNotExist{errutil.Args{"repoID": id}}
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -294,7 +293,7 @@ func (db *repos) GetByName(ctx context.Context, ownerID int64, name string) (*Re
|
|||||||
First(repo).
|
First(repo).
|
||||||
Error
|
Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == gorm.ErrRecordNotFound {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return nil, ErrRepoNotExist{
|
return nil, ErrRepoNotExist{
|
||||||
args: errutil.Args{
|
args: errutil.Args{
|
||||||
"ownerID": ownerID,
|
"ownerID": ownerID,
|
||||||
|
2
internal/db/testdata/backup/OrgUser.golden.json
vendored
Normal file
2
internal/db/testdata/backup/OrgUser.golden.json
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
{"ID":1,"Uid":1,"OrgID":11,"IsPublic":true,"IsOwner":true,"NumTeams":3}
|
||||||
|
{"ID":2,"Uid":2,"OrgID":11,"IsPublic":false,"IsOwner":false,"NumTeams":0}
|
@ -104,7 +104,7 @@ func (db *twoFactors) GetByUserID(ctx context.Context, userID int64) (*TwoFactor
|
|||||||
tf := new(TwoFactor)
|
tf := new(TwoFactor)
|
||||||
err := db.WithContext(ctx).Where("user_id = ?", userID).First(tf).Error
|
err := db.WithContext(ctx).Where("user_id = ?", userID).First(tf).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == gorm.ErrRecordNotFound {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return nil, ErrTwoFactorNotFound{args: errutil.Args{"userID": userID}}
|
return nil, ErrTwoFactorNotFound{args: errutil.Args{"userID": userID}}
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -738,7 +738,6 @@ func (db *users) Follow(ctx context.Context, userID, followID int64) error {
|
|||||||
} else if result.RowsAffected <= 0 {
|
} else if result.RowsAffected <= 0 {
|
||||||
return nil // Relation already exists
|
return nil // Relation already exists
|
||||||
}
|
}
|
||||||
|
|
||||||
return db.recountFollows(tx, userID, followID)
|
return db.recountFollows(tx, userID, followID)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -770,8 +769,7 @@ type ErrUserNotExist struct {
|
|||||||
// IsErrUserNotExist returns true if the underlying error has the type
|
// IsErrUserNotExist returns true if the underlying error has the type
|
||||||
// ErrUserNotExist.
|
// ErrUserNotExist.
|
||||||
func IsErrUserNotExist(err error) bool {
|
func IsErrUserNotExist(err error) bool {
|
||||||
_, ok := errors.Cause(err).(ErrUserNotExist)
|
return errors.As(err, &ErrUserNotExist{})
|
||||||
return ok
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (err ErrUserNotExist) Error() string {
|
func (err ErrUserNotExist) Error() string {
|
||||||
@ -811,7 +809,7 @@ func (db *users) GetByEmail(ctx context.Context, email string) (*User, error) {
|
|||||||
First(&user).
|
First(&user).
|
||||||
Error
|
Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == gorm.ErrRecordNotFound {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return nil, ErrUserNotExist{args: errutil.Args{"email": email}}
|
return nil, ErrUserNotExist{args: errutil.Args{"email": email}}
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -823,7 +821,7 @@ func (db *users) GetByID(ctx context.Context, id int64) (*User, error) {
|
|||||||
user := new(User)
|
user := new(User)
|
||||||
err := db.WithContext(ctx).Where("id = ?", id).First(user).Error
|
err := db.WithContext(ctx).Where("id = ?", id).First(user).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == gorm.ErrRecordNotFound {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return nil, ErrUserNotExist{args: errutil.Args{"userID": id}}
|
return nil, ErrUserNotExist{args: errutil.Args{"userID": id}}
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -831,11 +829,17 @@ func (db *users) GetByID(ctx context.Context, id int64) (*User, error) {
|
|||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *users) GetByUsername(ctx context.Context, username string) (*User, error) {
|
func getUserByUsername(ctx context.Context, db *gorm.DB, userType UserType, username string) (*User, error) {
|
||||||
|
if username == "" {
|
||||||
|
return nil, ErrUserNotExist{args: errutil.Args{"name": username}}
|
||||||
|
}
|
||||||
user := new(User)
|
user := new(User)
|
||||||
err := db.WithContext(ctx).Where("lower_name = ?", strings.ToLower(username)).First(user).Error
|
err := db.WithContext(ctx).
|
||||||
|
Where("type = ? AND lower_name = ?", userType, strings.ToLower(username)).
|
||||||
|
First(user).
|
||||||
|
Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == gorm.ErrRecordNotFound {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return nil, ErrUserNotExist{args: errutil.Args{"name": username}}
|
return nil, ErrUserNotExist{args: errutil.Args{"name": username}}
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -843,6 +847,10 @@ func (db *users) GetByUsername(ctx context.Context, username string) (*User, err
|
|||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *users) GetByUsername(ctx context.Context, username string) (*User, error) {
|
||||||
|
return getUserByUsername(ctx, db.DB, UserTypeIndividual, username)
|
||||||
|
}
|
||||||
|
|
||||||
func (db *users) GetByKeyID(ctx context.Context, keyID int64) (*User, error) {
|
func (db *users) GetByKeyID(ctx context.Context, keyID int64) (*User, error) {
|
||||||
user := new(User)
|
user := new(User)
|
||||||
err := db.WithContext(ctx).
|
err := db.WithContext(ctx).
|
||||||
@ -851,7 +859,7 @@ func (db *users) GetByKeyID(ctx context.Context, keyID int64) (*User, error) {
|
|||||||
First(user).
|
First(user).
|
||||||
Error
|
Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == gorm.ErrRecordNotFound {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return nil, ErrUserNotExist{args: errutil.Args{"keyID": keyID}}
|
return nil, ErrUserNotExist{args: errutil.Args{"keyID": keyID}}
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -1126,15 +1134,15 @@ func (ErrEmailNotExist) NotFound() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (db *users) GetEmail(ctx context.Context, userID int64, email string, needsActivated bool) (*EmailAddress, error) {
|
func (db *users) GetEmail(ctx context.Context, userID int64, email string, needsActivated bool) (*EmailAddress, error) {
|
||||||
tx := db.WithContext(ctx).Where("uid = ? AND email = ?", userID, email)
|
conds := db.WithContext(ctx).Where("uid = ? AND email = ?", userID, email)
|
||||||
if needsActivated {
|
if needsActivated {
|
||||||
tx = tx.Where("is_activated = ?", true)
|
conds.Where("is_activated = ?", true)
|
||||||
}
|
}
|
||||||
|
|
||||||
emailAddress := new(EmailAddress)
|
emailAddress := new(EmailAddress)
|
||||||
err := tx.First(emailAddress).Error
|
err := conds.First(emailAddress).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == gorm.ErrRecordNotFound {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return nil, ErrEmailNotExist{
|
return nil, ErrEmailNotExist{
|
||||||
args: errutil.Args{
|
args: errutil.Args{
|
||||||
"email": email,
|
"email": email,
|
||||||
@ -1213,7 +1221,7 @@ func (db *users) MarkEmailPrimary(ctx context.Context, userID int64, email strin
|
|||||||
var emailAddress EmailAddress
|
var emailAddress EmailAddress
|
||||||
err := db.WithContext(ctx).Where("uid = ? AND email = ?", userID, email).First(&emailAddress).Error
|
err := db.WithContext(ctx).Where("uid = ? AND email = ?", userID, email).First(&emailAddress).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == gorm.ErrRecordNotFound {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return ErrEmailNotExist{args: errutil.Args{"email": email}}
|
return ErrEmailNotExist{args: errutil.Args{"email": email}}
|
||||||
}
|
}
|
||||||
return errors.Wrap(err, "get email address")
|
return errors.Wrap(err, "get email address")
|
||||||
@ -1317,7 +1325,6 @@ type User struct {
|
|||||||
NumTeams int
|
NumTeams int
|
||||||
NumMembers int
|
NumMembers int
|
||||||
Teams []*Team `xorm:"-" gorm:"-" json:"-"`
|
Teams []*Team `xorm:"-" gorm:"-" json:"-"`
|
||||||
Members []*User `xorm:"-" gorm:"-" json:"-"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// BeforeCreate implements the GORM create hook.
|
// BeforeCreate implements the GORM create hook.
|
||||||
@ -1467,13 +1474,12 @@ func (u *User) IsFollowing(followID int64) bool {
|
|||||||
return Users.IsFollowing(context.TODO(), u.ID, followID)
|
return Users.IsFollowing(context.TODO(), u.ID, followID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsUserOrgOwner returns true if the user is in the owner team of the given
|
// IsUserOrgOwner returns true if the user is an owner of the organization.
|
||||||
// organization.
|
|
||||||
//
|
//
|
||||||
// TODO(unknwon): This is also used in templates, which should be fixed by
|
// TODO(unknwon): This is also used in templates, which should be fixed by
|
||||||
// having a dedicated type `template.User`.
|
// having a dedicated type `template.User`.
|
||||||
func (u *User) IsUserOrgOwner(orgId int64) bool {
|
func (u *User) IsUserOrgOwner(orgID int64) bool {
|
||||||
return IsOrganizationOwner(orgId, u.ID)
|
return Orgs.IsOwnedBy(context.TODO(), orgID, u.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsPublicMember returns true if the user has public membership of the given
|
// IsPublicMember returns true if the user has public membership of the given
|
||||||
@ -1481,8 +1487,9 @@ func (u *User) IsUserOrgOwner(orgId int64) bool {
|
|||||||
//
|
//
|
||||||
// TODO(unknwon): This is also used in templates, which should be fixed by
|
// TODO(unknwon): This is also used in templates, which should be fixed by
|
||||||
// having a dedicated type `template.User`.
|
// having a dedicated type `template.User`.
|
||||||
func (u *User) IsPublicMember(orgId int64) bool {
|
func (u *User) IsPublicMember(orgID int64) bool {
|
||||||
return IsPublicMembership(orgId, u.ID)
|
_, public := Orgs.HasMember(context.TODO(), orgID, u.ID)
|
||||||
|
return public
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOrganizationCount returns the count of organization membership that the
|
// GetOrganizationCount returns the count of organization membership that the
|
||||||
|
@ -801,6 +801,7 @@ func usersGetByID(t *testing.T, ctx context.Context, db *users) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func usersGetByUsername(t *testing.T, ctx context.Context, db *users) {
|
func usersGetByUsername(t *testing.T, ctx context.Context, db *users) {
|
||||||
|
t.Run("correct user type", func(t *testing.T) {
|
||||||
alice, err := db.Create(ctx, "alice", "alice@exmaple.com", CreateUserOptions{})
|
alice, err := db.Create(ctx, "alice", "alice@exmaple.com", CreateUserOptions{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
@ -811,6 +812,12 @@ func usersGetByUsername(t *testing.T, ctx context.Context, db *users) {
|
|||||||
_, err = db.GetByUsername(ctx, "bad_username")
|
_, err = db.GetByUsername(ctx, "bad_username")
|
||||||
wantErr := ErrUserNotExist{args: errutil.Args{"name": "bad_username"}}
|
wantErr := ErrUserNotExist{args: errutil.Args{"name": "bad_username"}}
|
||||||
assert.Equal(t, wantErr, err)
|
assert.Equal(t, wantErr, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("wrong user type", func(t *testing.T) {
|
||||||
|
// org1,err:=NewOrgsStore(db.DB).Create(ctx,"org1","// TODO: Use Orgs.Create
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func usersGetByKeyID(t *testing.T, ctx context.Context, db *users) {
|
func usersGetByKeyID(t *testing.T, ctx context.Context, db *users) {
|
||||||
|
@ -24,9 +24,7 @@ func Organizations(c *context.Context) {
|
|||||||
|
|
||||||
route.RenderUserSearch(c, &route.UserSearchOptions{
|
route.RenderUserSearch(c, &route.UserSearchOptions{
|
||||||
Type: db.UserTypeOrganization,
|
Type: db.UserTypeOrganization,
|
||||||
Counter: func(gocontext.Context) int64 {
|
Counter: db.Orgs.Count,
|
||||||
return db.CountOrganizations()
|
|
||||||
},
|
|
||||||
Ranger: func(_ gocontext.Context, page, pageSize int) ([]*db.User, error) {
|
Ranger: func(_ gocontext.Context, page, pageSize int) ([]*db.User, error) {
|
||||||
return db.Organizations(page, pageSize)
|
return db.Organizations(page, pageSize)
|
||||||
},
|
},
|
||||||
|
@ -104,7 +104,7 @@ func Edit(c *context.APIContext, form api.EditOrgOption) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
org, err = db.GetOrgByName(org.Name)
|
org, err = db.Orgs.GetByName(c.Req.Context(), org.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Error(err, "get organization")
|
c.Error(err, "get organization")
|
||||||
return
|
return
|
||||||
|
@ -87,7 +87,14 @@ func listUserRepositories(c *context.APIContext, username string) {
|
|||||||
// or an organization isn't a member of.
|
// or an organization isn't a member of.
|
||||||
var ownRepos []*db.Repository
|
var ownRepos []*db.Repository
|
||||||
if user.IsOrganization() {
|
if user.IsOrganization() {
|
||||||
ownRepos, _, err = user.GetUserRepositories(c.User.ID, 1, user.NumRepos)
|
ownRepos, _, err = db.Orgs.AccessibleRepositoriesByUser(
|
||||||
|
c.Req.Context(),
|
||||||
|
user.ID,
|
||||||
|
c.User.ID,
|
||||||
|
1,
|
||||||
|
user.NumRepos,
|
||||||
|
db.AccessibleRepositoriesByUserOptions{},
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
ownRepos, err = db.GetUserRepositories(&db.UserRepoOptions{
|
ownRepos, err = db.GetUserRepositories(&db.UserRepoOptions{
|
||||||
UserID: user.ID,
|
UserID: user.ID,
|
||||||
@ -191,7 +198,7 @@ func Create(c *context.APIContext, opt api.CreateRepoOption) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func CreateOrgRepo(c *context.APIContext, opt api.CreateRepoOption) {
|
func CreateOrgRepo(c *context.APIContext, opt api.CreateRepoOption) {
|
||||||
org, err := db.GetOrgByName(c.Params(":org"))
|
org, err := db.Orgs.GetByName(c.Req.Context(), c.Params(":org"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.NotFoundOrError(err, "get organization by name")
|
c.NotFoundOrError(err, "get organization by name")
|
||||||
return
|
return
|
||||||
|
@ -153,9 +153,7 @@ func ExploreOrganizations(c *context.Context) {
|
|||||||
|
|
||||||
RenderUserSearch(c, &UserSearchOptions{
|
RenderUserSearch(c, &UserSearchOptions{
|
||||||
Type: db.UserTypeOrganization,
|
Type: db.UserTypeOrganization,
|
||||||
Counter: func(gocontext.Context) int64 {
|
Counter: db.Orgs.Count,
|
||||||
return db.CountOrganizations()
|
|
||||||
},
|
|
||||||
Ranger: func(_ gocontext.Context, page, pageSize int) ([]*db.User, error) {
|
Ranger: func(_ gocontext.Context, page, pageSize int) ([]*db.User, error) {
|
||||||
return db.Organizations(page, pageSize)
|
return db.Organizations(page, pageSize)
|
||||||
},
|
},
|
||||||
|
@ -23,11 +23,12 @@ func Members(c *context.Context) {
|
|||||||
c.Data["Title"] = org.FullName
|
c.Data["Title"] = org.FullName
|
||||||
c.Data["PageIsOrgMembers"] = true
|
c.Data["PageIsOrgMembers"] = true
|
||||||
|
|
||||||
if err := org.GetMembers(0); err != nil {
|
members, err := db.Orgs.ListMembers(c.Req.Context(), org.ID, db.ListOrgMembersOptions{})
|
||||||
c.Error(err, "get members")
|
if err != nil {
|
||||||
|
c.Error(err, "list members")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.Data["Members"] = org.Members
|
c.Data["Members"] = members
|
||||||
|
|
||||||
c.Success(MEMBERS)
|
c.Success(MEMBERS)
|
||||||
}
|
}
|
||||||
@ -47,26 +48,26 @@ func MembersAction(c *context.Context) {
|
|||||||
c.NotFound()
|
c.NotFound()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = db.ChangeOrgUserStatus(org.ID, uid, false)
|
err = db.Orgs.SetMemberVisibility(c.Req.Context(), org.ID, uid, false)
|
||||||
case "public":
|
case "public":
|
||||||
if c.User.ID != uid && !c.Org.IsOwner {
|
if c.User.ID != uid && !c.Org.IsOwner {
|
||||||
c.NotFound()
|
c.NotFound()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = db.ChangeOrgUserStatus(org.ID, uid, true)
|
err = db.Orgs.SetMemberVisibility(c.Req.Context(), org.ID, uid, true)
|
||||||
case "remove":
|
case "remove":
|
||||||
if !c.Org.IsOwner {
|
if !c.Org.IsOwner {
|
||||||
c.NotFound()
|
c.NotFound()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = org.RemoveMember(uid)
|
err = db.Orgs.RemoveMember(c.Req.Context(), org.ID, uid)
|
||||||
if db.IsErrLastOrgOwner(err) {
|
if db.IsErrLastOrgOwner(err) {
|
||||||
c.Flash.Error(c.Tr("form.last_org_owner"))
|
c.Flash.Error(c.Tr("form.last_org_owner"))
|
||||||
c.Redirect(c.Org.OrgLink + "/members")
|
c.Redirect(c.Org.OrgLink + "/members")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
case "leave":
|
case "leave":
|
||||||
err = org.RemoveMember(c.User.ID)
|
err = db.Orgs.RemoveMember(c.Req.Context(), org.ID, c.User.ID)
|
||||||
if db.IsErrLastOrgOwner(err) {
|
if db.IsErrLastOrgOwner(err) {
|
||||||
c.Flash.Error(c.Tr("form.last_org_owner"))
|
c.Flash.Error(c.Tr("form.last_org_owner"))
|
||||||
c.Redirect(c.Org.OrgLink + "/members")
|
c.Redirect(c.Org.OrgLink + "/members")
|
||||||
@ -108,7 +109,7 @@ func Invitation(c *context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = org.AddMember(u.ID); err != nil {
|
if err = db.Orgs.AddMember(c.Req.Context(), org.ID, u.ID); err != nil {
|
||||||
c.Error(err, "add member")
|
c.Error(err, "add member")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -140,7 +140,14 @@ func Dashboard(c *context.Context) {
|
|||||||
var repos, mirrors []*db.Repository
|
var repos, mirrors []*db.Repository
|
||||||
var repoCount int64
|
var repoCount int64
|
||||||
if ctxUser.IsOrganization() {
|
if ctxUser.IsOrganization() {
|
||||||
repos, repoCount, err = ctxUser.GetUserRepositories(c.User.ID, 1, conf.UI.User.RepoPagingNum)
|
repos, repoCount, err = db.Orgs.AccessibleRepositoriesByUser(
|
||||||
|
c.Req.Context(),
|
||||||
|
ctxUser.ID,
|
||||||
|
c.User.ID,
|
||||||
|
1,
|
||||||
|
conf.UI.User.RepoPagingNum,
|
||||||
|
db.AccessibleRepositoriesByUserOptions{},
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Error(err, "get user repositories")
|
c.Error(err, "get user repositories")
|
||||||
return
|
return
|
||||||
@ -236,7 +243,14 @@ func Issues(c *context.Context) {
|
|||||||
showRepos = make([]*db.Repository, 0, 10)
|
showRepos = make([]*db.Repository, 0, 10)
|
||||||
)
|
)
|
||||||
if ctxUser.IsOrganization() {
|
if ctxUser.IsOrganization() {
|
||||||
repos, _, err = ctxUser.GetUserRepositories(c.User.ID, 1, ctxUser.NumRepos)
|
repos, _, err = db.Orgs.AccessibleRepositoriesByUser(
|
||||||
|
c.Req.Context(),
|
||||||
|
ctxUser.ID,
|
||||||
|
c.User.ID,
|
||||||
|
1,
|
||||||
|
ctxUser.NumRepos,
|
||||||
|
db.AccessibleRepositoriesByUserOptions{SkipCount: true},
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Error(err, "get repositories")
|
c.Error(err, "get repositories")
|
||||||
return
|
return
|
||||||
@ -409,7 +423,14 @@ func showOrgProfile(c *context.Context) {
|
|||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
if c.IsLogged && !c.User.IsAdmin {
|
if c.IsLogged && !c.User.IsAdmin {
|
||||||
repos, count, err = org.GetUserRepositories(c.User.ID, page, conf.UI.User.RepoPagingNum)
|
repos, count, err = db.Orgs.AccessibleRepositoriesByUser(
|
||||||
|
c.Req.Context(),
|
||||||
|
org.ID,
|
||||||
|
c.User.ID,
|
||||||
|
page,
|
||||||
|
conf.UI.User.RepoPagingNum,
|
||||||
|
db.AccessibleRepositoriesByUserOptions{},
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Error(err, "get user repositories")
|
c.Error(err, "get user repositories")
|
||||||
return
|
return
|
||||||
@ -432,11 +453,12 @@ func showOrgProfile(c *context.Context) {
|
|||||||
}
|
}
|
||||||
c.Data["Page"] = paginater.New(int(count), conf.UI.User.RepoPagingNum, page, 5)
|
c.Data["Page"] = paginater.New(int(count), conf.UI.User.RepoPagingNum, page, 5)
|
||||||
|
|
||||||
if err := org.GetMembers(12); err != nil {
|
members, err := db.Orgs.ListMembers(c.Req.Context(), org.ID, db.ListOrgMembersOptions{Limit: 12})
|
||||||
c.Error(err, "get members")
|
if err != nil {
|
||||||
|
c.Error(err, "list members")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.Data["Members"] = org.Members
|
c.Data["Members"] = members
|
||||||
|
|
||||||
c.Data["Teams"] = org.Teams
|
c.Data["Teams"] = org.Teams
|
||||||
|
|
||||||
|
@ -566,7 +566,7 @@ func SettingsOrganizations(c *context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func SettingsLeaveOrganization(c *context.Context) {
|
func SettingsLeaveOrganization(c *context.Context) {
|
||||||
if err := db.RemoveOrgUser(c.QueryInt64("id"), c.User.ID); err != nil {
|
if err := db.Orgs.RemoveMember(c.Req.Context(), c.QueryInt64("id"), c.User.ID); err != nil {
|
||||||
if db.IsErrLastOrgOwner(err) {
|
if db.IsErrLastOrgOwner(err) {
|
||||||
c.Flash.Error(c.Tr("form.last_org_owner"))
|
c.Flash.Error(c.Tr("form.last_org_owner"))
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user