]> source.dussan.org Git - gitea.git/commitdiff
Move AddCollabrator and CreateRepositoryByExample to service layer (#32419)
authorLunny Xiao <xiaolunwen@gmail.com>
Thu, 7 Nov 2024 03:28:11 +0000 (19:28 -0800)
committerGitHub <noreply@github.com>
Thu, 7 Nov 2024 03:28:11 +0000 (11:28 +0800)
- [x] Move `CreateRepositoryByExample` to service layer
- [x] Move `AddCollabrator` to service layer
- [x] Add a new parameter for `AddCollabrator` so that changing mode
immediately after that will become unnecessary.

19 files changed:
models/perm/access_mode.go
modules/repository/collaborator.go [deleted file]
modules/repository/collaborator_test.go [deleted file]
modules/repository/create.go
modules/repository/main_test.go
routers/api/v1/api.go
routers/api/v1/repo/collaborators.go
routers/web/repo/setting/collaboration.go
services/feed/action_test.go
services/issue/main_test.go
services/mailer/main_test.go
services/repository/adopt.go
services/repository/collaboration.go
services/repository/collaboration_test.go
services/repository/create.go
services/repository/fork.go
services/repository/generate.go
services/repository/transfer.go
templates/swagger/v1_json.tmpl

index 0364191e2e9b29121be90a1468dc149cf6e0339c..6baeb5531a51a8e4add6d189ed6a4f9227010cda 100644 (file)
@@ -60,3 +60,6 @@ func ParseAccessMode(permission string, allowed ...AccessMode) AccessMode {
        }
        return util.Iif(slices.Contains(allowed, m), m, AccessModeNone)
 }
