mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-04 15:04:00 +01:00 
			
		
		
		
	`RepoTransfer` now is at models, but if we want to move it into `repo` model, it will depend on `Team`. So this PR also makes repo model depend on org model to make it possible. Just refactor, no code change. - [x] Move `DeleteOrganization` from `models/organization` to service layer - [x] Move `AccessibleTeamReposEnv` to `models/repo` - [x] Move `RepoTransfer` from `models` to `models/repo` - [x] Merge `getUserTeamIDs` and `GetUserTeamIDs`, Merge `GetUserTeams` and `getUserTeams`. - [x] Remove `Team`'s `Repos []*repo_model.Repository` to avoid dependency recycle.
		
			
				
	
	
		
			234 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			234 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright 2024 The Gitea Authors. All rights reserved.
 | 
						|
// SPDX-License-Identifier: MIT
 | 
						|
 | 
						|
package repository
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
 | 
						|
	"code.gitea.io/gitea/models/db"
 | 
						|
	issues_model "code.gitea.io/gitea/models/issues"
 | 
						|
	"code.gitea.io/gitea/models/organization"
 | 
						|
	access_model "code.gitea.io/gitea/models/perm/access"
 | 
						|
	repo_model "code.gitea.io/gitea/models/repo"
 | 
						|
	"code.gitea.io/gitea/modules/setting"
 | 
						|
)
 | 
						|
 | 
						|
// TeamAddRepository adds new repository to team of organization.
 | 
						|
