mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 21:28:11 +09:00 
			
		
		
		
	Move AddCollabrator and CreateRepositoryByExample to service layer (#32419)
- [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.
This commit is contained in:
		| @@ -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") | ||||
|   | ||||
| @@ -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) | ||||
| 	}) | ||||
| } | ||||
| @@ -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)) | ||||
| 	} | ||||
| } | ||||
| @@ -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 | ||||
|   | ||||
| @@ -8,6 +8,7 @@ import ( | ||||
|  | ||||
| 	"code.gitea.io/gitea/models/unittest" | ||||
|  | ||||
| 	_ "code.gitea.io/gitea/models" | ||||
| 	_ "code.gitea.io/gitea/models/actions" | ||||
| ) | ||||
|  | ||||
|   | ||||
| @@ -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) | ||||
| 					}) | ||||
|   | ||||
| @@ -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,20 +176,18 @@ func AddCollaborator(ctx *context.APIContext) { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	if err := repo_module.AddCollaborator(ctx, ctx.Repo.Repository, collaborator); err != nil { | ||||
| 		if errors.Is(err, user_model.ErrBlockedUser) { | ||||
| 			ctx.Error(http.StatusForbidden, "AddCollaborator", err) | ||||
| 		} else { | ||||
| 			ctx.Error(http.StatusInternalServerError, "AddCollaborator", err) | ||||
| 		} | ||||
| 		return | ||||
| 	p := perm.AccessModeWrite | ||||
| 	if form.Permission != nil { | ||||
| 		p = perm.ParseAccessMode(*form.Permission) | ||||
| 	} | ||||
|  | ||||
| 	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 | ||||
| 	if err := repo_service.AddOrUpdateCollaborator(ctx, ctx.Repo.Repository, collaborator, p); err != nil { | ||||
| 		if errors.Is(err, user_model.ErrBlockedUser) { | ||||
| 			ctx.Error(http.StatusForbidden, "AddOrUpdateCollaborator", err) | ||||
| 		} else { | ||||
| 			ctx.Error(http.StatusInternalServerError, "AddOrUpdateCollaborator", err) | ||||
| 		} | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	ctx.Status(http.StatusNoContent) | ||||
|   | ||||
| @@ -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 | ||||
| 	} | ||||
|   | ||||
| @@ -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" | ||||
|   | ||||
| @@ -8,6 +8,7 @@ import ( | ||||
|  | ||||
| 	"code.gitea.io/gitea/models/unittest" | ||||
|  | ||||
| 	_ "code.gitea.io/gitea/models" | ||||
| 	_ "code.gitea.io/gitea/models/actions" | ||||
| ) | ||||
|  | ||||
|   | ||||
| @@ -8,6 +8,7 @@ import ( | ||||
|  | ||||
| 	"code.gitea.io/gitea/models/unittest" | ||||
|  | ||||
| 	_ "code.gitea.io/gitea/models" | ||||
| 	_ "code.gitea.io/gitea/models/actions" | ||||
| ) | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
| 		} | ||||
|  | ||||
|   | ||||
| @@ -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{ | ||||
|   | ||||
| @@ -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()) | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
| 		} | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
| 		} | ||||
| 	} | ||||
|   | ||||
							
								
								
									
										2
									
								
								templates/swagger/v1_json.tmpl
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								templates/swagger/v1_json.tmpl
									
									
									
										generated
									
									
									
								
							| @@ -5095,7 +5095,7 @@ | ||||
|         "tags": [ | ||||
|           "repository" | ||||
|         ], | ||||
|         "summary": "Add a collaborator to a repository", | ||||
|         "summary": "Add or Update a collaborator to a repository", | ||||
|         "operationId": "repoAddCollaborator", | ||||
|         "parameters": [ | ||||
|           { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user