+
+// ErrInvalidAccessMode is returned when an invalid access mode is used
+var ErrInvalidAccessMode = util.NewInvalidArgumentErrorf("Invalid access mode")
diff --git a/modules/repository/collaborator.go b/modules/repository/collaborator.go
deleted file mode 100644 (file)
index f71c58f..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2022 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package repository
-
-import (
-       "context"
-
-       "code.gitea.io/gitea/models/db"
-       "code.gitea.io/gitea/models/perm"
-       access_model "code.gitea.io/gitea/models/perm/access"
-       repo_model "code.gitea.io/gitea/models/repo"
-       user_model "code.gitea.io/gitea/models/user"
-
-       "xorm.io/builder"
-)
-
-func AddCollaborator(ctx context.Context, repo *repo_model.Repository, u *user_model.User) error {
-       if err := repo.LoadOwner(ctx); err != nil {
-               return err
-       }
-
-       if user_model.IsUserBlockedBy(ctx, u, repo.OwnerID) || user_model.IsUserBlockedBy(ctx, repo.Owner, u.ID) {
-               return user_model.ErrBlockedUser
-       }
-
-       return db.WithTx(ctx, func(ctx context.Context) error {
-               has, err := db.Exist[repo_model.Collaboration](ctx, builder.Eq{
-                       "repo_id": repo.ID,
-                       "user_id": u.ID,
-               })
-               if err != nil {
-                       return err
-               } else if has {
-                       return nil
-               }
-
-               if err = db.Insert(ctx, &repo_model.Collaboration{
-                       RepoID: repo.ID,
-                       UserID: u.ID,
-                       Mode:   perm.AccessModeWrite,
-               }); err != nil {
-                       return err
-               }
-
-               return access_model.RecalculateUserAccess(ctx, repo, u.ID)
-       })
-}
diff --git a/modules/repository/collaborator_test.go b/modules/repository/collaborator_test.go
deleted file mode 100644 (file)
index 622f6ab..0000000
+++ /dev/null
@@ -1,280 +0,0 @@
-// Copyright 2019 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package repository
-
-import (
-       "testing"
-
-       "code.gitea.io/gitea/models/db"
-       "code.gitea.io/gitea/models/organization"
-       perm_model "code.gitea.io/gitea/models/perm"
-       access_model "code.gitea.io/gitea/models/perm/access"
-       repo_model "code.gitea.io/gitea/models/repo"
-       "code.gitea.io/gitea/models/unit"
-       "code.gitea.io/gitea/models/unittest"
-       user_model "code.gitea.io/gitea/models/user"
-
-       "github.com/stretchr/testify/assert"
-)
-
-func TestRepository_AddCollaborator(t *testing.T) {
-       assert.NoError(t, unittest.PrepareTestDatabase())
-
-       testSuccess := func(repoID, userID int64) {
-               repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID})
-               assert.NoError(t, repo.LoadOwner(db.DefaultContext))
-               user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userID})
-               assert.NoError(t, AddCollaborator(db.DefaultContext, repo, user))
-               unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: repoID}, &user_model.User{ID: userID})
-       }
-       testSuccess(1, 4)
-       testSuccess(1, 4)
-       testSuccess(3, 4)
-}
-
-func TestRepoPermissionPublicNonOrgRepo(t *testing.T) {
-       assert.NoError(t, unittest.PrepareTestDatabase())
-
-       // public non-organization repo
-       repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
-       assert.NoError(t, repo.LoadUnits(db.DefaultContext))
-
-       // plain user
-       user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
-       perm, err := access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
-       assert.NoError(t, err)
-       for _, unit := range repo.Units {
-               assert.True(t, perm.CanRead(unit.Type))
-               assert.False(t, perm.CanWrite(unit.Type))
-       }
-
-       // change to collaborator
-       assert.NoError(t, AddCollaborator(db.DefaultContext, repo, user))
-       perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
-       assert.NoError(t, err)
-       for _, unit := range repo.Units {
-               assert.True(t, perm.CanRead(unit.Type))
-               assert.True(t, perm.CanWrite(unit.Type))
-       }
-
-       // collaborator
-       collaborator := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
-       perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, collaborator)
-       assert.NoError(t, err)
-       for _, unit := range repo.Units {
-               assert.True(t, perm.CanRead(unit.Type))
-               assert.True(t, perm.CanWrite(unit.Type))
-       }
-
-       // owner
-       owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
-       perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, owner)
-       assert.NoError(t, err)
-       for _, unit := range repo.Units {
-               assert.True(t, perm.CanRead(unit.Type))
-               assert.True(t, perm.CanWrite(unit.Type))
-       }
-
-       // admin
-       admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
-       perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, admin)
-       assert.NoError(t, err)
-       for _, unit := range repo.Units {
-               assert.True(t, perm.CanRead(unit.Type))
-               assert.True(t, perm.CanWrite(unit.Type))
-       }
-}
-
-func TestRepoPermissionPrivateNonOrgRepo(t *testing.T) {
-       assert.NoError(t, unittest.PrepareTestDatabase())
-
-       // private non-organization repo
-       repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
-       assert.NoError(t, repo.LoadUnits(db.DefaultContext))
-
-       // plain user
-       user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
-       perm, err := access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
-       assert.NoError(t, err)
-       for _, unit := range repo.Units {
-               assert.False(t, perm.CanRead(unit.Type))
-               assert.False(t, perm.CanWrite(unit.Type))
-       }
-
-       // change to collaborator to default write access
-       assert.NoError(t, AddCollaborator(db.DefaultContext, repo, user))
-       perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
-       assert.NoError(t, err)
-       for _, unit := range repo.Units {
-               assert.True(t, perm.CanRead(unit.Type))
-               assert.True(t, perm.CanWrite(unit.Type))
-       }
-
-       assert.NoError(t, repo_model.ChangeCollaborationAccessMode(db.DefaultContext, repo, user.ID, perm_model.AccessModeRead))
-       perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
-       assert.NoError(t, err)
-       for _, unit := range repo.Units {
-               assert.True(t, perm.CanRead(unit.Type))
-               assert.False(t, perm.CanWrite(unit.Type))
-       }
-
-       // owner
-       owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
-       perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, owner)
-       assert.NoError(t, err)
-       for _, unit := range repo.Units {
-               assert.True(t, perm.CanRead(unit.Type))
-               assert.True(t, perm.CanWrite(unit.Type))
-       }
-
-       // admin
-       admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
-       perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, admin)
-       assert.NoError(t, err)
-       for _, unit := range repo.Units {
-               assert.True(t, perm.CanRead(unit.Type))
-               assert.True(t, perm.CanWrite(unit.Type))
-       }
-}
-
-func TestRepoPermissionPublicOrgRepo(t *testing.T) {
-       assert.NoError(t, unittest.PrepareTestDatabase())
-
-       // public organization repo
-       repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 32})
-       assert.NoError(t, repo.LoadUnits(db.DefaultContext))
-
-       // plain user
-       user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
-       perm, err := access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
-       assert.NoError(t, err)
-       for _, unit := range repo.Units {
-               assert.True(t, perm.CanRead(unit.Type))
-               assert.False(t, perm.CanWrite(unit.Type))
-       }
-
-       // change to collaborator to default write access
-       assert.NoError(t, AddCollaborator(db.DefaultContext, repo, user))
-       perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
-       assert.NoError(t, err)
-       for _, unit := range repo.Units {
-               assert.True(t, perm.CanRead(unit.Type))
-               assert.True(t, perm.CanWrite(unit.Type))
-       }
-
-       assert.NoError(t, repo_model.ChangeCollaborationAccessMode(db.DefaultContext, repo, user.ID, perm_model.AccessModeRead))
-       perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
-       assert.NoError(t, err)
-       for _, unit := range repo.Units {
-               assert.True(t, perm.CanRead(unit.Type))
-               assert.False(t, perm.CanWrite(unit.Type))
-       }
-
-       // org member team owner
-       owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
-       perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, owner)
-       assert.NoError(t, err)
-       for _, unit := range repo.Units {
-               assert.True(t, perm.CanRead(unit.Type))
-               assert.True(t, perm.CanWrite(unit.Type))
-       }
-
-       // org member team tester
-       member := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 15})
-       perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, member)
-       assert.NoError(t, err)
-       for _, unit := range repo.Units {
-               assert.True(t, perm.CanRead(unit.Type))
-       }
-       assert.True(t, perm.CanWrite(unit.TypeIssues))
-       assert.False(t, perm.CanWrite(unit.TypeCode))
-
-       // admin
-       admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
-       perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, admin)
-       assert.NoError(t, err)
-       for _, unit := range repo.Units {
-               assert.True(t, perm.CanRead(unit.Type))
-               assert.True(t, perm.CanWrite(unit.Type))
-       }
-}
-
-func TestRepoPermissionPrivateOrgRepo(t *testing.T) {
-       assert.NoError(t, unittest.PrepareTestDatabase())
-
-       // private organization repo
-       repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 24})
-       assert.NoError(t, repo.LoadUnits(db.DefaultContext))
-
-       // plain user
-       user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
-       perm, err := access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
-       assert.NoError(t, err)
-       for _, unit := range repo.Units {
-               assert.False(t, perm.CanRead(unit.Type))
-               assert.False(t, perm.CanWrite(unit.Type))
-       }
-
-       // change to collaborator to default write access
-       assert.NoError(t, AddCollaborator(db.DefaultContext, repo, user))
-       perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
-       assert.NoError(t, err)
-       for _, unit := range repo.Units {
-               assert.True(t, perm.CanRead(unit.Type))
-               assert.True(t, perm.CanWrite(unit.Type))
-       }
-
-       assert.NoError(t, repo_model.ChangeCollaborationAccessMode(db.DefaultContext, repo, user.ID, perm_model.AccessModeRead))
-       perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
-       assert.NoError(t, err)
-       for _, unit := range repo.Units {
-               assert.True(t, perm.CanRead(unit.Type))
-               assert.False(t, perm.CanWrite(unit.Type))
-       }
-
-       // org member team owner
-       owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 15})
-       perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, owner)
-       assert.NoError(t, err)
-       for _, unit := range repo.Units {
-               assert.True(t, perm.CanRead(unit.Type))
-               assert.True(t, perm.CanWrite(unit.Type))
-       }
-
-       // update team information and then check permission
-       team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 5})
-       err = organization.UpdateTeamUnits(db.DefaultContext, team, nil)
-       assert.NoError(t, err)
-       perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, owner)
-       assert.NoError(t, err)
-       for _, unit := range repo.Units {
-               assert.True(t, perm.CanRead(unit.Type))
-               assert.True(t, perm.CanWrite(unit.Type))
-       }
-
-       // org member team tester
-       tester := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
-       perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, tester)
-       assert.NoError(t, err)
-       assert.True(t, perm.CanWrite(unit.TypeIssues))
-       assert.False(t, perm.CanWrite(unit.TypeCode))
-       assert.False(t, perm.CanRead(unit.TypeCode))
-
-       // org member team reviewer
-       reviewer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 20})
-       perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, reviewer)
-       assert.NoError(t, err)
-       assert.False(t, perm.CanRead(unit.TypeIssues))
-       assert.False(t, perm.CanWrite(unit.TypeCode))
-       assert.True(t, perm.CanRead(unit.TypeCode))
-
-       // admin
-       admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
-       perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, admin)
-       assert.NoError(t, err)
-       for _, unit := range repo.Units {
-               assert.True(t, perm.CanRead(unit.Type))
-               assert.True(t, perm.CanWrite(unit.Type))
-       }
-}
index 4f18b9b3fa647b92c74f5740ecca703a30124e7a..b4f7033bd7e3c37ebe8834c87f638cee0a824460 100644 (file)
@@ -11,160 +11,17 @@ import (
        "path/filepath"
        "strings"
 
-       "code.gitea.io/gitea/models"
        activities_model "code.gitea.io/gitea/models/activities"
        "code.gitea.io/gitea/models/db"
        git_model "code.gitea.io/gitea/models/git"
-       "code.gitea.io/gitea/models/organization"
-       "code.gitea.io/gitea/models/perm"
        access_model "code.gitea.io/gitea/models/perm/access"
        repo_model "code.gitea.io/gitea/models/repo"
-       "code.gitea.io/gitea/models/unit"
-       user_model "code.gitea.io/gitea/models/user"
-       "code.gitea.io/gitea/models/webhook"
        issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
        "code.gitea.io/gitea/modules/log"
-       "code.gitea.io/gitea/modules/setting"
        api "code.gitea.io/gitea/modules/structs"
        "code.gitea.io/gitea/modules/util"
 )
 