func TeamAddRepository(ctx context.Context, t *organization.Team, repo *repo_model.Repository) (err error) {
 | 
						|
	if repo.OwnerID != t.OrgID {
 | 
						|
		return errors.New("repository does not belong to organization")
 | 
						|
	} else if organization.HasTeamRepo(ctx, t.OrgID, t.ID, repo.ID) {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	return db.WithTx(ctx, func(ctx context.Context) error {
 | 
						|
		return addRepositoryToTeam(ctx, t, repo)
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
func addRepositoryToTeam(ctx context.Context, t *organization.Team, repo *repo_model.Repository) (err error) {
 | 
						|
	if err = organization.AddTeamRepo(ctx, t.OrgID, t.ID, repo.ID); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	if err = organization.IncrTeamRepoNum(ctx, t.ID); err != nil {
 | 
						|
		return fmt.Errorf("update team: %w", err)
 | 
						|
	}
 | 
						|
 | 
						|
	t.NumRepos++
 | 
						|
 | 
						|
	if err = access_model.RecalculateTeamAccesses(ctx, repo, 0); err != nil {
 | 
						|
		return fmt.Errorf("recalculateAccesses: %w", err)
 | 
						|
	}
 | 
						|
 | 
						|
	// Make all team members watch this repo if enabled in global settings
 | 
						|
	if setting.Service.AutoWatchNewRepos {
 | 
						|
		if err = t.LoadMembers(ctx); err != nil {
 | 
						|
			return fmt.Errorf("getMembers: %w", err)
 | 
						|
		}
 | 
						|
		for _, u := range t.Members {
 | 
						|
			if err = repo_model.WatchRepo(ctx, u, repo, true); err != nil {
 | 
						|
				return fmt.Errorf("watchRepo: %w", err)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// AddAllRepositoriesToTeam adds all repositories to the team.
 | 
						|
// If the team already has some repositories they will be left unchanged.
 | 
						|
func AddAllRepositoriesToTeam(ctx context.Context, t *organization.Team) error {
 | 
						|
	return db.WithTx(ctx, func(ctx context.Context) error {
 | 
						|
		orgRepos, err := repo_model.GetOrgRepositories(ctx, t.OrgID)
 | 
						|
		if err != nil {
 | 
						|
			return fmt.Errorf("get org repos: %w", err)
 | 
						|
		}
 | 
						|
 | 
						|
		for _, repo := range orgRepos {
 | 
						|
			if !organization.HasTeamRepo(ctx, t.OrgID, t.ID, repo.ID) {
 | 
						|
				if err := addRepositoryToTeam(ctx, t, repo); err != nil {
 | 
						|
					return fmt.Errorf("AddRepository: %w", err)
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		return nil
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
// RemoveAllRepositoriesFromTeam removes all repositories from team and recalculates access
 | 
						|
func RemoveAllRepositoriesFromTeam(ctx context.Context, t *organization.Team) (err error) {
 | 
						|
	if t.IncludesAllRepositories {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	ctx, committer, err := db.TxContext(ctx)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	defer committer.Close()
 | 
						|
 | 
						|
	if err = removeAllRepositoriesFromTeam(ctx, t); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	return committer.Commit()
 | 
						|
}
 | 
						|
 | 
						|
// removeAllRepositoriesFromTeam removes all repositories from team and recalculates access
 | 
						|
// Note: Shall not be called if team includes all repositories
 | 
						|
func removeAllRepositoriesFromTeam(ctx context.Context, t *organization.Team) (err error) {
 | 
						|
	e := db.GetEngine(ctx)
 | 
						|
	repos, err := repo_model.GetTeamRepositories(ctx, &repo_model.SearchTeamRepoOptions{
 | 
						|
		TeamID: t.ID,
 | 
						|
	})
 | 
						|
	if err != nil {
 | 
						|
		return fmt.Errorf("GetTeamRepositories: %w", err)
 | 
						|
	}
 | 
						|
 | 
						|
	// Delete all accesses.
 | 
						|
	for _, repo := range repos {
 | 
						|
		if err := access_model.RecalculateTeamAccesses(ctx, repo, t.ID); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
 | 
						|
		// Remove watches from all users and now unaccessible repos
 | 
						|
		for _, user := range t.Members {
 | 
						|
			has, err := access_model.HasAnyUnitAccess(ctx, user.ID, repo)
 | 
						|
			if err != nil {
 | 
						|
				return err
 | 
						|
			} else if has {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
 | 
						|
			if err = repo_model.WatchRepo(ctx, user, repo, false); err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
 | 
						|
			// Remove all IssueWatches a user has subscribed to in the repositories
 | 
						|
			if err = issues_model.RemoveIssueWatchersByRepoID(ctx, user.ID, repo.ID); err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// Delete team-repo
 | 
						|
	if _, err := e.
 | 
						|
		Where("team_id=?", t.ID).
 | 
						|
		Delete(new(organization.TeamRepo)); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	t.NumRepos = 0
 | 
						|
	if _, err = e.ID(t.ID).Cols("num_repos").Update(t); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// RemoveRepositoryFromTeam removes repository from team of organization.
 | 
						|
// If the team shall include all repositories the request is ignored.
 | 
						|
func RemoveRepositoryFromTeam(ctx context.Context, t *organization.Team, repoID int64) error {
 | 
						|
	if !HasRepository(ctx, t, repoID) {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	if t.IncludesAllRepositories {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	repo, err := repo_model.GetRepositoryByID(ctx, repoID)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	ctx, committer, err := db.TxContext(ctx)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	defer committer.Close()
 | 
						|
 | 
						|
	if err = removeRepositoryFromTeam(ctx, t, repo, true); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	return committer.Commit()
 | 
						|
}
 | 
						|
 | 
						|
// removeRepositoryFromTeam removes a repository from a team and recalculates access
 | 
						|
// Note: Repository shall not be removed from team if it includes all repositories (unless the repository is deleted)
 | 
						|
func removeRepositoryFromTeam(ctx context.Context, t *organization.Team, repo *repo_model.Repository, recalculate bool) (err error) {
 | 
						|
	e := db.GetEngine(ctx)
 | 
						|
	if err = organization.RemoveTeamRepo(ctx, t.ID, repo.ID); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	t.NumRepos--
 | 
						|
	if _, err = e.ID(t.ID).Cols("num_repos").Update(t); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	// Don't need to recalculate when delete a repository from organization.
 | 
						|
	if recalculate {
 | 
						|
		if err = access_model.RecalculateTeamAccesses(ctx, repo, t.ID); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	teamMembers, err := organization.GetTeamMembers(ctx, &organization.SearchMembersOptions{
 | 
						|
		TeamID: t.ID,
 | 
						|
	})
 | 
						|
	if err != nil {
 | 
						|
		return fmt.Errorf("GetTeamMembers: %w", err)
 | 
						|
	}
 | 
						|
	for _, member := range teamMembers {
 | 
						|
		has, err := access_model.HasAnyUnitAccess(ctx, member.ID, repo)
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		} else if has {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		if err = repo_model.WatchRepo(ctx, member, repo, false); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
 | 
						|
		// Remove all IssueWatches a user has subscribed to in the repositories
 | 
						|
		if err := issues_model.RemoveIssueWatchersByRepoID(ctx, member.ID, repo.ID); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// HasRepository returns true if given repository belong to team.
 | 
						|
func HasRepository(ctx context.Context, t *organization.Team, repoID int64) bool {
 | 
						|
	return organization.HasTeamRepo(ctx, t.OrgID, t.ID, repoID)
 | 
						|
}
 |