-// CreateRepositoryByExample creates a repository for the user/organization.
-func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, repo *repo_model.Repository, overwriteOrAdopt, isFork bool) (err error) {
-       if err = repo_model.IsUsableRepoName(repo.Name); err != nil {
-               return err
-       }
-
-       has, err := repo_model.IsRepositoryModelExist(ctx, u, repo.Name)
-       if err != nil {
-               return fmt.Errorf("IsRepositoryExist: %w", err)
-       } else if has {
-               return repo_model.ErrRepoAlreadyExist{
-                       Uname: u.Name,
-                       Name:  repo.Name,
-               }
-       }
-
-       repoPath := repo_model.RepoPath(u.Name, repo.Name)
-       isExist, err := util.IsExist(repoPath)
-       if err != nil {
-               log.Error("Unable to check if %s exists. Error: %v", repoPath, err)
-               return err
-       }
-       if !overwriteOrAdopt && isExist {
-               log.Error("Files already exist in %s and we are not going to adopt or delete.", repoPath)
-               return repo_model.ErrRepoFilesAlreadyExist{
-                       Uname: u.Name,
-                       Name:  repo.Name,
-               }
-       }
-
-       if err = db.Insert(ctx, repo); err != nil {
-               return err
-       }
-       if err = repo_model.DeleteRedirect(ctx, u.ID, repo.Name); err != nil {
-               return err
-       }
-
-       // insert units for repo
-       defaultUnits := unit.DefaultRepoUnits
-       if isFork {
-               defaultUnits = unit.DefaultForkRepoUnits
-       }
-       units := make([]repo_model.RepoUnit, 0, len(defaultUnits))
-       for _, tp := range defaultUnits {
-               if tp == unit.TypeIssues {
-                       units = append(units, repo_model.RepoUnit{
-                               RepoID: repo.ID,
-                               Type:   tp,
-                               Config: &repo_model.IssuesConfig{
-                                       EnableTimetracker:                setting.Service.DefaultEnableTimetracking,
-                                       AllowOnlyContributorsToTrackTime: setting.Service.DefaultAllowOnlyContributorsToTrackTime,
-                                       EnableDependencies:               setting.Service.DefaultEnableDependencies,
-                               },
-                       })
-               } else if tp == unit.TypePullRequests {
-                       units = append(units, repo_model.RepoUnit{
-                               RepoID: repo.ID,
-                               Type:   tp,
-                               Config: &repo_model.PullRequestsConfig{
-                                       AllowMerge: true, AllowRebase: true, AllowRebaseMerge: true, AllowSquash: true, AllowFastForwardOnly: true,
-                                       DefaultMergeStyle: repo_model.MergeStyle(setting.Repository.PullRequest.DefaultMergeStyle),
-                                       AllowRebaseUpdate: true,
-                               },
-                       })
-               } else if tp == unit.TypeProjects {
-                       units = append(units, repo_model.RepoUnit{
-                               RepoID: repo.ID,
-                               Type:   tp,
-                               Config: &repo_model.ProjectsConfig{ProjectsMode: repo_model.ProjectsModeAll},
-                       })
-               } else {
-                       units = append(units, repo_model.RepoUnit{
-                               RepoID: repo.ID,
-                               Type:   tp,
-                       })
-               }
-       }
-
-       if err = db.Insert(ctx, units); err != nil {
-               return err
-       }
-
-       // Remember visibility preference.
-       u.LastRepoVisibility = repo.IsPrivate
-       if err = user_model.UpdateUserCols(ctx, u, "last_repo_visibility"); err != nil {
-               return fmt.Errorf("UpdateUserCols: %w", err)
-       }
-
-       if err = user_model.IncrUserRepoNum(ctx, u.ID); err != nil {
-               return fmt.Errorf("IncrUserRepoNum: %w", err)
-       }
-       u.NumRepos++
-
-       // Give access to all members in teams with access to all repositories.
-       if u.IsOrganization() {
-               teams, err := organization.FindOrgTeams(ctx, u.ID)
-               if err != nil {
-                       return fmt.Errorf("FindOrgTeams: %w", err)
-               }
-               for _, t := range teams {
-                       if t.IncludesAllRepositories {
-                               if err := models.AddRepository(ctx, t, repo); err != nil {
-                                       return fmt.Errorf("AddRepository: %w", err)
-                               }
-                       }
-               }
-
-               if isAdmin, err := access_model.IsUserRepoAdmin(ctx, repo, doer); err != nil {
-                       return fmt.Errorf("IsUserRepoAdmin: %w", err)
-               } else if !isAdmin {
-                       // Make creator repo admin if it wasn't assigned automatically
-                       if err = AddCollaborator(ctx, repo, doer); err != nil {
-                               return fmt.Errorf("AddCollaborator: %w", err)
-                       }
-                       if err = repo_model.ChangeCollaborationAccessMode(ctx, repo, doer.ID, perm.AccessModeAdmin); err != nil {
-                               return fmt.Errorf("ChangeCollaborationAccessModeCtx: %w", err)
-                       }
-               }
-       } else if err = access_model.RecalculateAccesses(ctx, repo); err != nil {
-               // Organization automatically called this in AddRepository method.
-               return fmt.Errorf("RecalculateAccesses: %w", err)
-       }
-
-       if setting.Service.AutoWatchNewRepos {
-               if err = repo_model.WatchRepo(ctx, doer, repo, true); err != nil {
-                       return fmt.Errorf("WatchRepo: %w", err)
-               }
-       }
-
-       if err = webhook.CopyDefaultWebhooksToRepo(ctx, repo.ID); err != nil {
-               return fmt.Errorf("CopyDefaultWebhooksToRepo: %w", err)
-       }
-
-       return nil
-}
-
 const notRegularFileMode = os.ModeSymlink | os.ModeNamedPipe | os.ModeSocket | os.ModeDevice | os.ModeCharDevice | os.ModeIrregular
 
 // getDirectorySize returns the disk consumption for a given path
index f81dfcdafbc80f9d713bd1bbd64983a4c7f69af8..799e8c17c3dc60405ffed32266aa533db88cc3ed 100644 (file)
@@ -8,6 +8,7 @@ import (
 
        "code.gitea.io/gitea/models/unittest"
 
+       _ "code.gitea.io/gitea/models"
        _ "code.gitea.io/gitea/models/actions"
 )
 
index bfc601c8356aebefef90115abf22e0f2a1a8229c..23f466873bad950186b4df23c3921bd4f25a6952 100644 (file)
@@ -1172,7 +1172,7 @@ func Routes() *web.Router {
                                        m.Get("", reqAnyRepoReader(), repo.ListCollaborators)
                                        m.Group("/{collaborator}", func() {
                                                m.Combo("").Get(reqAnyRepoReader(), repo.IsCollaborator).
-                                                       Put(reqAdmin(), bind(api.AddCollaboratorOption{}), repo.AddCollaborator).
+                                                       Put(reqAdmin(), bind(api.AddCollaboratorOption{}), repo.AddOrUpdateCollaborator).
                                                        Delete(reqAdmin(), repo.DeleteCollaborator)
                                                m.Get("/permission", repo.GetRepoPermissions)
                                        })
index 39c9ba527dfae1558922d846bd4875833d969777..ea9d8b0f37e9c02ec4d5bce2d86dcc222aaf5575 100644 (file)
@@ -12,7 +12,6 @@ import (
        access_model "code.gitea.io/gitea/models/perm/access"
        repo_model "code.gitea.io/gitea/models/repo"
        user_model "code.gitea.io/gitea/models/user"
-       repo_module "code.gitea.io/gitea/modules/repository"
        api "code.gitea.io/gitea/modules/structs"
        "code.gitea.io/gitea/modules/web"
        "code.gitea.io/gitea/routers/api/v1/utils"
@@ -123,11 +122,11 @@ func IsCollaborator(ctx *context.APIContext) {
        }
 }
 
-// AddCollaborator add a collaborator to a repository
-func AddCollaborator(ctx *context.APIContext) {
+// AddOrUpdateCollaborator add or update a collaborator to a repository
+func AddOrUpdateCollaborator(ctx *context.APIContext) {
        // swagger:operation PUT /repos/{owner}/{repo}/collaborators/{collaborator} repository repoAddCollaborator
        // ---
-       // summary: Add a collaborator to a repository
+       // summary: Add or Update a collaborator to a repository
        // produces:
        // - application/json
        // parameters:
@@ -177,22 +176,20 @@ func AddCollaborator(ctx *context.APIContext) {
                return
        }
 
-       if err := repo_module.AddCollaborator(ctx, ctx.Repo.Repository, collaborator); err != nil {
+       p := perm.AccessModeWrite
+       if form.Permission != nil {
+               p = perm.ParseAccessMode(*form.Permission)
+       }
+
+       if err := repo_service.AddOrUpdateCollaborator(ctx, ctx.Repo.Repository, collaborator, p); err != nil {
                if errors.Is(err, user_model.ErrBlockedUser) {
-                       ctx.Error(http.StatusForbidden, "AddCollaborator", err)
+                       ctx.Error(http.StatusForbidden, "AddOrUpdateCollaborator", err)
                } else {
-                       ctx.Error(http.StatusInternalServerError, "AddCollaborator", err)
+                       ctx.Error(http.StatusInternalServerError, "AddOrUpdateCollaborator", err)
                }
                return
        }
 
-       if form.Permission != nil {
-               if err := repo_model.ChangeCollaborationAccessMode(ctx, ctx.Repo.Repository, collaborator.ID, perm.ParseAccessMode(*form.Permission)); err != nil {
-                       ctx.Error(http.StatusInternalServerError, "ChangeCollaborationAccessMode", err)
-                       return
-               }
-       }
-
        ctx.Status(http.StatusNoContent)
 }
 
index 31f9f76d0f99676e60f5b8d598de691f090581bc..18ecff8250858684fdc1e004c3fe3d0836e24738 100644 (file)
@@ -14,7 +14,6 @@ import (
        unit_model "code.gitea.io/gitea/models/unit"
        user_model "code.gitea.io/gitea/models/user"
        "code.gitea.io/gitea/modules/log"
-       repo_module "code.gitea.io/gitea/modules/repository"
        "code.gitea.io/gitea/modules/setting"
        "code.gitea.io/gitea/services/context"
        "code.gitea.io/gitea/services/mailer"
@@ -100,12 +99,12 @@ func CollaborationPost(ctx *context.Context) {
                }
        }
 
-       if err = repo_module.AddCollaborator(ctx, ctx.Repo.Repository, u); err != nil {
+       if err = repo_service.AddOrUpdateCollaborator(ctx, ctx.Repo.Repository, u, perm.AccessModeWrite); err != nil {
                if errors.Is(err, user_model.ErrBlockedUser) {
                        ctx.Flash.Error(ctx.Tr("repo.settings.add_collaborator.blocked_user"))
                        ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
                } else {
-                       ctx.ServerError("AddCollaborator", err)
+                       ctx.ServerError("AddOrUpdateCollaborator", err)
                }
                return
        }
index e1b071d8f604e276ef764bc45490f28620ad4529..60cf7fbb49d20b8cd08b14fc9f1a37865ea60531 100644 (file)
@@ -13,6 +13,7 @@ import (
        "code.gitea.io/gitea/models/unittest"
        user_model "code.gitea.io/gitea/models/user"
 
+       _ "code.gitea.io/gitea/models"
        _ "code.gitea.io/gitea/models/actions"
 
        "github.com/stretchr/testify/assert"
index 5dac54183baf41cfb62c1b41a738dfff1ee23c4d..819c5d98c3f6c60231aeca135ea4abc617e5b18d 100644 (file)
@@ -8,6 +8,7 @@ import (
 
        "code.gitea.io/gitea/models/unittest"
 
+       _ "code.gitea.io/gitea/models"
        _ "code.gitea.io/gitea/models/actions"
 )
 
index f803c736ca14ac699c52f34299f4a71813b3a32d..5591bea02b4016d8da422c59c18a4ad56dd3936a 100644 (file)
@@ -8,6 +8,7 @@ import (
 
        "code.gitea.io/gitea/models/unittest"
 
+       _ "code.gitea.io/gitea/models"
        _ "code.gitea.io/gitea/models/actions"
 )
 
index 3d6fe71a091910bab1827bf653ff993b8bc62c91..615f4d482c034f96c0f2198565da9a5194294e44 100644 (file)
@@ -66,7 +66,7 @@ func AdoptRepository(ctx context.Context, doer, u *user_model.User, opts CreateR
                        }
                }
 
-               if err := repo_module.CreateRepositoryByExample(ctx, doer, u, repo, true, false); err != nil {
+               if err := CreateRepositoryByExample(ctx, doer, u, repo, true, false); err != nil {
                        return err
                }
 
index 4a43ae2a28fca7ac339cbb7c20bccc68492fd09b..abe0489fc5a81ddb5110a56e7885f9b189c3ca9d 100644 (file)
@@ -9,11 +9,60 @@ import (
 
        "code.gitea.io/gitea/models"
        "code.gitea.io/gitea/models/db"
+       "code.gitea.io/gitea/models/perm"
        access_model "code.gitea.io/gitea/models/perm/access"
        repo_model "code.gitea.io/gitea/models/repo"
        user_model "code.gitea.io/gitea/models/user"
+
+       "xorm.io/builder"
 )
 
+func AddOrUpdateCollaborator(ctx context.Context, repo *repo_model.Repository, u *user_model.User, mode perm.AccessMode) error {
+       // only allow valid access modes, read, write and admin
+       if mode < perm.AccessModeRead || mode > perm.AccessModeAdmin {
+               return perm.ErrInvalidAccessMode
+       }
+
+       if err := repo.LoadOwner(ctx); err != nil {
+               return err
+       }
+
+       if user_model.IsUserBlockedBy(ctx, u, repo.OwnerID) || user_model.IsUserBlockedBy(ctx, repo.Owner, u.ID) {
+               return user_model.ErrBlockedUser
+       }
+
+       return db.WithTx(ctx, func(ctx context.Context) error {
+               collaboration, has, err := db.Get[repo_model.Collaboration](ctx, builder.Eq{
+                       "repo_id": repo.ID,
+                       "user_id": u.ID,
+               })
+               if err != nil {
+                       return err
+               } else if has {
+                       if collaboration.Mode == mode {
+                               return nil
+                       }
+                       if _, err = db.GetEngine(ctx).
+                               Where("repo_id=?", repo.ID).
+                               And("user_id=?", u.ID).
+                               Cols("mode").
+                               Update(&repo_model.Collaboration{
+                                       Mode: mode,
+                               }); err != nil {
+                               return err
+                       }
+               } else if err = db.Insert(ctx, &repo_model.Collaboration{
+                       RepoID: repo.ID,
+                       UserID: u.ID,
+                       Mode:   mode,
+               }); err != nil {
+                       return err
+               }
+
+               return access_model.RecalculateUserAccess(ctx, repo, u.ID)
+       })
+}
+
 // DeleteCollaboration removes collaboration relation between the user and repository.
 func DeleteCollaboration(ctx context.Context, repo *repo_model.Repository, collaborator *user_model.User) (err error) {
        collaboration := &repo_model.Collaboration{
index a2eb06b81a220cbd98965a76cc44ff2f708fb0dd..2b9a5d0b8bab1633c34bd491cf28abff4c2b0210 100644 (file)
@@ -7,6 +7,7 @@ import (
        "testing"
 
        "code.gitea.io/gitea/models/db"
+       "code.gitea.io/gitea/models/perm"
        repo_model "code.gitea.io/gitea/models/repo"
        "code.gitea.io/gitea/models/unittest"
        user_model "code.gitea.io/gitea/models/user"
@@ -14,6 +15,21 @@ import (
        "github.com/stretchr/testify/assert"
 )
 
+func TestRepository_AddCollaborator(t *testing.T) {
+       assert.NoError(t, unittest.PrepareTestDatabase())
+
+       testSuccess := func(repoID, userID int64) {
+               repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID})
+               assert.NoError(t, repo.LoadOwner(db.DefaultContext))
+               user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userID})
+               assert.NoError(t, AddOrUpdateCollaborator(db.DefaultContext, repo, user, perm.AccessModeWrite))
+               unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: repoID}, &user_model.User{ID: userID})
+       }
+       testSuccess(1, 4)
+       testSuccess(1, 4)
+       testSuccess(3, 4)
+}
+
 func TestRepository_DeleteCollaboration(t *testing.T) {
        assert.NoError(t, unittest.PrepareTestDatabase())
 
index 282b2d3e58d46ec01727e755e39637914ead69ba..261ac7fcccae01e167f902ad4227eb504461c6d7 100644 (file)
@@ -12,9 +12,15 @@ import (
        "strings"
        "time"
 
+       "code.gitea.io/gitea/models"
        "code.gitea.io/gitea/models/db"
+       "code.gitea.io/gitea/models/organization"
+       "code.gitea.io/gitea/models/perm"
+       access_model "code.gitea.io/gitea/models/perm/access"
        repo_model "code.gitea.io/gitea/models/repo"
+       "code.gitea.io/gitea/models/unit"
        user_model "code.gitea.io/gitea/models/user"
+       "code.gitea.io/gitea/models/webhook"
        "code.gitea.io/gitea/modules/git"
        "code.gitea.io/gitea/modules/gitrepo"
        "code.gitea.io/gitea/modules/log"
@@ -243,7 +249,7 @@ func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opt
        var rollbackRepo *repo_model.Repository
 
        if err := db.WithTx(ctx, func(ctx context.Context) error {
-               if err := repo_module.CreateRepositoryByExample(ctx, doer, u, repo, false, false); err != nil {
+               if err := CreateRepositoryByExample(ctx, doer, u, repo, false, false); err != nil {
                        return err
                }
 
@@ -335,3 +341,136 @@ func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opt
 
        return repo, nil
 }
+
+// CreateRepositoryByExample creates a repository for the user/organization.
+func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, repo *repo_model.Repository, overwriteOrAdopt, isFork bool) (err error) {
+       if err = repo_model.IsUsableRepoName(repo.Name); err != nil {
+               return err
+       }
+
+       has, err := repo_model.IsRepositoryModelExist(ctx, u, repo.Name)
+       if err != nil {
+               return fmt.Errorf("IsRepositoryExist: %w", err)
+       } else if has {
+               return repo_model.ErrRepoAlreadyExist{
+                       Uname: u.Name,
+                       Name:  repo.Name,
+               }
+       }
+
+       repoPath := repo_model.RepoPath(u.Name, repo.Name)
+       isExist, err := util.IsExist(repoPath)
+       if err != nil {
+               log.Error("Unable to check if %s exists. Error: %v", repoPath, err)
+               return err
+       }
+       if !overwriteOrAdopt && isExist {
+               log.Error("Files already exist in %s and we are not going to adopt or delete.", repoPath)
+               return repo_model.ErrRepoFilesAlreadyExist{
+                       Uname: u.Name,
+                       Name:  repo.Name,
+               }
+       }
+
+       if err = db.Insert(ctx, repo); err != nil {
+               return err
+       }
+       if err = repo_model.DeleteRedirect(ctx, u.ID, repo.Name); err != nil {
+               return err
+       }
+
+       // insert units for repo
+       defaultUnits := unit.DefaultRepoUnits
+       if isFork {
+               defaultUnits = unit.DefaultForkRepoUnits
+       }
+       units := make([]repo_model.RepoUnit, 0, len(defaultUnits))
+       for _, tp := range defaultUnits {
+               if tp == unit.TypeIssues {
+                       units = append(units, repo_model.RepoUnit{
+                               RepoID: repo.ID,
+                               Type:   tp,
+                               Config: &repo_model.IssuesConfig{
+                                       EnableTimetracker:                setting.Service.DefaultEnableTimetracking,
+                                       AllowOnlyContributorsToTrackTime: setting.Service.DefaultAllowOnlyContributorsToTrackTime,
+                                       EnableDependencies:               setting.Service.DefaultEnableDependencies,
+                               },
+                       })
+               } else if tp == unit.TypePullRequests {
+                       units = append(units, repo_model.RepoUnit{
+                               RepoID: repo.ID,
+                               Type:   tp,
+                               Config: &repo_model.PullRequestsConfig{
+                                       AllowMerge: true, AllowRebase: true, AllowRebaseMerge: true, AllowSquash: true, AllowFastForwardOnly: true,
+                                       DefaultMergeStyle: repo_model.MergeStyle(setting.Repository.PullRequest.DefaultMergeStyle),
+                                       AllowRebaseUpdate: true,
+                               },
+                       })
+               } else if tp == unit.TypeProjects {
+                       units = append(units, repo_model.RepoUnit{
+                               RepoID: repo.ID,
+                               Type:   tp,
+                               Config: &repo_model.ProjectsConfig{ProjectsMode: repo_model.ProjectsModeAll},
+                       })
+               } else {
+                       units = append(units, repo_model.RepoUnit{
+                               RepoID: repo.ID,
+                               Type:   tp,
+                       })
+               }
+       }
+
+       if err = db.Insert(ctx, units); err != nil {
+               return err
+       }
+
+       // Remember visibility preference.
+       u.LastRepoVisibility = repo.IsPrivate
+       if err = user_model.UpdateUserCols(ctx, u, "last_repo_visibility"); err != nil {
+               return fmt.Errorf("UpdateUserCols: %w", err)
+       }
+
+       if err = user_model.IncrUserRepoNum(ctx, u.ID); err != nil {
+               return fmt.Errorf("IncrUserRepoNum: %w", err)
+       }
+       u.NumRepos++
+
+       // Give access to all members in teams with access to all repositories.
+       if u.IsOrganization() {
+               teams, err := organization.FindOrgTeams(ctx, u.ID)
+               if err != nil {
+                       return fmt.Errorf("FindOrgTeams: %w", err)
+               }
+               for _, t := range teams {
+                       if t.IncludesAllRepositories {
+                               if err := models.AddRepository(ctx, t, repo); err != nil {
+                                       return fmt.Errorf("AddRepository: %w", err)
+                               }
+                       }
+               }
+
+               if isAdmin, err := access_model.IsUserRepoAdmin(ctx, repo, doer); err != nil {
+                       return fmt.Errorf("IsUserRepoAdmin: %w", err)
+               } else if !isAdmin {
+                       // Make creator repo admin if it wasn't assigned automatically
+                       if err = AddOrUpdateCollaborator(ctx, repo, doer, perm.AccessModeAdmin); err != nil {
+                               return fmt.Errorf("AddCollaborator: %w", err)
+                       }
+               }
+       } else if err = access_model.RecalculateAccesses(ctx, repo); err != nil {
+               // Organization automatically called this in AddRepository method.
+               return fmt.Errorf("RecalculateAccesses: %w", err)
+       }
+
+       if setting.Service.AutoWatchNewRepos {
+               if err = repo_model.WatchRepo(ctx, doer, repo, true); err != nil {
+                       return fmt.Errorf("WatchRepo: %w", err)
+               }
+       }
+
+       if err = webhook.CopyDefaultWebhooksToRepo(ctx, repo.ID); err != nil {
+               return fmt.Errorf("CopyDefaultWebhooksToRepo: %w", err)
+       }
+
+       return nil
+}
index e1145556791c5668ab717293ab300f53cfdb8e44..5b24015a0384f2716c2de6377539a25af756c6a2 100644 (file)
@@ -134,7 +134,7 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork
        }()
 
        err = db.WithTx(ctx, func(txCtx context.Context) error {
-               if err = repo_module.CreateRepositoryByExample(txCtx, doer, owner, repo, false, true); err != nil {
+               if err = CreateRepositoryByExample(txCtx, doer, owner, repo, false, true); err != nil {
                        return err
                }
 
index 2b95bbcd4d315637b23c8711fb773c6b87db15de..f2280de8b214e4a79924b8dcec439629acc62063 100644 (file)
@@ -343,7 +343,7 @@ func generateRepository(ctx context.Context, doer, owner *user_model.User, templ
                ObjectFormatName: templateRepo.ObjectFormatName,
        }
 
-       if err = repo_module.CreateRepositoryByExample(ctx, doer, owner, generateRepo, false, false); err != nil {
+       if err = CreateRepositoryByExample(ctx, doer, owner, generateRepo, false, false); err != nil {
                return nil, err
        }
 
index 7ad6b46fa4ce895b0dd5b10196253d5c5f2e2a0a..301d89533781daf9cad8c0e7a5a14a7df16e6351 100644 (file)
@@ -20,7 +20,6 @@ import (
        user_model "code.gitea.io/gitea/models/user"
        "code.gitea.io/gitea/modules/globallock"
        "code.gitea.io/gitea/modules/log"
-       repo_module "code.gitea.io/gitea/modules/repository"
        "code.gitea.io/gitea/modules/util"
        notify_service "code.gitea.io/gitea/services/notify"
 )
@@ -419,10 +418,7 @@ func StartRepositoryTransfer(ctx context.Context, doer, newOwner *user_model.Use
                return err
        }
        if !hasAccess {
-               if err := repo_module.AddCollaborator(ctx, repo, newOwner); err != nil {
-                       return err
-               }
-               if err := repo_model.ChangeCollaborationAccessMode(ctx, repo, newOwner.ID, perm.AccessModeRead); err != nil {
+               if err := AddOrUpdateCollaborator(ctx, repo, newOwner, perm.AccessModeRead); err != nil {
                        return err
                }
        }
index 01b12460ac11f5ed12356b6c99ab3772fcb63411..b9fb1910a383ee4fe713b32d12f43129c29d6481 100644 (file)
         "tags": [
           "repository"
         ],
-        "summary": "Add a collaborator to a repository",
+        "summary": "Add or Update a collaborator to a repository",
         "operationId": "repoAddCollaborator",
         "parameters": [
           {