summaryrefslogtreecommitdiffstats
path: root/models
diff options
context:
space:
mode:
Diffstat (limited to 'models')
-rw-r--r--models/action.go125
-rw-r--r--models/action_test.go37
-rw-r--r--models/error.go63
-rw-r--r--models/issue_watch.go5
-rw-r--r--models/notification.go2
-rw-r--r--models/org.go2
-rw-r--r--models/org_team.go8
-rw-r--r--models/release.go86
-rw-r--r--models/repo.go303
-rw-r--r--models/repo/archiver.go7
-rw-r--r--models/repo/fork.go69
-rw-r--r--models/repo/fork_test.go32
-rw-r--r--models/repo/main_test.go6
-rw-r--r--models/repo/redirect.go82
-rw-r--r--models/repo/redirect_test.go77
-rw-r--r--models/repo/repo.go36
-rw-r--r--models/repo/repo_test.go7
-rw-r--r--models/repo/repo_unit.go26
-rw-r--r--models/repo/star.go (renamed from models/star.go)5
-rw-r--r--models/repo/star_test.go (renamed from models/star_test.go)7
-rw-r--r--models/repo/topic.go (renamed from models/topic.go)25
-rw-r--r--models/repo/topic_test.go (renamed from models/topic_test.go)2
-rw-r--r--models/repo/update.go179
-rw-r--r--models/repo/watch.go196
-rw-r--r--models/repo/watch_test.go (renamed from models/repo_watch_test.go)80
-rw-r--r--models/repo_archiver.go25
-rw-r--r--models/repo_avatar.go115
-rw-r--r--models/repo_collaboration.go11
-rw-r--r--models/repo_generate.go56
-rw-r--r--models/repo_permission.go18
-rw-r--r--models/repo_redirect.go62
-rw-r--r--models/repo_redirect_test.go78
-rw-r--r--models/repo_test.go90
-rw-r--r--models/repo_transfer.go37
-rw-r--r--models/repo_watch.go328
-rw-r--r--models/statistic.go4
-rw-r--r--models/update.go100
-rw-r--r--models/user.go8
38 files changed, 1091 insertions, 1308 deletions
diff --git a/models/action.go b/models/action.go
index 16d6c42aa5..da9e6776b1 100644
--- a/models/action.go
+++ b/models/action.go
@@ -16,6 +16,7 @@ import (
"code.gitea.io/gitea/models/db"
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/modules/base"
"code.gitea.io/gitea/modules/git"
@@ -414,3 +415,127 @@ func DeleteOldActions(olderThan time.Duration) (err error) {
_, err = db.GetEngine(db.DefaultContext).Where("created_unix < ?", time.Now().Add(-olderThan).Unix()).Delete(&Action{})
return
}
+
+func notifyWatchers(ctx context.Context, actions ...*Action) error {
+ var watchers []*repo_model.Watch
+ var repo *repo_model.Repository
+ var err error
+ var permCode []bool
+ var permIssue []bool
+ var permPR []bool
+
+ e := db.GetEngine(ctx)
+
+ for _, act := range actions {
+ repoChanged := repo == nil || repo.ID != act.RepoID
+
+ if repoChanged {
+ // Add feeds for user self and all watchers.
+ watchers, err = repo_model.GetWatchers(ctx, act.RepoID)
+ if err != nil {
+ return fmt.Errorf("get watchers: %v", err)
+ }
+ }
+
+ // Add feed for actioner.
+ act.UserID = act.ActUserID
+ if _, err = e.Insert(act); err != nil {
+ return fmt.Errorf("insert new actioner: %v", err)
+ }
+
+ if repoChanged {
+ act.loadRepo()
+ repo = act.Repo
+
+ // check repo owner exist.
+ if err := act.Repo.GetOwner(ctx); err != nil {
+ return fmt.Errorf("can't get repo owner: %v", err)
+ }
+ } else if act.Repo == nil {
+ act.Repo = repo
+ }
+
+ // Add feed for organization
+ if act.Repo.Owner.IsOrganization() && act.ActUserID != act.Repo.Owner.ID {
+ act.ID = 0
+ act.UserID = act.Repo.Owner.ID
+ if _, err = e.InsertOne(act); err != nil {
+ return fmt.Errorf("insert new actioner: %v", err)
+ }
+ }
+
+ if repoChanged {
+ permCode = make([]bool, len(watchers))
+ permIssue = make([]bool, len(watchers))
+ permPR = make([]bool, len(watchers))
+ for i, watcher := range watchers {
+ user, err := user_model.GetUserByIDEngine(e, watcher.UserID)
+ if err != nil {
+ permCode[i] = false
+ permIssue[i] = false
+ permPR[i] = false
+ continue
+ }
+ perm, err := getUserRepoPermission(ctx, repo, user)
+ if err != nil {
+ permCode[i] = false
+ permIssue[i] = false
+ permPR[i] = false
+ continue
+ }
+ permCode[i] = perm.CanRead(unit.TypeCode)
+ permIssue[i] = perm.CanRead(unit.TypeIssues)
+ permPR[i] = perm.CanRead(unit.TypePullRequests)
+ }
+ }
+
+ for i, watcher := range watchers {
+ if act.ActUserID == watcher.UserID {
+ continue
+ }
+ act.ID = 0
+ act.UserID = watcher.UserID
+ act.Repo.Units = nil
+
+ switch act.OpType {
+ case ActionCommitRepo, ActionPushTag, ActionDeleteTag, ActionPublishRelease, ActionDeleteBranch:
+ if !permCode[i] {
+ continue
+ }
+ case ActionCreateIssue, ActionCommentIssue, ActionCloseIssue, ActionReopenIssue:
+ if !permIssue[i] {
+ continue
+ }
+ case ActionCreatePullRequest, ActionCommentPull, ActionMergePullRequest, ActionClosePullRequest, ActionReopenPullRequest:
+ if !permPR[i] {
+ continue
+ }
+ }
+
+ if _, err = e.InsertOne(act); err != nil {
+ return fmt.Errorf("insert new action: %v", err)
+ }
+ }
+ }
+ return nil
+}
+
+// NotifyWatchers creates batch of actions for every watcher.
+func NotifyWatchers(actions ...*Action) error {
+ return notifyWatchers(db.DefaultContext, actions...)
+}
+
+// NotifyWatchersActions creates batch of actions for every watcher.
+func NotifyWatchersActions(acts []*Action) error {
+ ctx, committer, err := db.TxContext()
+ if err != nil {
+ return err
+ }
+ defer committer.Close()
+ for _, act := range acts {
+ if err := notifyWatchers(ctx, act); err != nil {
+ return err
+ }
+ }
+ return committer.Commit()
+}
diff --git a/models/action_test.go b/models/action_test.go
index 02edae2df7..306d382364 100644
--- a/models/action_test.go
+++ b/models/action_test.go
@@ -92,3 +92,40 @@ func TestGetFeeds2(t *testing.T) {
assert.NoError(t, err)
assert.Len(t, actions, 0)
}
+
+func TestNotifyWatchers(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
+ action := &Action{
+ ActUserID: 8,
+ RepoID: 1,
+ OpType: ActionStarRepo,
+ }
+ assert.NoError(t, NotifyWatchers(action))
+
+ // One watchers are inactive, thus action is only created for user 8, 1, 4, 11
+ unittest.AssertExistsAndLoadBean(t, &Action{
+ ActUserID: action.ActUserID,
+ UserID: 8,
+ RepoID: action.RepoID,
+ OpType: action.OpType,
+ })
+ unittest.AssertExistsAndLoadBean(t, &Action{
+ ActUserID: action.ActUserID,
+ UserID: 1,
+ RepoID: action.RepoID,
+ OpType: action.OpType,
+ })
+ unittest.AssertExistsAndLoadBean(t, &Action{
+ ActUserID: action.ActUserID,
+ UserID: 4,
+ RepoID: action.RepoID,
+ OpType: action.OpType,
+ })
+ unittest.AssertExistsAndLoadBean(t, &Action{
+ ActUserID: action.ActUserID,
+ UserID: 11,
+ RepoID: action.RepoID,
+ OpType: action.OpType,
+ })
+}
diff --git a/models/error.go b/models/error.go
index 54556fd787..f0e8751d75 100644
--- a/models/error.go
+++ b/models/error.go
@@ -71,21 +71,6 @@ func (err ErrUserNotAllowedCreateOrg) Error() string {
return "user is not allowed to create organizations"
}
-// ErrReachLimitOfRepo represents a "ReachLimitOfRepo" kind of error.
-type ErrReachLimitOfRepo struct {
- Limit int
-}
-
-// IsErrReachLimitOfRepo checks if an error is a ErrReachLimitOfRepo.
-func IsErrReachLimitOfRepo(err error) bool {
- _, ok := err.(ErrReachLimitOfRepo)
- return ok
-}
-
-func (err ErrReachLimitOfRepo) Error() string {
- return fmt.Sprintf("user has reached maximum limit of repositories [limit: %d]", err.Limit)
-}
-
// __ __.__ __ .__
// / \ / \__| | _|__|
// \ \/\/ / | |/ / |
@@ -322,38 +307,6 @@ func (err ErrRepoTransferInProgress) Error() string {
return fmt.Sprintf("repository is already being transferred [uname: %s, name: %s]", err.Uname, err.Name)
}
-// ErrRepoAlreadyExist represents a "RepoAlreadyExist" kind of error.
-type ErrRepoAlreadyExist struct {
- Uname string
- Name string
-}
-
-// IsErrRepoAlreadyExist checks if an error is a ErrRepoAlreadyExist.
-func IsErrRepoAlreadyExist(err error) bool {
- _, ok := err.(ErrRepoAlreadyExist)
- return ok
-}
-
-func (err ErrRepoAlreadyExist) Error() string {
- return fmt.Sprintf("repository already exists [uname: %s, name: %s]", err.Uname, err.Name)
-}
-
-// ErrRepoFilesAlreadyExist represents a "RepoFilesAlreadyExist" kind of error.
-type ErrRepoFilesAlreadyExist struct {
- Uname string
- Name string
-}
-
-// IsErrRepoFilesAlreadyExist checks if an error is a ErrRepoAlreadyExist.
-func IsErrRepoFilesAlreadyExist(err error) bool {
- _, ok := err.(ErrRepoFilesAlreadyExist)
- return ok
-}
-
-func (err ErrRepoFilesAlreadyExist) Error() string {
- return fmt.Sprintf("repository files already exist [uname: %s, name: %s]", err.Uname, err.Name)
-}
-
// ErrForkAlreadyExist represents a "ForkAlreadyExist" kind of error.
type ErrForkAlreadyExist struct {
Uname string
@@ -371,22 +324,6 @@ func (err ErrForkAlreadyExist) Error() string {
return fmt.Sprintf("repository is already forked by user [uname: %s, repo path: %s, fork path: %s]", err.Uname, err.RepoName, err.ForkName)
}
-// ErrRepoRedirectNotExist represents a "RepoRedirectNotExist" kind of error.
-type ErrRepoRedirectNotExist struct {
- OwnerID int64
- RepoName string
-}
-
-// IsErrRepoRedirectNotExist check if an error is an ErrRepoRedirectNotExist.
-func IsErrRepoRedirectNotExist(err error) bool {
- _, ok := err.(ErrRepoRedirectNotExist)
- return ok
-}
-
-func (err ErrRepoRedirectNotExist) Error() string {
- return fmt.Sprintf("repository redirect does not exist [uid: %d, name: %s]", err.OwnerID, err.RepoName)
-}
-
// ErrInvalidCloneAddr represents a "InvalidCloneAddr" kind of error.
type ErrInvalidCloneAddr struct {
Host string
diff --git a/models/issue_watch.go b/models/issue_watch.go
index bf5c2593a3..181cd23433 100644
--- a/models/issue_watch.go
+++ b/models/issue_watch.go
@@ -6,6 +6,7 @@ package models
import (
"code.gitea.io/gitea/models/db"
+ repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/timeutil"
)
@@ -80,11 +81,11 @@ func CheckIssueWatch(user *user_model.User, issue *Issue) (bool, error) {
if exist {
return iw.IsWatching, nil
}
- w, err := getWatch(db.GetEngine(db.DefaultContext), user.ID, issue.RepoID)
+ w, err := repo_model.GetWatch(db.DefaultContext, user.ID, issue.RepoID)
if err != nil {
return false, err
}
- return isWatchMode(w.Mode) || IsUserParticipantsOfIssue(user, issue), nil
+ return repo_model.IsWatchMode(w.Mode) || IsUserParticipantsOfIssue(user, issue), nil
}
// GetIssueWatchersIDs returns IDs of subscribers or explicit unsubscribers to a given issue id
diff --git a/models/notification.go b/models/notification.go
index b71973823a..0be0144924 100644
--- a/models/notification.go
+++ b/models/notification.go
@@ -225,7 +225,7 @@ func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, n
toNotify[id] = struct{}{}
}
if !(issue.IsPull && HasWorkInProgressPrefix(issue.Title)) {
- repoWatches, err := getRepoWatchersIDs(e, issue.RepoID)
+ repoWatches, err := repo_model.GetRepoWatchersIDs(ctx, issue.RepoID)
if err != nil {
return err
}
diff --git a/models/org.go b/models/org.go
index e5cd80ab78..c135bb9d3c 100644
--- a/models/org.go
+++ b/models/org.go
@@ -794,7 +794,7 @@ func removeOrgUser(ctx context.Context, orgID, userID int64) error {
return fmt.Errorf("GetUserRepositories [%d]: %v", userID, err)
}
for _, repoID := range repoIDs {
- if err = watchRepo(sess, userID, repoID, false); err != nil {
+ if err = repo_model.WatchRepoCtx(ctx, userID, repoID, false); err != nil {
return err
}
}
diff --git a/models/org_team.go b/models/org_team.go
index c42312323c..3d4a2882c7 100644
--- a/models/org_team.go
+++ b/models/org_team.go
@@ -238,7 +238,7 @@ func (t *Team) addRepository(ctx context.Context, repo *repo_model.Repository) (
return fmt.Errorf("getMembers: %v", err)
}
for _, u := range t.Members {
- if err = watchRepo(e, u.ID, repo.ID, true); err != nil {
+ if err = repo_model.WatchRepoCtx(ctx, u.ID, repo.ID, true); err != nil {
return fmt.Errorf("watchRepo: %v", err)
}
}
@@ -341,7 +341,7 @@ func (t *Team) removeAllRepositories(ctx context.Context) (err error) {
continue
}
- if err = watchRepo(e, user.ID, repo.ID, false); err != nil {
+ if err = repo_model.WatchRepoCtx(ctx, user.ID, repo.ID, false); err != nil {
return err
}
@@ -399,7 +399,7 @@ func (t *Team) removeRepository(ctx context.Context, repo *repo_model.Repository
continue
}
- if err = watchRepo(e, teamUser.UID, repo.ID, false); err != nil {
+ if err = repo_model.WatchRepoCtx(ctx, teamUser.UID, repo.ID, false); err != nil {
return err
}
@@ -857,7 +857,7 @@ func AddTeamMember(team *Team, userID int64) error {
return err
}
if setting.Service.AutoWatchNewRepos {
- if err = watchRepo(sess, userID, repo.ID, true); err != nil {
+ if err = repo_model.WatchRepoCtx(ctx, userID, repo.ID, true); err != nil {
return err
}
}
diff --git a/models/release.go b/models/release.go
index a19d4f937f..51ac0426ac 100644
--- a/models/release.go
+++ b/models/release.go
@@ -370,3 +370,89 @@ func UpdateReleasesMigrationsByType(gitServiceType structs.GitServiceType, origi
})
return err
}
+
+// PushUpdateDeleteTagsContext updates a number of delete tags with context
+func PushUpdateDeleteTagsContext(ctx context.Context, repo *repo_model.Repository, tags []string) error {
+ return pushUpdateDeleteTags(db.GetEngine(ctx), repo, tags)
+}
+
+func pushUpdateDeleteTags(e db.Engine, repo *repo_model.Repository, tags []string) error {
+ if len(tags) == 0 {
+ return nil
+ }
+ lowerTags := make([]string, 0, len(tags))
+ for _, tag := range tags {
+ lowerTags = append(lowerTags, strings.ToLower(tag))
+ }
+
+ if _, err := e.
+ Where("repo_id = ? AND is_tag = ?", repo.ID, true).
+ In("lower_tag_name", lowerTags).
+ Delete(new(Release)); err != nil {
+ return fmt.Errorf("Delete: %v", err)
+ }
+
+ if _, err := e.
+ Where("repo_id = ? AND is_tag = ?", repo.ID, false).
+ In("lower_tag_name", lowerTags).
+ Cols("is_draft", "num_commits", "sha1").
+ Update(&Release{
+ IsDraft: true,
+ }); err != nil {
+ return fmt.Errorf("Update: %v", err)
+ }
+
+ return nil
+}
+
+// PushUpdateDeleteTag must be called for any push actions to delete tag
+func PushUpdateDeleteTag(repo *repo_model.Repository, tagName string) error {
+ rel, err := GetRelease(repo.ID, tagName)
+ if err != nil {
+ if IsErrReleaseNotExist(err) {
+ return nil
+ }
+ return fmt.Errorf("GetRelease: %v", err)
+ }
+ if rel.IsTag {
+ if _, err = db.GetEngine(db.DefaultContext).ID(rel.ID).Delete(new(Release)); err != nil {
+ return fmt.Errorf("Delete: %v", err)
+ }
+ } else {
+ rel.IsDraft = true
+ rel.NumCommits = 0
+ rel.Sha1 = ""
+ if _, err = db.GetEngine(db.DefaultContext).ID(rel.ID).AllCols().Update(rel); err != nil {
+ return fmt.Errorf("Update: %v", err)
+ }
+ }
+
+ return nil
+}
+
+// SaveOrUpdateTag must be called for any push actions to add tag
+func SaveOrUpdateTag(repo *repo_model.Repository, newRel *Release) error {
+ rel, err := GetRelease(repo.ID, newRel.TagName)
+ if err != nil && !IsErrReleaseNotExist(err) {
+ return fmt.Errorf("GetRelease: %v", err)
+ }
+
+ if rel == nil {
+ rel = newRel
+ if _, err = db.GetEngine(db.DefaultContext).Insert(rel); err != nil {
+ return fmt.Errorf("InsertOne: %v", err)
+ }
+ } else {
+ rel.Sha1 = newRel.Sha1
+ rel.CreatedUnix = newRel.CreatedUnix
+ rel.NumCommits = newRel.NumCommits
+ rel.IsDraft = false
+ if rel.IsTag && newRel.PublisherID > 0 {
+ rel.PublisherID = newRel.PublisherID
+ }
+ if _, err = db.GetEngine(db.DefaultContext).ID(rel.ID).AllCols().Update(rel); err != nil {
+ return fmt.Errorf("Update: %v", err)
+ }
+ }
+ return nil
+}
diff --git a/models/repo.go b/models/repo.go
index adc62c9528..397b4380d6 100644
--- a/models/repo.go
+++ b/models/repo.go
@@ -14,7 +14,6 @@ import (
"sort"
"strconv"
"strings"
- "time"
"unicode/utf8"
_ "image/jpeg" // Needed for jpeg support
@@ -218,7 +217,7 @@ func getReviewers(ctx context.Context, repo *repo_model.Repository, doerID, post
"SELECT uid AS user_id FROM `org_user` WHERE org_id = ? "+
") AND id NOT IN (?, ?) ORDER BY name",
repo.ID, perm.AccessModeRead,
- repo.ID, RepoWatchModeNormal, RepoWatchModeAuto,
+ repo.ID, repo_model.WatchModeNormal, repo_model.WatchModeAuto,
repo.OwnerID,
doerID, posterID).
Find(&users); err != nil {
@@ -280,7 +279,7 @@ func CanUserForkRepo(user *user_model.User, repo *repo_model.Repository) (bool,
if user == nil {
return false, nil
}
- if repo.OwnerID != user.ID && !HasForkedRepo(user.ID, repo.ID) {
+ if repo.OwnerID != user.ID && !repo_model.HasForkedRepo(user.ID, repo.ID) {
return true, nil
}
ownedOrgs, err := GetOrgsCanCreateRepoByUserID(user.ID)
@@ -288,7 +287,7 @@ func CanUserForkRepo(user *user_model.User, repo *repo_model.Repository) (bool,
return false, err
}
for _, org := range ownedOrgs {
- if repo.OwnerID != org.ID && !HasForkedRepo(org.ID, repo.ID) {
+ if repo.OwnerID != org.ID && !repo_model.HasForkedRepo(org.ID, repo.ID) {
return true, nil
}
}
@@ -317,24 +316,6 @@ func CanUserDelete(repo *repo_model.Repository, user *user_model.User) (bool, er
return false, nil
}
-// GetRepoReaders returns all users that have explicit read access or higher to the repository.
-func GetRepoReaders(repo *repo_model.Repository) (_ []*user_model.User, err error) {
- return getUsersWithAccessMode(db.DefaultContext, repo, perm.AccessModeRead)
-}
-
-// GetRepoWriters returns all users that have write access to the repository.
-func GetRepoWriters(repo *repo_model.Repository) (_ []*user_model.User, err error) {
- return getUsersWithAccessMode(db.DefaultContext, repo, perm.AccessModeWrite)
-}
-
-// IsRepoReader returns true if user has explicit read access or higher to the repository.
-func IsRepoReader(repo *repo_model.Repository, userID int64) (bool, error) {
- if repo.OwnerID == userID {
- return true, nil
- }
- return db.GetEngine(db.DefaultContext).Where("repo_id = ? AND user_id = ? AND mode >= ?", repo.ID, userID, perm.AccessModeRead).Get(&Access{})
-}
-
// getUsersWithAccessMode returns users that have at least given access mode to the repository.
func getUsersWithAccessMode(ctx context.Context, repo *repo_model.Repository, mode perm.AccessMode) (_ []*user_model.User, err error) {
if err = repo.GetOwner(ctx); err != nil {
@@ -372,35 +353,6 @@ func SetRepoReadBy(repoID, userID int64) error {
return setRepoNotificationStatusReadIfUnread(db.GetEngine(db.DefaultContext), userID, repoID)
}
-// CheckCreateRepository check if could created a repository
-func CheckCreateRepository(doer, u *user_model.User, name string, overwriteOrAdopt bool) error {
- if !doer.CanCreateRepo() {
- return ErrReachLimitOfRepo{u.MaxRepoCreation}
- }
-
- if err := IsUsableRepoName(name); err != nil {
- return err
- }
-
- has, err := repo_model.IsRepositoryExist(u, name)
- if err != nil {
- return fmt.Errorf("IsRepositoryExist: %v", err)
- } else if has {
- return ErrRepoAlreadyExist{u.Name, name}
- }
-
- repoPath := repo_model.RepoPath(u.Name, 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 {
- return ErrRepoFilesAlreadyExist{u.Name, name}
- }
- return nil
-}
-
// CreateRepoOptions contains the create repository options
type CreateRepoOptions struct {
Name string
@@ -421,13 +373,6 @@ type CreateRepoOptions struct {
MirrorInterval string
}
-// ForkRepoOptions contains the fork repository options
-type ForkRepoOptions struct {
- BaseRepo *repo_model.Repository
- Name string
- Description string
-}
-
// GetRepoInitFile returns repository init files
func GetRepoInitFile(tp, name string) ([]byte, error) {
cleanedName := strings.TrimLeft(path.Clean("/"+name), "/")
@@ -457,23 +402,9 @@ func GetRepoInitFile(tp, name string) ([]byte, error) {
}
}
-var (
- reservedRepoNames = []string{".", ".."}
- reservedRepoPatterns = []string{"*.git", "*.wiki", "*.rss", "*.atom"}
-)
-
-// IsUsableRepoName returns true when repository is usable
-func IsUsableRepoName(name string) error {
- if db.AlphaDashDotPattern.MatchString(name) {
- // Note: usually this error is normally caught up earlier in the UI
- return db.ErrNameCharsNotAllowed{Name: name}
- }
- return db.IsUsableName(reservedRepoNames, reservedRepoPatterns, name)
-}
-
// CreateRepository creates a repository for the user/organization.
func CreateRepository(ctx context.Context, doer, u *user_model.User, repo *repo_model.Repository, overwriteOrAdopt bool) (err error) {
- if err = IsUsableRepoName(repo.Name); err != nil {
+ if err = repo_model.IsUsableRepoName(repo.Name); err != nil {
return err
}
@@ -481,7 +412,10 @@ func CreateRepository(ctx context.Context, doer, u *user_model.User, repo *repo_
if err != nil {
return fmt.Errorf("IsRepositoryExist: %v", err)
} else if has {
- return ErrRepoAlreadyExist{u.Name, repo.Name}
+ return repo_model.ErrRepoAlreadyExist{
+ Uname: u.Name,
+ Name: repo.Name,
+ }
}
repoPath := repo_model.RepoPath(u.Name, repo.Name)
@@ -492,7 +426,7 @@ func CreateRepository(ctx context.Context, doer, u *user_model.User, repo *repo_
}
if !overwriteOrAdopt && isExist {
log.Error("Files already exist in %s and we are not going to adopt or delete.", repoPath)
- return ErrRepoFilesAlreadyExist{
+ return repo_model.ErrRepoFilesAlreadyExist{
Uname: u.Name,
Name: repo.Name,
}
@@ -501,7 +435,7 @@ func CreateRepository(ctx context.Context, doer, u *user_model.User, repo *repo_
if err = db.Insert(ctx, repo); err != nil {
return err
}
- if err = deleteRepoRedirect(db.GetEngine(ctx), u.ID, repo.Name); err != nil {
+ if err = repo_model.DeleteRedirect(ctx, u.ID, repo.Name); err != nil {
return err
}
@@ -578,7 +512,7 @@ func CreateRepository(ctx context.Context, doer, u *user_model.User, repo *repo_
}
if setting.Service.AutoWatchNewRepos {
- if err = watchRepo(db.GetEngine(ctx), doer.ID, repo.ID, true); err != nil {
+ if err = repo_model.WatchRepoCtx(ctx, doer.ID, repo.ID, true); err != nil {
return fmt.Errorf("watchRepo: %v", err)
}
}
@@ -633,67 +567,6 @@ func DecrementRepoForkNum(ctx context.Context, repoID int64) error {
return err
}
-// ChangeRepositoryName changes all corresponding setting from old repository name to new one.
-func ChangeRepositoryName(doer *user_model.User, repo *repo_model.Repository, newRepoName string) (err error) {
- oldRepoName := repo.Name
- newRepoName = strings.ToLower(newRepoName)
- if err = IsUsableRepoName(newRepoName); err != nil {
- return err
- }
-
- if err := repo.GetOwner(db.DefaultContext); err != nil {
- return err
- }
-
- has, err := repo_model.IsRepositoryExist(repo.Owner, newRepoName)
- if err != nil {
- return fmt.Errorf("IsRepositoryExist: %v", err)
- } else if has {
- return ErrRepoAlreadyExist{repo.Owner.Name, newRepoName}
- }
-
- newRepoPath := repo_model.RepoPath(repo.Owner.Name, newRepoName)
- if err = util.Rename(repo.RepoPath(), newRepoPath); err != nil {
- return fmt.Errorf("rename repository directory: %v", err)
- }
-
- wikiPath := repo.WikiPath()
- isExist, err := util.IsExist(wikiPath)
- if err != nil {
- log.Error("Unable to check if %s exists. Error: %v", wikiPath, err)
- return err
- }
- if isExist {
- if err = util.Rename(wikiPath, repo_model.WikiPath(repo.Owner.Name, newRepoName)); err != nil {
- return fmt.Errorf("rename repository wiki: %v", err)
- }
- }
-
- ctx, committer, err := db.TxContext()
- if err != nil {
- return err
- }
- defer committer.Close()
-
- if err := newRepoRedirect(db.GetEngine(ctx), repo.Owner.ID, repo.ID, oldRepoName, newRepoName); err != nil {
- return err
- }
-
- return committer.Commit()
-}
-
-func getRepositoriesByForkID(e db.Engine, forkID int64) ([]*repo_model.Repository, error) {
- repos := make([]*repo_model.Repository, 0, 10)
- return repos, e.
- Where("fork_id=?", forkID).
- Find(&repos)
-}
-
-// GetRepositoriesByForkID returns all repositories with given fork ID.
-func GetRepositoriesByForkID(forkID int64) ([]*repo_model.Repository, error) {
- return getRepositoriesByForkID(db.GetEngine(db.DefaultContext), forkID)
-}
-
func updateRepository(ctx context.Context, repo *repo_model.Repository, visibilityChanged bool) (err error) {
repo.LowerName = strings.ToLower(repo.Name)
@@ -740,7 +613,7 @@ func updateRepository(ctx context.Context, repo *repo_model.Repository, visibili
return err
}
- forkRepos, err := getRepositoriesByForkID(e, repo.ID)
+ forkRepos, err := repo_model.GetRepositoriesByForkID(ctx, repo.ID)
if err != nil {
return fmt.Errorf("getRepositoriesByForkID: %v", err)
}
@@ -775,58 +648,6 @@ func UpdateRepository(repo *repo_model.Repository, visibilityChanged bool) (err
return committer.Commit()
}
-// UpdateRepositoryOwnerNames updates repository owner_names (this should only be used when the ownerName has changed case)
-func UpdateRepositoryOwnerNames(ownerID int64, ownerName string) error {
- if ownerID == 0 {
- return nil
- }
- ctx, committer, err := db.TxContext()
- if err != nil {
- return err
- }
- defer committer.Close()
-
- if _, err := db.GetEngine(ctx).Where("owner_id = ?", ownerID).Cols("owner_name").Update(&repo_model.Repository{
- OwnerName: ownerName,
- }); err != nil {
- return err
- }
-
- return committer.Commit()
-}
-
-// UpdateRepositoryUpdatedTime updates a repository's updated time
-func UpdateRepositoryUpdatedTime(repoID int64, updateTime time.Time) error {
- _, err := db.GetEngine(db.DefaultContext).Exec("UPDATE repository SET updated_unix = ? WHERE id = ?", updateTime.Unix(), repoID)
- return err
-}
-
-// UpdateRepositoryUnits updates a repository's units
-func UpdateRepositoryUnits(repo *repo_model.Repository, units []repo_model.RepoUnit, deleteUnitTypes []unit.Type) (err error) {
- ctx, committer, err := db.TxContext()
- if err != nil {
- return err
- }
- defer committer.Close()
-
- // Delete existing settings of units before adding again
- for _, u := range units {
- deleteUnitTypes = append(deleteUnitTypes, u.Type)
- }
-
- if _, err = db.GetEngine(ctx).Where("repo_id = ?", repo.ID).In("type", deleteUnitTypes).Delete(new(repo_model.RepoUnit)); err != nil {
- return err
- }
-
- if len(units) > 0 {
- if err = db.Insert(ctx, units); err != nil {
- return err
- }
- }
-
- return committer.Commit()
-}
-
// DeleteRepository deletes a repository for a user or organization.
// make sure if you call this func to close open sessions (sqlite will otherwise get a deadlock)
func DeleteRepository(doer *user_model.User, uid, repoID int64) error {
@@ -927,11 +748,11 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error {
&repo_model.PushMirror{RepoID: repoID},
&Release{RepoID: repoID},
&repo_model.RepoIndexerStatus{RepoID: repoID},
- &RepoRedirect{RedirectRepoID: repoID},
+ &repo_model.Redirect{RedirectRepoID: repoID},
&repo_model.RepoUnit{RepoID: repoID},
- &Star{RepoID: repoID},
+ &repo_model.Star{RepoID: repoID},
&Task{RepoID: repoID},
- &Watch{RepoID: repoID},
+ &repo_model.Watch{RepoID: repoID},
&webhook.Webhook{RepoID: repoID},
); err != nil {
return fmt.Errorf("deleteBeans: %v", err)
@@ -964,7 +785,7 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error {
}
if len(repo.Topics) > 0 {
- if err := removeTopicsFromRepo(sess, repo.ID); err != nil {
+ if err := repo_model.RemoveTopicsFromRepo(ctx, repo.ID); err != nil {
return err
}
}
@@ -1261,76 +1082,6 @@ func CheckRepoStats(ctx context.Context) error {
return nil
}
-// SetArchiveRepoState sets if a repo is archived
-func SetArchiveRepoState(repo *repo_model.Repository, isArchived bool) (err error) {
- repo.IsArchived = isArchived
- _, err = db.GetEngine(db.DefaultContext).Where("id = ?", repo.ID).Cols("is_archived").NoAutoTime().Update(repo)
- return
-}
-
-// ___________ __
-// \_ _____/__________| | __
-// | __)/ _ \_ __ \ |/ /
-// | \( <_> ) | \/ <
-// \___ / \____/|__| |__|_ \
-// \/ \/
-
-// GetForkedRepo checks if given user has already forked a repository with given ID.
-func GetForkedRepo(ownerID, repoID int64) *repo_model.Repository {
- repo := new(repo_model.Repository)
- has, _ := db.GetEngine(db.DefaultContext).
- Where("owner_id=? AND fork_id=?", ownerID, repoID).
- Get(repo)
- if has {
- return repo
- }
- return nil
-}
-
-// HasForkedRepo checks if given user has already forked a repository with given ID.
-func HasForkedRepo(ownerID, repoID int64) bool {
- has, _ := db.GetEngine(db.DefaultContext).
- Table("repository").
- Where("owner_id=? AND fork_id=?", ownerID, repoID).
- Exist()
- return has
-}
-
-// GetForks returns all the forks of the repository
-func GetForks(repo *repo_model.Repository, listOptions db.ListOptions) ([]*repo_model.Repository, error) {
- if listOptions.Page == 0 {
- forks := make([]*repo_model.Repository, 0, repo.NumForks)
- return forks, db.GetEngine(db.DefaultContext).Find(&forks, &repo_model.Repository{ForkID: repo.ID})
- }
-
- sess := db.GetPaginatedSession(&listOptions)
- forks := make([]*repo_model.Repository, 0, listOptions.PageSize)
- return forks, sess.Find(&forks, &repo_model.Repository{ForkID: repo.ID})
-}
-
-// GetUserFork return user forked repository from this repository, if not forked return nil
-func GetUserFork(repoID, userID int64) (*repo_model.Repository, error) {
- var forkedRepo repo_model.Repository
- has, err := db.GetEngine(db.DefaultContext).Where("fork_id = ?", repoID).And("owner_id = ?", userID).Get(&forkedRepo)
- if err != nil {
- return nil, err
- }
- if !has {
- return nil, nil
- }
- return &forkedRepo, nil
-}
-
-func updateRepositoryCols(e db.Engine, repo *repo_model.Repository, cols ...string) error {
- _, err := e.ID(repo.ID).Cols(cols...).Update(repo)
- return err
-}
-
-// UpdateRepositoryCols updates repository's columns
-func UpdateRepositoryCols(repo *repo_model.Repository, cols ...string) error {
- return updateRepositoryCols(db.GetEngine(db.DefaultContext), repo, cols...)
-}
-
func updateUserStarNumbers(users []user_model.User) error {
ctx, committer, err := db.TxContext()
if err != nil {
@@ -1370,28 +1121,6 @@ func DoctorUserStarNum() (err error) {
return
}
-// IterateRepository iterate repositories
-func IterateRepository(f func(repo *repo_model.Repository) error) error {
- var start int
- batchSize := setting.Database.IterateBufferSize
- for {
- repos := make([]*repo_model.Repository, 0, batchSize)
- if err := db.GetEngine(db.DefaultContext).Limit(batchSize, start).Find(&repos); err != nil {
- return err
- }
- if len(repos) == 0 {
- return nil
- }
- start += len(repos)
-
- for _, repo := range repos {
- if err := f(repo); err != nil {
- return err
- }
- }
- }
-}
-
// LinkedRepository returns the linked repo if any
func LinkedRepository(a *repo_model.Attachment) (*repo_model.Repository, unit.Type, error) {
if a.IssueID != 0 {
diff --git a/models/repo/archiver.go b/models/repo/archiver.go
index cee6013ca3..c29891397f 100644
--- a/models/repo/archiver.go
+++ b/models/repo/archiver.go
@@ -107,3 +107,10 @@ func FindRepoArchives(opts FindRepoArchiversOption) ([]*RepoArchiver, error) {
Find(&archivers)
return archivers, err
}
+
+// SetArchiveRepoState sets if a repo is archived
+func SetArchiveRepoState(repo *Repository, isArchived bool) (err error) {
+ repo.IsArchived = isArchived
+ _, err = db.GetEngine(db.DefaultContext).Where("id = ?", repo.ID).Cols("is_archived").NoAutoTime().Update(repo)
+ return
+}
diff --git a/models/repo/fork.go b/models/repo/fork.go
new file mode 100644
index 0000000000..570a5b68ab
--- /dev/null
+++ b/models/repo/fork.go
@@ -0,0 +1,69 @@
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package repo
+
+import (
+ "context"
+
+ "code.gitea.io/gitea/models/db"
+)
+
+func getRepositoriesByForkID(e db.Engine, forkID int64) ([]*Repository, error) {
+ repos := make([]*Repository, 0, 10)
+ return repos, e.
+ Where("fork_id=?", forkID).
+ Find(&repos)
+}
+
+// GetRepositoriesByForkID returns all repositories with given fork ID.
+func GetRepositoriesByForkID(ctx context.Context, forkID int64) ([]*Repository, error) {
+ return getRepositoriesByForkID(db.GetEngine(ctx), forkID)
+}
+
+// GetForkedRepo checks if given user has already forked a repository with given ID.
+func GetForkedRepo(ownerID, repoID int64) *Repository {
+ repo := new(Repository)
+ has, _ := db.GetEngine(db.DefaultContext).
+ Where("owner_id=? AND fork_id=?", ownerID, repoID).
+ Get(repo)
+ if has {
+ return repo
+ }
+ return nil
+}
+
+// HasForkedRepo checks if given user has already forked a repository with given ID.
+func HasForkedRepo(ownerID, repoID int64) bool {
+ has, _ := db.GetEngine(db.DefaultContext).
+ Table("repository").
+ Where("owner_id=? AND fork_id=?", ownerID, repoID).
+ Exist()
+ return has
+}
+
+// GetUserFork return user forked repository from this repository, if not forked return nil
+func GetUserFork(repoID, userID int64) (*Repository, error) {
+ var forkedRepo Repository
+ has, err := db.GetEngine(db.DefaultContext).Where("fork_id = ?", repoID).And("owner_id = ?", userID).Get(&forkedRepo)
+ if err != nil {
+ return nil, err
+ }
+ if !has {
+ return nil, nil
+ }
+ return &forkedRepo, nil
+}
+
+// GetForks returns all the forks of the repository
+func GetForks(repo *Repository, listOptions db.ListOptions) ([]*Repository, error) {
+ if listOptions.Page == 0 {
+ forks := make([]*Repository, 0, repo.NumForks)
+ return forks, db.GetEngine(db.DefaultContext).Find(&forks, &Repository{ForkID: repo.ID})
+ }
+
+ sess := db.GetPaginatedSession(&listOptions)
+ forks := make([]*Repository, 0, listOptions.PageSize)
+ return forks, sess.Find(&forks, &Repository{ForkID: repo.ID})
+}
diff --git a/models/repo/fork_test.go b/models/repo/fork_test.go
new file mode 100644
index 0000000000..bf6b90b388
--- /dev/null
+++ b/models/repo/fork_test.go
@@ -0,0 +1,32 @@
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package repo
+
+import (
+ "testing"
+
+ "code.gitea.io/gitea/models/unittest"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestGetUserFork(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
+ // User13 has repo 11 forked from repo10
+ repo, err := GetRepositoryByID(10)
+ assert.NoError(t, err)
+ assert.NotNil(t, repo)
+ repo, err = GetUserFork(repo.ID, 13)
+ assert.NoError(t, err)
+ assert.NotNil(t, repo)
+
+ repo, err = GetRepositoryByID(9)
+ assert.NoError(t, err)
+ assert.NotNil(t, repo)
+ repo, err = GetUserFork(repo.ID, 13)
+ assert.NoError(t, err)
+ assert.Nil(t, repo)
+}
diff --git a/models/repo/main_test.go b/models/repo/main_test.go
index f40a976281..fdd6c3f4d3 100644
--- a/models/repo/main_test.go
+++ b/models/repo/main_test.go
@@ -18,5 +18,11 @@ func TestMain(m *testing.M) {
"repository.yml",
"repo_unit.yml",
"repo_indexer_status.yml",
+ "repo_redirect.yml",
+ "watch.yml",
+ "star.yml",
+ "topic.yml",
+ "repo_topic.yml",
+ "user.yml",
)
}
diff --git a/models/repo/redirect.go b/models/repo/redirect.go
new file mode 100644
index 0000000000..88fad6f3e3
--- /dev/null
+++ b/models/repo/redirect.go
@@ -0,0 +1,82 @@
+// Copyright 2017 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package repo
+
+import (
+ "context"
+ "fmt"
+ "strings"
+
+ "code.gitea.io/gitea/models/db"
+)
+
+// ErrRedirectNotExist represents a "RedirectNotExist" kind of error.
+type ErrRedirectNotExist struct {
+ OwnerID int64
+ RepoName string
+}
+
+// IsErrRedirectNotExist check if an error is an ErrRepoRedirectNotExist.
+func IsErrRedirectNotExist(err error) bool {
+ _, ok := err.(ErrRedirectNotExist)
+ return ok
+}
+
+func (err ErrRedirectNotExist) Error() string {
+ return fmt.Sprintf("repository redirect does not exist [uid: %d, name: %s]", err.OwnerID, err.RepoName)
+}
+
+// Redirect represents that a repo name should be redirected to another
+type Redirect struct {
+ ID int64 `xorm:"pk autoincr"`
+ OwnerID int64 `xorm:"UNIQUE(s)"`
+ LowerName string `xorm:"UNIQUE(s) INDEX NOT NULL"`
+ RedirectRepoID int64 // repoID to redirect to
+}
+
+// TableName represents real table name in database
+func (Redirect) TableName() string {
+ return "repo_redirect"
+}
+
+func init() {
+ db.RegisterModel(new(Redirect))
+}
+
+// LookupRedirect look up if a repository has a redirect name
+func LookupRedirect(ownerID int64, repoName string) (int64, error) {
+ repoName = strings.ToLower(repoName)
+ redirect := &Redirect{OwnerID: ownerID, LowerName: repoName}
+ if has, err := db.GetEngine(db.DefaultContext).Get(redirect); err != nil {
+ return 0, err
+ } else if !has {
+ return 0, ErrRedirectNotExist{OwnerID: ownerID, RepoName: repoName}
+ }
+ return redirect.RedirectRepoID, nil
+}
+
+// NewRedirect create a new repo redirect
+func NewRedirect(ctx context.Context, ownerID, repoID int64, oldRepoName, newRepoName string) error {
+ oldRepoName = strings.ToLower(oldRepoName)
+ newRepoName = strings.ToLower(newRepoName)
+
+ if err := DeleteRedirect(ctx, ownerID, newRepoName); err != nil {
+ return err
+ }
+
+ return db.Insert(ctx, &Redirect{
+ OwnerID: ownerID,
+ LowerName: oldRepoName,
+ RedirectRepoID: repoID,
+ })
+}
+
+// DeleteRedirect delete any redirect from the specified repo name to
+// anything else
+func DeleteRedirect(ctx context.Context, ownerID int64, repoName string) error {
+ repoName = strings.ToLower(repoName)
+ _, err := db.GetEngine(ctx).Delete(&Redirect{OwnerID: ownerID, LowerName: repoName})
+ return err
+}
diff --git a/models/repo/redirect_test.go b/models/repo/redirect_test.go
new file mode 100644
index 0000000000..2dca2cbbfd
--- /dev/null
+++ b/models/repo/redirect_test.go
@@ -0,0 +1,77 @@
+// Copyright 2017 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package repo
+
+import (
+ "testing"
+
+ "code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/models/unittest"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestLookupRedirect(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
+ repoID, err := LookupRedirect(2, "oldrepo1")
+ assert.NoError(t, err)
+ assert.EqualValues(t, 1, repoID)
+
+ _, err = LookupRedirect(unittest.NonexistentID, "doesnotexist")
+ assert.True(t, IsErrRedirectNotExist(err))
+}
+
+func TestNewRedirect(t *testing.T) {
+ // redirect to a completely new name
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
+ repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
+ assert.NoError(t, NewRedirect(db.DefaultContext, repo.OwnerID, repo.ID, repo.Name, "newreponame"))
+
+ unittest.AssertExistsAndLoadBean(t, &Redirect{
+ OwnerID: repo.OwnerID,
+ LowerName: repo.LowerName,
+ RedirectRepoID: repo.ID,
+ })
+ unittest.AssertExistsAndLoadBean(t, &Redirect{
+ OwnerID: repo.OwnerID,
+ LowerName: "oldrepo1",
+ RedirectRepoID: repo.ID,
+ })
+}
+
+func TestNewRedirect2(t *testing.T) {
+ // redirect to previously used name
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
+ repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
+ assert.NoError(t, NewRedirect(db.DefaultContext, repo.OwnerID, repo.ID, repo.Name, "oldrepo1"))
+
+ unittest.AssertExistsAndLoadBean(t, &Redirect{
+ OwnerID: repo.OwnerID,
+ LowerName: repo.LowerName,
+ RedirectRepoID: repo.ID,
+ })
+ unittest.AssertNotExistsBean(t, &Redirect{
+ OwnerID: repo.OwnerID,
+ LowerName: "oldrepo1",
+ RedirectRepoID: repo.ID,
+ })
+}
+
+func TestNewRedirect3(t *testing.T) {
+ // redirect for a previously-unredirected repo
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
+ repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 2}).(*Repository)
+ assert.NoError(t, NewRedirect(db.DefaultContext, repo.OwnerID, repo.ID, repo.Name, "newreponame"))
+
+ unittest.AssertExistsAndLoadBean(t, &Redirect{
+ OwnerID: repo.OwnerID,
+ LowerName: repo.LowerName,
+ RedirectRepoID: repo.ID,
+ })
+}
diff --git a/models/repo/repo.go b/models/repo/repo.go
index 9353e813bc..8907691dde 100644
--- a/models/repo/repo.go
+++ b/models/repo/repo.go
@@ -25,6 +25,20 @@ import (
"code.gitea.io/gitea/modules/util"
)
+var (
+ reservedRepoNames = []string{".", ".."}
+ reservedRepoPatterns = []string{"*.git", "*.wiki", "*.rss", "*.atom"}
+)
+
+// IsUsableRepoName returns true when repository is usable
+func IsUsableRepoName(name string) error {
+ if db.AlphaDashDotPattern.MatchString(name) {
+ // Note: usually this error is normally caught up earlier in the UI
+ return db.ErrNameCharsNotAllowed{Name: name}
+ }
+ return db.IsUsableName(reservedRepoNames, reservedRepoPatterns, name)
+}
+
// TrustModelType defines the types of trust model for this repository
type TrustModelType int
@@ -734,3 +748,25 @@ func GetPublicRepositoryCount(u *user_model.User) (int64, error) {
func GetPrivateRepositoryCount(u *user_model.User) (int64, error) {
return getPrivateRepositoryCount(db.GetEngine(db.DefaultContext), u)
}
+
+// IterateRepository iterate repositories
+func IterateRepository(f func(repo *Repository) error) error {
+ var start int
+ batchSize := setting.Database.IterateBufferSize
+ for {
+ repos := make([]*Repository, 0, batchSize)
+ if err := db.GetEngine(db.DefaultContext).Limit(batchSize, start).Find(&repos); err != nil {
+ return err
+ }
+ if len(repos) == 0 {
+ return nil
+ }
+ start += len(repos)
+
+ for _, repo := range repos {
+ if err := f(repo); err != nil {
+ return err
+ }
+ }
+ }
+}
diff --git a/models/repo/repo_test.go b/models/repo/repo_test.go
index 6f48a22e49..92b95f1d41 100644
--- a/models/repo/repo_test.go
+++ b/models/repo/repo_test.go
@@ -42,3 +42,10 @@ func TestGetPrivateRepositoryCount(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, int64(2), count)
}
+
+func TestRepoAPIURL(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+ repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 10}).(*Repository)
+
+ assert.Equal(t, "https://try.gitea.io/api/v1/repos/user12/repo10", repo.APIURL())
+}
diff --git a/models/repo/repo_unit.go b/models/repo/repo_unit.go
index 5f6c43f02f..1957f88ff3 100644
--- a/models/repo/repo_unit.go
+++ b/models/repo/repo_unit.go
@@ -242,3 +242,29 @@ func UpdateRepoUnit(unit *RepoUnit) error {
_, err := db.GetEngine(db.DefaultContext).ID(unit.ID).Update(unit)
return err
}
+
+// UpdateRepositoryUnits updates a repository's units
+func UpdateRepositoryUnits(repo *Repository, units []RepoUnit, deleteUnitTypes []unit.Type) (err error) {
+ ctx, committer, err := db.TxContext()
+ if err != nil {
+ return err
+ }
+ defer committer.Close()
+
+ // Delete existing settings of units before adding again
+ for _, u := range units {
+ deleteUnitTypes = append(deleteUnitTypes, u.Type)
+ }
+
+ if _, err = db.GetEngine(ctx).Where("repo_id = ?", repo.ID).In("type", deleteUnitTypes).Delete(new(RepoUnit)); err != nil {
+ return err
+ }
+
+ if len(units) > 0 {
+ if err = db.Insert(ctx, units); err != nil {
+ return err
+ }
+ }
+
+ return committer.Commit()
+}
diff --git a/models/star.go b/models/repo/star.go
index de3207797e..8db297e3b4 100644
--- a/models/star.go
+++ b/models/repo/star.go
@@ -2,11 +2,10 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package models
+package repo
import (
"code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/timeutil"
)
@@ -76,7 +75,7 @@ func isStaring(e db.Engine, userID, repoID int64) bool {
}
// GetStargazers returns the users that starred the repo.
-func GetStargazers(repo *repo_model.Repository, opts db.ListOptions) ([]*user_model.User, error) {
+func GetStargazers(repo *Repository, opts db.ListOptions) ([]*user_model.User, error) {
sess := db.GetEngine(db.DefaultContext).Where("star.repo_id = ?", repo.ID).
Join("LEFT", "star", "`user`.id = star.uid")
if opts.Page > 0 {
diff --git a/models/star_test.go b/models/repo/star_test.go
index 8da83661c9..20c4b6bef4 100644
--- a/models/star_test.go
+++ b/models/repo/star_test.go
@@ -2,13 +2,12 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package models
+package repo
import (
"testing"
"code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
"github.com/stretchr/testify/assert"
@@ -36,7 +35,7 @@ func TestIsStaring(t *testing.T) {
func TestRepository_GetStargazers(t *testing.T) {
// repo with stargazers
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 4}).(*Repository)
gazers, err := GetStargazers(repo, db.ListOptions{Page: 0})
assert.NoError(t, err)
if assert.Len(t, gazers, 1) {
@@ -47,7 +46,7 @@ func TestRepository_GetStargazers(t *testing.T) {
func TestRepository_GetStargazers2(t *testing.T) {
// repo with stargazers
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 3}).(*Repository)
gazers, err := GetStargazers(repo, db.ListOptions{Page: 0})
assert.NoError(t, err)
assert.Len(t, gazers, 0)
diff --git a/models/topic.go b/models/repo/topic.go
index 2767d6c58b..121863519b 100644
--- a/models/topic.go
+++ b/models/repo/topic.go
@@ -2,15 +2,15 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package models
+package repo
import (
+ "context"
"fmt"
"regexp"
"strings"
"code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/timeutil"
"xorm.io/builder"
@@ -33,7 +33,7 @@ type Topic struct {
}
// RepoTopic represents associated repositories and topics
-type RepoTopic struct {
+type RepoTopic struct { //revive:disable-line:exported
RepoID int64 `xorm:"pk"`
TopicID int64 `xorm:"pk"`
}
@@ -145,8 +145,9 @@ func removeTopicFromRepo(e db.Engine, repoID int64, topic *Topic) error {
return nil
}
-// removeTopicsFromRepo remove all topics from the repo and decrements respective topics repo count
-func removeTopicsFromRepo(e db.Engine, repoID int64) error {
+// RemoveTopicsFromRepo remove all topics from the repo and decrements respective topics repo count
+func RemoveTopicsFromRepo(ctx context.Context, repoID int64) error {
+ e := db.GetEngine(ctx)
_, err := e.Where(
builder.In("id",
builder.Select("topic_id").From("repo_topic").Where(builder.Eq{"repo_id": repoID}),
@@ -254,7 +255,7 @@ func AddTopic(repoID int64, topicName string) (*Topic, error) {
return nil, err
}
- if _, err := sess.ID(repoID).Cols("topics").Update(&repo_model.Repository{
+ if _, err := sess.ID(repoID).Cols("topics").Update(&Repository{
Topics: topicNames,
}); err != nil {
return nil, err
@@ -348,7 +349,7 @@ func SaveTopics(repoID int64, topicNames ...string) error {
return err
}
- if _, err := sess.ID(repoID).Cols("topics").Update(&repo_model.Repository{
+ if _, err := sess.ID(repoID).Cols("topics").Update(&Repository{
Topics: topicNames,
}); err != nil {
return err
@@ -356,3 +357,13 @@ func SaveTopics(repoID int64, topicNames ...string) error {
return committer.Commit()
}
+
+// GenerateTopics generates topics from a template repository
+func GenerateTopics(ctx context.Context, templateRepo, generateRepo *Repository) error {
+ for _, topic := range templateRepo.Topics {
+ if _, err := addTopicByNameToRepo(db.GetEngine(ctx), generateRepo.ID, topic); err != nil {
+ return err
+ }
+ }
+ return nil
+}
diff --git a/models/topic_test.go b/models/repo/topic_test.go
index 0219bdded5..353d96ef3e 100644
--- a/models/topic_test.go
+++ b/models/repo/topic_test.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package models
+package repo
import (
"testing"
diff --git a/models/repo/update.go b/models/repo/update.go
new file mode 100644
index 0000000000..efc562a405
--- /dev/null
+++ b/models/repo/update.go
@@ -0,0 +1,179 @@
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package repo
+
+import (
+ "context"
+ "fmt"
+ "strings"
+ "time"
+
+ "code.gitea.io/gitea/models/db"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/util"
+)
+
+// UpdateRepositoryOwnerNames updates repository owner_names (this should only be used when the ownerName has changed case)
+func UpdateRepositoryOwnerNames(ownerID int64, ownerName string) error {
+ if ownerID == 0 {
+ return nil
+ }
+ ctx, committer, err := db.TxContext()
+ if err != nil {
+ return err
+ }
+ defer committer.Close()
+
+ if _, err := db.GetEngine(ctx).Where("owner_id = ?", ownerID).Cols("owner_name").Update(&Repository{
+ OwnerName: ownerName,
+ }); err != nil {
+ return err
+ }
+
+ return committer.Commit()
+}
+
+// UpdateRepositoryUpdatedTime updates a repository's updated time
+func UpdateRepositoryUpdatedTime(repoID int64, updateTime time.Time) error {
+ _, err := db.GetEngine(db.DefaultContext).Exec("UPDATE repository SET updated_unix = ? WHERE id = ?", updateTime.Unix(), repoID)
+ return err
+}
+
+// UpdateRepositoryColsCtx updates repository's columns
+func UpdateRepositoryColsCtx(ctx context.Context, repo *Repository, cols ...string) error {
+ _, err := db.GetEngine(ctx).ID(repo.ID).Cols(cols...).Update(repo)
+ return err
+}
+
+// UpdateRepositoryCols updates repository's columns
+func UpdateRepositoryCols(repo *Repository, cols ...string) error {
+ return UpdateRepositoryColsCtx(db.DefaultContext, repo, cols...)
+}
+
+// ErrReachLimitOfRepo represents a "ReachLimitOfRepo" kind of error.
+type ErrReachLimitOfRepo struct {
+ Limit int
+}
+
+// IsErrReachLimitOfRepo checks if an error is a ErrReachLimitOfRepo.
+func IsErrReachLimitOfRepo(err error) bool {
+ _, ok := err.(ErrReachLimitOfRepo)
+ return ok
+}
+
+func (err ErrReachLimitOfRepo) Error() string {
+ return fmt.Sprintf("user has reached maximum limit of repositories [limit: %d]", err.Limit)
+}
+
+// ErrRepoAlreadyExist represents a "RepoAlreadyExist" kind of error.
+type ErrRepoAlreadyExist struct {
+ Uname string
+ Name string
+}
+
+// IsErrRepoAlreadyExist checks if an error is a ErrRepoAlreadyExist.
+func IsErrRepoAlreadyExist(err error) bool {
+ _, ok := err.(ErrRepoAlreadyExist)
+ return ok
+}
+
+func (err ErrRepoAlreadyExist) Error() string {
+ return fmt.Sprintf("repository already exists [uname: %s, name: %s]", err.Uname, err.Name)
+}
+
+// ErrRepoFilesAlreadyExist represents a "RepoFilesAlreadyExist" kind of error.
+type ErrRepoFilesAlreadyExist struct {
+ Uname string
+ Name string
+}
+
+// IsErrRepoFilesAlreadyExist checks if an error is a ErrRepoAlreadyExist.
+func IsErrRepoFilesAlreadyExist(err error) bool {
+ _, ok := err.(ErrRepoFilesAlreadyExist)
+ return ok
+}
+
+func (err ErrRepoFilesAlreadyExist) Error() string {
+ return fmt.Sprintf("repository files already exist [uname: %s, name: %s]", err.Uname, err.Name)
+}
+
+// CheckCreateRepository check if could created a repository
+func CheckCreateRepository(doer, u *user_model.User, name string, overwriteOrAdopt bool) error {
+ if !doer.CanCreateRepo() {
+ return ErrReachLimitOfRepo{u.MaxRepoCreation}
+ }
+
+ if err := IsUsableRepoName(name); err != nil {
+ return err
+ }
+
+ has, err := IsRepositoryExist(u, name)
+ if err != nil {
+ return fmt.Errorf("IsRepositoryExist: %v", err)
+ } else if has {
+ return ErrRepoAlreadyExist{u.Name, name}
+ }
+
+ repoPath := RepoPath(u.Name, 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 {
+ return ErrRepoFilesAlreadyExist{u.Name, name}
+ }
+ return nil
+}
+
+// ChangeRepositoryName changes all corresponding setting from old repository name to new one.
+func ChangeRepositoryName(doer *user_model.User, repo *Repository, newRepoName string) (err error) {
+ oldRepoName := repo.Name
+ newRepoName = strings.ToLower(newRepoName)
+ if err = IsUsableRepoName(newRepoName); err != nil {
+ return err
+ }
+
+ if err := repo.GetOwner(db.DefaultContext); err != nil {
+ return err
+ }
+
+ has, err := IsRepositoryExist(repo.Owner, newRepoName)
+ if err != nil {
+ return fmt.Errorf("IsRepositoryExist: %v", err)
+ } else if has {
+ return ErrRepoAlreadyExist{repo.Owner.Name, newRepoName}
+ }
+
+ newRepoPath := RepoPath(repo.Owner.Name, newRepoName)
+ if err = util.Rename(repo.RepoPath(), newRepoPath); err != nil {
+ return fmt.Errorf("rename repository directory: %v", err)
+ }
+
+ wikiPath := repo.WikiPath()
+ isExist, err := util.IsExist(wikiPath)
+ if err != nil {
+ log.Error("Unable to check if %s exists. Error: %v", wikiPath, err)
+ return err
+ }
+ if isExist {
+ if err = util.Rename(wikiPath, WikiPath(repo.Owner.Name, newRepoName)); err != nil {
+ return fmt.Errorf("rename repository wiki: %v", err)
+ }
+ }
+
+ ctx, committer, err := db.TxContext()
+ if err != nil {
+ return err
+ }
+ defer committer.Close()
+
+ if err := NewRedirect(ctx, repo.Owner.ID, repo.ID, oldRepoName, newRepoName); err != nil {
+ return err
+ }
+
+ return committer.Commit()
+}
diff --git a/models/repo/watch.go b/models/repo/watch.go
new file mode 100644
index 0000000000..8e54f0970d
--- /dev/null
+++ b/models/repo/watch.go
@@ -0,0 +1,196 @@
+// Copyright 2017 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package repo
+
+import (
+ "context"
+
+ "code.gitea.io/gitea/models/db"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/timeutil"
+)
+
+// WatchMode specifies what kind of watch the user has on a repository
+type WatchMode int8
+
+const (
+ // WatchModeNone don't watch
+ WatchModeNone WatchMode = iota // 0
+ // WatchModeNormal watch repository (from other sources)
+ WatchModeNormal // 1
+ // WatchModeDont explicit don't auto-watch
+ WatchModeDont // 2
+ // WatchModeAuto watch repository (from AutoWatchOnChanges)
+ WatchModeAuto // 3
+)
+
+// Watch is connection request for receiving repository notification.
+type Watch struct {
+ ID int64 `xorm:"pk autoincr"`
+ UserID int64 `xorm:"UNIQUE(watch)"`
+ RepoID int64 `xorm:"UNIQUE(watch)"`
+ Mode WatchMode `xorm:"SMALLINT NOT NULL DEFAULT 1"`
+ CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
+ UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
+}
+
+func init() {
+ db.RegisterModel(new(Watch))
+}
+
+// GetWatch gets what kind of subscription a user has on a given repository; returns dummy record if none found
+func GetWatch(ctx context.Context, userID, repoID int64) (Watch, error) {
+ watch := Watch{UserID: userID, RepoID: repoID}
+ has, err := db.GetEngine(ctx).Get(&watch)
+ if err != nil {
+ return watch, err
+ }
+ if !has {
+ watch.Mode = WatchModeNone
+ }
+ return watch, nil
+}
+
+// IsWatchMode Decodes watchability of WatchMode
+func IsWatchMode(mode WatchMode) bool {
+ return mode != WatchModeNone && mode != WatchModeDont
+}
+
+// IsWatching checks if user has watched given repository.
+func IsWatching(userID, repoID int64) bool {
+ watch, err := GetWatch(db.DefaultContext, userID, repoID)
+ return err == nil && IsWatchMode(watch.Mode)
+}
+
+func watchRepoMode(ctx context.Context, watch Watch, mode WatchMode) (err error) {
+ if watch.Mode == mode {
+ return nil
+ }
+ if mode == WatchModeAuto && (watch.Mode == WatchModeDont || IsWatchMode(watch.Mode)) {
+ // Don't auto watch if already watching or deliberately not watching
+ return nil
+ }
+
+ hadrec := watch.Mode != WatchModeNone
+ needsrec := mode != WatchModeNone
+ repodiff := 0
+
+ if IsWatchMode(mode) && !IsWatchMode(watch.Mode) {
+ repodiff = 1
+ } else if !IsWatchMode(mode) && IsWatchMode(watch.Mode) {
+ repodiff = -1
+ }
+
+ watch.Mode = mode
+
+ e := db.GetEngine(ctx)
+
+ if !hadrec && needsrec {
+ watch.Mode = mode
+ if _, err = e.Insert(watch); err != nil {
+ return err
+ }
+ } else if needsrec {
+ watch.Mode = mode
+ if _, err := e.ID(watch.ID).AllCols().Update(watch); err != nil {
+ return err
+ }
+ } else if _, err = e.Delete(Watch{ID: watch.ID}); err != nil {
+ return err
+ }
+ if repodiff != 0 {
+ _, err = e.Exec("UPDATE `repository` SET num_watches = num_watches + ? WHERE id = ?", repodiff, watch.RepoID)
+ }
+ return err
+}
+
+// WatchRepoMode watch repository in specific mode.
+func WatchRepoMode(userID, repoID int64, mode WatchMode) (err error) {
+ var watch Watch
+ if watch, err = GetWatch(db.DefaultContext, userID, repoID); err != nil {
+ return err
+ }
+ return watchRepoMode(db.DefaultContext, watch, mode)
+}
+
+// WatchRepoCtx watch or unwatch repository.
+func WatchRepoCtx(ctx context.Context, userID, repoID int64, doWatch bool) (err error) {
+ var watch Watch
+ if watch, err = GetWatch(ctx, userID, repoID); err != nil {
+ return err
+ }
+ if !doWatch && watch.Mode == WatchModeAuto {
+ err = watchRepoMode(ctx, watch, WatchModeDont)
+ } else if !doWatch {
+ err = watchRepoMode(ctx, watch, WatchModeNone)
+ } else {
+ err = watchRepoMode(ctx, watch, WatchModeNormal)
+ }
+ return err
+}
+
+// WatchRepo watch or unwatch repository.
+func WatchRepo(userID, repoID int64, watch bool) (err error) {
+ return WatchRepoCtx(db.DefaultContext, userID, repoID, watch)
+}
+
+// GetWatchers returns all watchers of given repository.
+func GetWatchers(ctx context.Context, repoID int64) ([]*Watch, error) {
+ watches := make([]*Watch, 0, 10)
+ return watches, db.GetEngine(ctx).Where("`watch`.repo_id=?", repoID).
+ And("`watch`.mode<>?", WatchModeDont).
+ And("`user`.is_active=?", true).
+ And("`user`.prohibit_login=?", false).
+ Join("INNER", "`user`", "`user`.id = `watch`.user_id").
+ Find(&watches)
+}
+
+// GetRepoWatchersIDs returns IDs of watchers for a given repo ID
+// but avoids joining with `user` for performance reasons
+// User permissions must be verified elsewhere if required
+func GetRepoWatchersIDs(ctx context.Context, repoID int64) ([]int64, error) {
+ ids := make([]int64, 0, 64)
+ return ids, db.GetEngine(ctx).Table("watch").
+ Where("watch.repo_id=?", repoID).
+ And("watch.mode<>?", WatchModeDont).
+ Select("user_id").
+ Find(&ids)
+}
+
+// GetRepoWatchers returns range of users watching given repository.
+func GetRepoWatchers(repoID int64, opts db.ListOptions) ([]*user_model.User, error) {
+ sess := db.GetEngine(db.DefaultContext).Where("watch.repo_id=?", repoID).
+ Join("LEFT", "watch", "`user`.id=`watch`.user_id").
+ And("`watch`.mode<>?", WatchModeDont)
+ if opts.Page > 0 {
+ sess = db.SetSessionPagination(sess, &opts)
+ users := make([]*user_model.User, 0, opts.PageSize)
+
+ return users, sess.Find(&users)
+ }
+
+ users := make([]*user_model.User, 0, 8)
+ return users, sess.Find(&users)
+}
+
+func watchIfAuto(ctx context.Context, userID, repoID int64, isWrite bool) error {
+ if !isWrite || !setting.Service.AutoWatchOnChanges {
+ return nil
+ }
+ watch, err := GetWatch(ctx, userID, repoID)
+ if err != nil {
+ return err
+ }
+ if watch.Mode != WatchModeNone {
+ return nil
+ }
+ return watchRepoMode(ctx, watch, WatchModeAuto)
+}
+
+// WatchIfAuto subscribes to repo if AutoWatchOnChanges is set
+func WatchIfAuto(userID, repoID int64, isWrite bool) error {
+ return watchIfAuto(db.DefaultContext, userID, repoID, isWrite)
+}
diff --git a/models/repo_watch_test.go b/models/repo/watch_test.go
index 1a60521396..2ff3ced2dc 100644
--- a/models/repo_watch_test.go
+++ b/models/repo/watch_test.go
@@ -2,13 +2,12 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package models
+package repo
import (
"testing"
"code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/setting"
@@ -27,25 +26,11 @@ func TestIsWatching(t *testing.T) {
assert.False(t, IsWatching(unittest.NonexistentID, unittest.NonexistentID))
}
-func TestWatchRepo(t *testing.T) {
- assert.NoError(t, unittest.PrepareTestDatabase())
- const repoID = 3
- const userID = 2
-
- assert.NoError(t, WatchRepo(userID, repoID, true))
- unittest.AssertExistsAndLoadBean(t, &Watch{RepoID: repoID, UserID: userID})
- unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: repoID})
-
- assert.NoError(t, WatchRepo(userID, repoID, false))
- unittest.AssertNotExistsBean(t, &Watch{RepoID: repoID, UserID: userID})
- unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: repoID})
-}
-
func TestGetWatchers(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
- watches, err := GetWatchers(repo.ID)
+ repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
+ watches, err := GetWatchers(db.DefaultContext, repo.ID)
assert.NoError(t, err)
// One watchers are inactive, thus minus 1
assert.Len(t, watches, repo.NumWatches-1)
@@ -53,7 +38,7 @@ func TestGetWatchers(t *testing.T) {
assert.EqualValues(t, repo.ID, watch.RepoID)
}
- watches, err = GetWatchers(unittest.NonexistentID)
+ watches, err = GetWatchers(db.DefaultContext, unittest.NonexistentID)
assert.NoError(t, err)
assert.Len(t, watches, 0)
}
@@ -61,7 +46,7 @@ func TestGetWatchers(t *testing.T) {
func TestRepository_GetWatchers(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
watchers, err := GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
assert.NoError(t, err)
assert.Len(t, watchers, repo.NumWatches)
@@ -69,53 +54,16 @@ func TestRepository_GetWatchers(t *testing.T) {
unittest.AssertExistsAndLoadBean(t, &Watch{UserID: watcher.ID, RepoID: repo.ID})
}
- repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 9}).(*repo_model.Repository)
+ repo = unittest.AssertExistsAndLoadBean(t, &Repository{ID: 9}).(*Repository)
watchers, err = GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
assert.NoError(t, err)
assert.Len(t, watchers, 0)
}
-func TestNotifyWatchers(t *testing.T) {
- assert.NoError(t, unittest.PrepareTestDatabase())
-
- action := &Action{
- ActUserID: 8,
- RepoID: 1,
- OpType: ActionStarRepo,
- }
- assert.NoError(t, NotifyWatchers(action))
-
- // One watchers are inactive, thus action is only created for user 8, 1, 4, 11
- unittest.AssertExistsAndLoadBean(t, &Action{
- ActUserID: action.ActUserID,
- UserID: 8,
- RepoID: action.RepoID,
- OpType: action.OpType,
- })
- unittest.AssertExistsAndLoadBean(t, &Action{
- ActUserID: action.ActUserID,
- UserID: 1,
- RepoID: action.RepoID,
- OpType: action.OpType,
- })
- unittest.AssertExistsAndLoadBean(t, &Action{
- ActUserID: action.ActUserID,
- UserID: 4,
- RepoID: action.RepoID,
- OpType: action.OpType,
- })
- unittest.AssertExistsAndLoadBean(t, &Action{
- ActUserID: action.ActUserID,
- UserID: 11,
- RepoID: action.RepoID,
- OpType: action.OpType,
- })
-}
-
func TestWatchIfAuto(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
watchers, err := GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
assert.NoError(t, err)
assert.Len(t, watchers, repo.NumWatches)
@@ -174,18 +122,18 @@ func TestWatchRepoMode(t *testing.T) {
unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1}, 0)
- assert.NoError(t, WatchRepoMode(12, 1, RepoWatchModeAuto))
+ assert.NoError(t, WatchRepoMode(12, 1, WatchModeAuto))
unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1}, 1)
- unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1, Mode: RepoWatchModeAuto}, 1)
+ unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1, Mode: WatchModeAuto}, 1)
- assert.NoError(t, WatchRepoMode(12, 1, RepoWatchModeNormal))
+ assert.NoError(t, WatchRepoMode(12, 1, WatchModeNormal))
unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1}, 1)
- unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1, Mode: RepoWatchModeNormal}, 1)
+ unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1, Mode: WatchModeNormal}, 1)
- assert.NoError(t, WatchRepoMode(12, 1, RepoWatchModeDont))
+ assert.NoError(t, WatchRepoMode(12, 1, WatchModeDont))
unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1}, 1)
- unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1, Mode: RepoWatchModeDont}, 1)
+ unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1, Mode: WatchModeDont}, 1)
- assert.NoError(t, WatchRepoMode(12, 1, RepoWatchModeNone))
+ assert.NoError(t, WatchRepoMode(12, 1, WatchModeNone))
unittest.AssertCount(t, &Watch{UserID: 12, RepoID: 1}, 0)
}
diff --git a/models/repo_archiver.go b/models/repo_archiver.go
deleted file mode 100644
index 1ac05da043..0000000000
--- a/models/repo_archiver.go
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2021 The Gitea Authors. All rights reserved.
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file.
-
-package models
-
-import (
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
-)
-
-// LoadArchiverRepo loads repository
-func LoadArchiverRepo(archiver *repo_model.RepoArchiver) (*repo_model.Repository, error) {
- var repo repo_model.Repository
- has, err := db.GetEngine(db.DefaultContext).ID(archiver.RepoID).Get(&repo)
- if err != nil {
- return nil, err
- }
- if !has {
- return nil, repo_model.ErrRepoNotExist{
- ID: archiver.RepoID,
- }
- }
- return &repo, nil
-}
diff --git a/models/repo_avatar.go b/models/repo_avatar.go
deleted file mode 100644
index 27af911a7e..0000000000
--- a/models/repo_avatar.go
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright 2020 The Gitea Authors. All rights reserved.
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file.
-
-package models
-
-import (
- "context"
- "crypto/md5"
- "fmt"
- "image/png"
- "io"
- "strconv"
-
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/avatar"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/storage"
-)
-
-// RemoveRandomAvatars removes the randomly generated avatars that were created for repositories
-func RemoveRandomAvatars(ctx context.Context) error {
- return db.GetEngine(db.DefaultContext).
- Where("id > 0").BufferSize(setting.Database.IterateBufferSize).
- Iterate(new(repo_model.Repository),
- func(idx int, bean interface{}) error {
- repository := bean.(*repo_model.Repository)
- select {
- case <-ctx.Done():
- return db.ErrCancelledf("before random avatars removed for %s", repository.FullName())
- default:
- }
- stringifiedID := strconv.FormatInt(repository.ID, 10)
- if repository.Avatar == stringifiedID {
- return DeleteRepoAvatar(repository)
- }
- return nil
- })
-}
-
-// UploadRepoAvatar saves custom avatar for repository.
-// FIXME: split uploads to different subdirs in case we have massive number of repos.
-func UploadRepoAvatar(repo *repo_model.Repository, data []byte) error {
- m, err := avatar.Prepare(data)
- if err != nil {
- return err
- }
-
- newAvatar := fmt.Sprintf("%d-%x", repo.ID, md5.Sum(data))
- if repo.Avatar == newAvatar { // upload the same picture
- return nil
- }
-
- ctx, committer, err := db.TxContext()
- if err != nil {
- return err
- }
- defer committer.Close()
-
- oldAvatarPath := repo.CustomAvatarRelativePath()
-
- // Users can upload the same image to other repo - prefix it with ID
- // Then repo will be removed - only it avatar file will be removed
- repo.Avatar = newAvatar
- if _, err := db.GetEngine(ctx).ID(repo.ID).Cols("avatar").Update(repo); err != nil {
- return fmt.Errorf("UploadAvatar: Update repository avatar: %v", err)
- }
-
- if err := storage.SaveFrom(storage.RepoAvatars, repo.CustomAvatarRelativePath(), func(w io.Writer) error {
- if err := png.Encode(w, *m); err != nil {
- log.Error("Encode: %v", err)
- }
- return err
- }); err != nil {
- return fmt.Errorf("UploadAvatar %s failed: Failed to remove old repo avatar %s: %v", repo.RepoPath(), newAvatar, err)
- }
-
- if len(oldAvatarPath) > 0 {
- if err := storage.RepoAvatars.Delete(oldAvatarPath); err != nil {
- return fmt.Errorf("UploadAvatar: Failed to remove old repo avatar %s: %v", oldAvatarPath, err)
- }
- }
-
- return committer.Commit()
-}
-
-// DeleteRepoAvatar deletes the repos's custom avatar.
-func DeleteRepoAvatar(repo *repo_model.Repository) error {
- // Avatar not exists
- if len(repo.Avatar) == 0 {
- return nil
- }
-
- avatarPath := repo.CustomAvatarRelativePath()
- log.Trace("DeleteAvatar[%d]: %s", repo.ID, avatarPath)
-
- ctx, committer, err := db.TxContext()
- if err != nil {
- return err
- }
- defer committer.Close()
-
- repo.Avatar = ""
- if _, err := db.GetEngine(ctx).ID(repo.ID).Cols("avatar").Update(repo); err != nil {
- return fmt.Errorf("DeleteAvatar: Update repository avatar: %v", err)
- }
-
- if err := storage.RepoAvatars.Delete(avatarPath); err != nil {
- return fmt.Errorf("DeleteAvatar: Failed to remove %s: %v", avatarPath, err)
- }
-
- return committer.Commit()
-}
diff --git a/models/repo_collaboration.go b/models/repo_collaboration.go
index ab6a3bafbe..3aca1023e6 100644
--- a/models/repo_collaboration.go
+++ b/models/repo_collaboration.go
@@ -207,15 +207,13 @@ func DeleteCollaboration(repo *repo_model.Repository, uid int64) (err error) {
}
defer committer.Close()
- sess := db.GetEngine(ctx)
-
- if has, err := sess.Delete(collaboration); err != nil || has == 0 {
+ if has, err := db.GetEngine(ctx).Delete(collaboration); err != nil || has == 0 {
return err
} else if err = recalculateAccesses(ctx, repo); err != nil {
return err
}
- if err = watchRepo(sess, uid, repo.ID, false); err != nil {
+ if err = repo_model.WatchRepoCtx(ctx, uid, repo.ID, false); err != nil {
return err
}
@@ -253,13 +251,12 @@ func reconsiderWatches(ctx context.Context, repo *repo_model.Repository, uid int
if has, err := hasAccess(ctx, uid, repo); err != nil || has {
return err
}
- e := db.GetEngine(ctx)
- if err := watchRepo(e, uid, repo.ID, false); err != nil {
+ if err := repo_model.WatchRepoCtx(ctx, uid, repo.ID, false); err != nil {
return err
}
// Remove all IssueWatches a user has subscribed to in the repository
- return removeIssueWatchersByRepoID(e, uid, repo.ID)
+ return removeIssueWatchersByRepoID(db.GetEngine(ctx), uid, repo.ID)
}
func getRepoTeams(e db.Engine, repo *repo_model.Repository) (teams []*Team, err error) {
diff --git a/models/repo_generate.go b/models/repo_generate.go
index 6b5b8e5bc1..fc749f1120 100644
--- a/models/repo_generate.go
+++ b/models/repo_generate.go
@@ -8,15 +8,12 @@ import (
"bufio"
"bytes"
"context"
- "strconv"
"strings"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/storage"
"github.com/gobwas/glob"
)
@@ -70,49 +67,6 @@ func (gt GiteaTemplate) Globs() []glob.Glob {
return gt.globs
}
-// GenerateTopics generates topics from a template repository
-func GenerateTopics(ctx context.Context, templateRepo, generateRepo *repo_model.Repository) error {
- for _, topic := range templateRepo.Topics {
- if _, err := addTopicByNameToRepo(db.GetEngine(ctx), generateRepo.ID, topic); err != nil {
- return err
- }
- }
- return nil
-}
-
-// GenerateGitHooks generates git hooks from a template repository
-func GenerateGitHooks(ctx context.Context, templateRepo, generateRepo *repo_model.Repository) error {
- generateGitRepo, err := git.OpenRepository(generateRepo.RepoPath())
- if err != nil {
- return err
- }
- defer generateGitRepo.Close()
-
- templateGitRepo, err := git.OpenRepository(templateRepo.RepoPath())
- if err != nil {
- return err
- }
- defer templateGitRepo.Close()
-
- templateHooks, err := templateGitRepo.Hooks()
- if err != nil {
- return err
- }
-
- for _, templateHook := range templateHooks {
- generateHook, err := generateGitRepo.GetHook(templateHook.Name())
- if err != nil {
- return err
- }
-
- generateHook.Content = templateHook.Content
- if err := generateHook.Update(); err != nil {
- return err
- }
- }
- return nil
-}
-
// GenerateWebhooks generates webhooks from a template repository
func GenerateWebhooks(ctx context.Context, templateRepo, generateRepo *repo_model.Repository) error {
templateWebhooks, err := webhook.ListWebhooksByOpts(&webhook.ListWebhookOptions{RepoID: templateRepo.ID})
@@ -141,16 +95,6 @@ func GenerateWebhooks(ctx context.Context, templateRepo, generateRepo *repo_mode
return nil
}
-// GenerateAvatar generates the avatar from a template repository
-func GenerateAvatar(ctx context.Context, templateRepo, generateRepo *repo_model.Repository) error {
- generateRepo.Avatar = strings.Replace(templateRepo.Avatar, strconv.FormatInt(templateRepo.ID, 10), strconv.FormatInt(generateRepo.ID, 10), 1)
- if _, err := storage.Copy(storage.RepoAvatars, generateRepo.CustomAvatarRelativePath(), storage.RepoAvatars, templateRepo.CustomAvatarRelativePath()); err != nil {
- return err
- }
-
- return updateRepositoryCols(db.GetEngine(ctx), generateRepo, "avatar")
-}
-
// GenerateIssueLabels generates issue labels from a template repository
func GenerateIssueLabels(ctx context.Context, templateRepo, generateRepo *repo_model.Repository) error {
templateLabels, err := getLabelsByRepoID(db.GetEngine(ctx), templateRepo.ID, "", db.ListOptions{})
diff --git a/models/repo_permission.go b/models/repo_permission.go
index 3dc8db92b8..45878c8ba4 100644
--- a/models/repo_permission.go
+++ b/models/repo_permission.go
@@ -419,3 +419,21 @@ func FilterOutRepoIdsWithoutUnitAccess(u *user_model.User, repoIDs []int64, unit
}
return repoIDs[:i], nil
}
+
+// GetRepoReaders returns all users that have explicit read access or higher to the repository.
+func GetRepoReaders(repo *repo_model.Repository) (_ []*user_model.User, err error) {
+ return getUsersWithAccessMode(db.DefaultContext, repo, perm_model.AccessModeRead)
+}
+
+// GetRepoWriters returns all users that have write access to the repository.
+func GetRepoWriters(repo *repo_model.Repository) (_ []*user_model.User, err error) {
+ return getUsersWithAccessMode(db.DefaultContext, repo, perm_model.AccessModeWrite)
+}
+
+// IsRepoReader returns true if user has explicit read access or higher to the repository.
+func IsRepoReader(repo *repo_model.Repository, userID int64) (bool, error) {
+ if repo.OwnerID == userID {
+ return true, nil
+ }
+ return db.GetEngine(db.DefaultContext).Where("repo_id = ? AND user_id = ? AND mode >= ?", repo.ID, userID, perm_model.AccessModeRead).Get(&Access{})
+}
diff --git a/models/repo_redirect.go b/models/repo_redirect.go
deleted file mode 100644
index 18422f9d18..0000000000
--- a/models/repo_redirect.go
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2017 The Gitea Authors. All rights reserved.
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file.
-
-package models
-
-import (
- "strings"
-
- "code.gitea.io/gitea/models/db"
-)
-
-// RepoRedirect represents that a repo name should be redirected to another
-type RepoRedirect struct {
- ID int64 `xorm:"pk autoincr"`
- OwnerID int64 `xorm:"UNIQUE(s)"`
- LowerName string `xorm:"UNIQUE(s) INDEX NOT NULL"`
- RedirectRepoID int64 // repoID to redirect to
-}
-
-func init() {
- db.RegisterModel(new(RepoRedirect))
-}
-
-// LookupRepoRedirect look up if a repository has a redirect name
-func LookupRepoRedirect(ownerID int64, repoName string) (int64, error) {
- repoName = strings.ToLower(repoName)
- redirect := &RepoRedirect{OwnerID: ownerID, LowerName: repoName}
- if has, err := db.GetEngine(db.DefaultContext).Get(redirect); err != nil {
- return 0, err
- } else if !has {
- return 0, ErrRepoRedirectNotExist{OwnerID: ownerID, RepoName: repoName}
- }
- return redirect.RedirectRepoID, nil
-}
-
-// newRepoRedirect create a new repo redirect
-func newRepoRedirect(e db.Engine, ownerID, repoID int64, oldRepoName, newRepoName string) error {
- oldRepoName = strings.ToLower(oldRepoName)
- newRepoName = strings.ToLower(newRepoName)
-
- if err := deleteRepoRedirect(e, ownerID, newRepoName); err != nil {
- return err
- }
-
- if _, err := e.Insert(&RepoRedirect{
- OwnerID: ownerID,
- LowerName: oldRepoName,
- RedirectRepoID: repoID,
- }); err != nil {
- return err
- }
- return nil
-}
-
-// deleteRepoRedirect delete any redirect from the specified repo name to
-// anything else
-func deleteRepoRedirect(e db.Engine, ownerID int64, repoName string) error {
- repoName = strings.ToLower(repoName)
- _, err := e.Delete(&RepoRedirect{OwnerID: ownerID, LowerName: repoName})
- return err
-}
diff --git a/models/repo_redirect_test.go b/models/repo_redirect_test.go
deleted file mode 100644
index c6d471448e..0000000000
--- a/models/repo_redirect_test.go
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2017 The Gitea Authors. All rights reserved.
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file.
-
-package models
-
-import (
- "testing"
-
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestLookupRepoRedirect(t *testing.T) {
- assert.NoError(t, unittest.PrepareTestDatabase())
-
- repoID, err := LookupRepoRedirect(2, "oldrepo1")
- assert.NoError(t, err)
- assert.EqualValues(t, 1, repoID)
-
- _, err = LookupRepoRedirect(unittest.NonexistentID, "doesnotexist")
- assert.True(t, IsErrRepoRedirectNotExist(err))
-}
-
-func TestNewRepoRedirect(t *testing.T) {
- // redirect to a completely new name
- assert.NoError(t, unittest.PrepareTestDatabase())
-
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
- assert.NoError(t, newRepoRedirect(db.GetEngine(db.DefaultContext), repo.OwnerID, repo.ID, repo.Name, "newreponame"))
-
- unittest.AssertExistsAndLoadBean(t, &RepoRedirect{
- OwnerID: repo.OwnerID,
- LowerName: repo.LowerName,
- RedirectRepoID: repo.ID,
- })
- unittest.AssertExistsAndLoadBean(t, &RepoRedirect{
- OwnerID: repo.OwnerID,
- LowerName: "oldrepo1",
- RedirectRepoID: repo.ID,
- })
-}
-
-func TestNewRepoRedirect2(t *testing.T) {
- // redirect to previously used name
- assert.NoError(t, unittest.PrepareTestDatabase())
-
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
- assert.NoError(t, newRepoRedirect(db.GetEngine(db.DefaultContext), repo.OwnerID, repo.ID, repo.Name, "oldrepo1"))
-
- unittest.AssertExistsAndLoadBean(t, &RepoRedirect{
- OwnerID: repo.OwnerID,
- LowerName: repo.LowerName,
- RedirectRepoID: repo.ID,
- })
- unittest.AssertNotExistsBean(t, &RepoRedirect{
- OwnerID: repo.OwnerID,
- LowerName: "oldrepo1",
- RedirectRepoID: repo.ID,
- })
-}
-
-func TestNewRepoRedirect3(t *testing.T) {
- // redirect for a previously-unredirected repo
- assert.NoError(t, unittest.PrepareTestDatabase())
-
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*repo_model.Repository)
- assert.NoError(t, newRepoRedirect(db.GetEngine(db.DefaultContext), repo.OwnerID, repo.ID, repo.Name, "newreponame"))
-
- unittest.AssertExistsAndLoadBean(t, &RepoRedirect{
- OwnerID: repo.OwnerID,
- LowerName: repo.LowerName,
- RedirectRepoID: repo.ID,
- })
-}
diff --git a/models/repo_test.go b/models/repo_test.go
index 72a2977343..45e016a8fc 100644
--- a/models/repo_test.go
+++ b/models/repo_test.go
@@ -5,11 +5,6 @@
package models
import (
- "bytes"
- "crypto/md5"
- "fmt"
- "image"
- "image/png"
"testing"
"code.gitea.io/gitea/models/db"
@@ -22,6 +17,20 @@ import (
"github.com/stretchr/testify/assert"
)
+func TestWatchRepo(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+ const repoID = 3
+ const userID = 2
+
+ assert.NoError(t, repo_model.WatchRepo(userID, repoID, true))
+ unittest.AssertExistsAndLoadBean(t, &repo_model.Watch{RepoID: repoID, UserID: userID})
+ unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: repoID})
+
+ assert.NoError(t, repo_model.WatchRepo(userID, repoID, false))
+ unittest.AssertNotExistsBean(t, &repo_model.Watch{RepoID: repoID, UserID: userID})
+ unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: repoID})
+}
+
func TestMetas(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
@@ -90,77 +99,6 @@ func TestUpdateRepositoryVisibilityChanged(t *testing.T) {
assert.True(t, act.IsPrivate)
}
-func TestGetUserFork(t *testing.T) {
- assert.NoError(t, unittest.PrepareTestDatabase())
-
- // User13 has repo 11 forked from repo10
- repo, err := repo_model.GetRepositoryByID(10)
- assert.NoError(t, err)
- assert.NotNil(t, repo)
- repo, err = GetUserFork(repo.ID, 13)
- assert.NoError(t, err)
- assert.NotNil(t, repo)
-
- repo, err = repo_model.GetRepositoryByID(9)
- assert.NoError(t, err)
- assert.NotNil(t, repo)
- repo, err = GetUserFork(repo.ID, 13)
- assert.NoError(t, err)
- assert.Nil(t, repo)
-}
-
-func TestRepoAPIURL(t *testing.T) {
- assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10}).(*repo_model.Repository)
-
- assert.Equal(t, "https://try.gitea.io/api/v1/repos/user12/repo10", repo.APIURL())
-}
-
-func TestUploadAvatar(t *testing.T) {
- // Generate image
- myImage := image.NewRGBA(image.Rect(0, 0, 1, 1))
- var buff bytes.Buffer
- png.Encode(&buff, myImage)
-
- assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10}).(*repo_model.Repository)
-
- err := UploadRepoAvatar(repo, buff.Bytes())
- assert.NoError(t, err)
- assert.Equal(t, fmt.Sprintf("%d-%x", 10, md5.Sum(buff.Bytes())), repo.Avatar)
-}
-
-func TestUploadBigAvatar(t *testing.T) {
- // Generate BIG image
- myImage := image.NewRGBA(image.Rect(0, 0, 5000, 1))
- var buff bytes.Buffer
- png.Encode(&buff, myImage)
-
- assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10}).(*repo_model.Repository)
-
- err := UploadRepoAvatar(repo, buff.Bytes())
- assert.Error(t, err)
-}
-
-func TestDeleteAvatar(t *testing.T) {
- // Generate image
- myImage := image.NewRGBA(image.Rect(0, 0, 1, 1))
- var buff bytes.Buffer
- png.Encode(&buff, myImage)
-
- assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10}).(*repo_model.Repository)
-
- err := UploadRepoAvatar(repo, buff.Bytes())
- assert.NoError(t, err)
-
- err = DeleteRepoAvatar(repo)
- assert.NoError(t, err)
-
- assert.Equal(t, "", repo.Avatar)
-}
-
func TestDoctorUserStarNum(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
diff --git a/models/repo_transfer.go b/models/repo_transfer.go
index 398ed0755a..f7d5e20990 100644
--- a/models/repo_transfer.go
+++ b/models/repo_transfer.go
@@ -5,6 +5,7 @@
package models
import (
+ "context"
"fmt"
"os"
@@ -112,8 +113,8 @@ func GetPendingRepositoryTransfer(repo *repo_model.Repository) (*RepoTransfer, e
return transfer, nil
}
-func deleteRepositoryTransfer(e db.Engine, repoID int64) error {
- _, err := e.Where("repo_id = ?", repoID).Delete(&RepoTransfer{})
+func deleteRepositoryTransfer(ctx context.Context, repoID int64) error {
+ _, err := db.GetEngine(ctx).Where("repo_id = ?", repoID).Delete(&RepoTransfer{})
return err
}
@@ -125,14 +126,13 @@ func CancelRepositoryTransfer(repo *repo_model.Repository) error {
return err
}
defer committer.Close()
- sess := db.GetEngine(ctx)
repo.Status = repo_model.RepositoryReady
- if err := updateRepositoryCols(sess, repo, "status"); err != nil {
+ if err := repo_model.UpdateRepositoryColsCtx(ctx, repo, "status"); err != nil {
return err
}
- if err := deleteRepositoryTransfer(sess, repo.ID); err != nil {
+ if err := deleteRepositoryTransfer(ctx, repo.ID); err != nil {
return err
}
@@ -158,7 +158,6 @@ func CreatePendingRepositoryTransfer(doer, newOwner *user_model.User, repoID int
return err
}
defer committer.Close()
- sess := db.GetEngine(ctx)
repo, err := repo_model.GetRepositoryByIDCtx(ctx, repoID)
if err != nil {
@@ -171,7 +170,7 @@ func CreatePendingRepositoryTransfer(doer, newOwner *user_model.User, repoID int
}
repo.Status = repo_model.RepositoryPendingTransfer
- if err := updateRepositoryCols(sess, repo, "status"); err != nil {
+ if err := repo_model.UpdateRepositoryColsCtx(ctx, repo, "status"); err != nil {
return err
}
@@ -179,7 +178,10 @@ func CreatePendingRepositoryTransfer(doer, newOwner *user_model.User, repoID int
if has, err := repo_model.IsRepositoryExistCtx(ctx, newOwner, repo.Name); err != nil {
return fmt.Errorf("IsRepositoryExist: %v", err)
} else if has {
- return ErrRepoAlreadyExist{newOwner.LowerName, repo.Name}
+ return repo_model.ErrRepoAlreadyExist{
+ Uname: newOwner.LowerName,
+ Name: repo.Name,
+ }
}
transfer := &RepoTransfer{
@@ -256,7 +258,10 @@ func TransferOwnership(doer *user_model.User, newOwnerName string, repo *repo_mo
if has, err := repo_model.IsRepositoryExistCtx(ctx, newOwner, repo.Name); err != nil {
return fmt.Errorf("IsRepositoryExist: %v", err)
} else if has {
- return ErrRepoAlreadyExist{newOwnerName, repo.Name}
+ return repo_model.ErrRepoAlreadyExist{
+ Uname: newOwnerName,
+ Name: repo.Name,
+ }
}
oldOwner := repo.Owner
@@ -336,13 +341,13 @@ func TransferOwnership(doer *user_model.User, newOwnerName string, repo *repo_mo
return fmt.Errorf("decrease old owner repository count: %v", err)
}
- if err := watchRepo(sess, doer.ID, repo.ID, true); err != nil {
+ if err := repo_model.WatchRepoCtx(ctx, doer.ID, repo.ID, true); err != nil {
return fmt.Errorf("watchRepo: %v", err)
}
// Remove watch for organization.
if oldOwner.IsOrganization() {
- if err := watchRepo(sess, oldOwner.ID, repo.ID, false); err != nil {
+ if err := repo_model.WatchRepoCtx(ctx, oldOwner.ID, repo.ID, false); err != nil {
return fmt.Errorf("watchRepo [false]: %v", err)
}
}
@@ -399,21 +404,21 @@ func TransferOwnership(doer *user_model.User, newOwnerName string, repo *repo_mo
wikiRenamed = true
}
- if err := deleteRepositoryTransfer(sess, repo.ID); err != nil {
+ if err := deleteRepositoryTransfer(ctx, repo.ID); err != nil {
return fmt.Errorf("deleteRepositoryTransfer: %v", err)
}
repo.Status = repo_model.RepositoryReady
- if err := updateRepositoryCols(sess, repo, "status"); err != nil {
+ if err := repo_model.UpdateRepositoryColsCtx(ctx, repo, "status"); err != nil {
return err
}
// If there was previously a redirect at this location, remove it.
- if err := deleteRepoRedirect(sess, newOwner.ID, repo.Name); err != nil {
+ if err := repo_model.DeleteRedirect(ctx, newOwner.ID, repo.Name); err != nil {
return fmt.Errorf("delete repo redirect: %v", err)
}
- if err := newRepoRedirect(sess, oldOwner.ID, repo.ID, repo.Name, repo.Name); err != nil {
- return fmt.Errorf("newRepoRedirect: %v", err)
+ if err := repo_model.NewRedirect(ctx, oldOwner.ID, repo.ID, repo.Name, repo.Name); err != nil {
+ return fmt.Errorf("repo_model.NewRedirect: %v", err)
}
return committer.Commit()
diff --git a/models/repo_watch.go b/models/repo_watch.go
deleted file mode 100644
index 6ae478d65f..0000000000
--- a/models/repo_watch.go
+++ /dev/null
@@ -1,328 +0,0 @@
-// Copyright 2017 The Gitea Authors. All rights reserved.
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file.
-
-package models
-
-import (
- "context"
- "fmt"
-
- "code.gitea.io/gitea/models/db"
- 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/modules/setting"
- "code.gitea.io/gitea/modules/timeutil"
-)
-
-// RepoWatchMode specifies what kind of watch the user has on a repository
-type RepoWatchMode int8
-
-const (
- // RepoWatchModeNone don't watch
- RepoWatchModeNone RepoWatchMode = iota // 0
- // RepoWatchModeNormal watch repository (from other sources)
- RepoWatchModeNormal // 1
- // RepoWatchModeDont explicit don't auto-watch
- RepoWatchModeDont // 2
- // RepoWatchModeAuto watch repository (from AutoWatchOnChanges)
- RepoWatchModeAuto // 3
-)
-
-// Watch is connection request for receiving repository notification.
-type Watch struct {
- ID int64 `xorm:"pk autoincr"`
- UserID int64 `xorm:"UNIQUE(watch)"`
- RepoID int64 `xorm:"UNIQUE(watch)"`
- Mode RepoWatchMode `xorm:"SMALLINT NOT NULL DEFAULT 1"`
- CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
- UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
-}
-
-func init() {
- db.RegisterModel(new(Watch))
-}
-
-// getWatch gets what kind of subscription a user has on a given repository; returns dummy record if none found
-func getWatch(e db.Engine, userID, repoID int64) (Watch, error) {
- watch := Watch{UserID: userID, RepoID: repoID}
- has, err := e.Get(&watch)
- if err != nil {
- return watch, err
- }
- if !has {
- watch.Mode = RepoWatchModeNone
- }
- return watch, nil
-}
-
-// Decodes watchability of RepoWatchMode
-func isWatchMode(mode RepoWatchMode) bool {
- return mode != RepoWatchModeNone && mode != RepoWatchModeDont
-}
-
-// IsWatching checks if user has watched given repository.
-func IsWatching(userID, repoID int64) bool {
- watch, err := getWatch(db.GetEngine(db.DefaultContext), userID, repoID)
- return err == nil && isWatchMode(watch.Mode)
-}
-
-func watchRepoMode(e db.Engine, watch Watch, mode RepoWatchMode) (err error) {
- if watch.Mode == mode {
- return nil
- }
- if mode == RepoWatchModeAuto && (watch.Mode == RepoWatchModeDont || isWatchMode(watch.Mode)) {
- // Don't auto watch if already watching or deliberately not watching
- return nil
- }
-
- hadrec := watch.Mode != RepoWatchModeNone
- needsrec := mode != RepoWatchModeNone
- repodiff := 0
-
- if isWatchMode(mode) && !isWatchMode(watch.Mode) {
- repodiff = 1
- } else if !isWatchMode(mode) && isWatchMode(watch.Mode) {
- repodiff = -1
- }
-
- watch.Mode = mode
-
- if !hadrec && needsrec {
- watch.Mode = mode
- if _, err = e.Insert(watch); err != nil {
- return err
- }
- } else if needsrec {
- watch.Mode = mode
- if _, err := e.ID(watch.ID).AllCols().Update(watch); err != nil {
- return err
- }
- } else if _, err = e.Delete(Watch{ID: watch.ID}); err != nil {
- return err
- }
- if repodiff != 0 {
- _, err = e.Exec("UPDATE `repository` SET num_watches = num_watches + ? WHERE id = ?", repodiff, watch.RepoID)
- }
- return err
-}
-
-// WatchRepoMode watch repository in specific mode.
-func WatchRepoMode(userID, repoID int64, mode RepoWatchMode) (err error) {
- var watch Watch
- if watch, err = getWatch(db.GetEngine(db.DefaultContext), userID, repoID); err != nil {
- return err
- }
- return watchRepoMode(db.GetEngine(db.DefaultContext), watch, mode)
-}
-
-func watchRepo(e db.Engine, userID, repoID int64, doWatch bool) (err error) {
- var watch Watch
- if watch, err = getWatch(e, userID, repoID); err != nil {
- return err
- }
- if !doWatch && watch.Mode == RepoWatchModeAuto {
- err = watchRepoMode(e, watch, RepoWatchModeDont)
- } else if !doWatch {
- err = watchRepoMode(e, watch, RepoWatchModeNone)
- } else {
- err = watchRepoMode(e, watch, RepoWatchModeNormal)
- }
- return err
-}
-
-// WatchRepo watch or unwatch repository.
-func WatchRepo(userID, repoID int64, watch bool) (err error) {
- return watchRepo(db.GetEngine(db.DefaultContext), userID, repoID, watch)
-}
-
-func getWatchers(e db.Engine, repoID int64) ([]*Watch, error) {
- watches := make([]*Watch, 0, 10)
- return watches, e.Where("`watch`.repo_id=?", repoID).
- And("`watch`.mode<>?", RepoWatchModeDont).
- And("`user`.is_active=?", true).
- And("`user`.prohibit_login=?", false).
- Join("INNER", "`user`", "`user`.id = `watch`.user_id").
- Find(&watches)
-}
-
-// GetWatchers returns all watchers of given repository.
-func GetWatchers(repoID int64) ([]*Watch, error) {
- return getWatchers(db.GetEngine(db.DefaultContext), repoID)
-}
-
-// GetRepoWatchersIDs returns IDs of watchers for a given repo ID
-// but avoids joining with `user` for performance reasons
-// User permissions must be verified elsewhere if required
-func GetRepoWatchersIDs(repoID int64) ([]int64, error) {
- return getRepoWatchersIDs(db.GetEngine(db.DefaultContext), repoID)
-}
-
-func getRepoWatchersIDs(e db.Engine, repoID int64) ([]int64, error) {
- ids := make([]int64, 0, 64)
- return ids, e.Table("watch").
- Where("watch.repo_id=?", repoID).
- And("watch.mode<>?", RepoWatchModeDont).
- Select("user_id").
- Find(&ids)
-}
-
-// GetRepoWatchers returns range of users watching given repository.
-func GetRepoWatchers(repoID int64, opts db.ListOptions) ([]*user_model.User, error) {
- sess := db.GetEngine(db.DefaultContext).Where("watch.repo_id=?", repoID).
- Join("LEFT", "watch", "`user`.id=`watch`.user_id").
- And("`watch`.mode<>?", RepoWatchModeDont)
- if opts.Page > 0 {
- sess = db.SetSessionPagination(sess, &opts)
- users := make([]*user_model.User, 0, opts.PageSize)
-
- return users, sess.Find(&users)
- }
-
- users := make([]*user_model.User, 0, 8)
- return users, sess.Find(&users)
-}
-
-func notifyWatchers(ctx context.Context, actions ...*Action) error {
- var watchers []*Watch
- var repo *repo_model.Repository
- var err error
- var permCode []bool
- var permIssue []bool
- var permPR []bool
-
- e := db.GetEngine(ctx)
-
- for _, act := range actions {
- repoChanged := repo == nil || repo.ID != act.RepoID
-
- if repoChanged {
- // Add feeds for user self and all watchers.
- watchers, err = getWatchers(e, act.RepoID)
- if err != nil {
- return fmt.Errorf("get watchers: %v", err)
- }
- }
-
- // Add feed for actioner.
- act.UserID = act.ActUserID
- if _, err = e.InsertOne(act); err != nil {
- return fmt.Errorf("insert new actioner: %v", err)
- }
-
- if repoChanged {
- act.loadRepo()
- repo = act.Repo
-
- // check repo owner exist.
- if err := act.Repo.GetOwner(ctx); err != nil {
- return fmt.Errorf("can't get repo owner: %v", err)
- }
- } else if act.Repo == nil {
- act.Repo = repo
- }
-
- // Add feed for organization
- if act.Repo.Owner.IsOrganization() && act.ActUserID != act.Repo.Owner.ID {
- act.ID = 0
- act.UserID = act.Repo.Owner.ID
- if _, err = e.InsertOne(act); err != nil {
- return fmt.Errorf("insert new actioner: %v", err)
- }
- }
-
- if repoChanged {
- permCode = make([]bool, len(watchers))
- permIssue = make([]bool, len(watchers))
- permPR = make([]bool, len(watchers))
- for i, watcher := range watchers {
- user, err := user_model.GetUserByIDEngine(e, watcher.UserID)
- if err != nil {
- permCode[i] = false
- permIssue[i] = false
- permPR[i] = false
- continue
- }
- perm, err := getUserRepoPermission(ctx, repo, user)
- if err != nil {
- permCode[i] = false
- permIssue[i] = false
- permPR[i] = false
- continue
- }
- permCode[i] = perm.CanRead(unit.TypeCode)
- permIssue[i] = perm.CanRead(unit.TypeIssues)
- permPR[i] = perm.CanRead(unit.TypePullRequests)
- }
- }
-
- for i, watcher := range watchers {
- if act.ActUserID == watcher.UserID {
- continue
- }
- act.ID = 0
- act.UserID = watcher.UserID
- act.Repo.Units = nil
-
- switch act.OpType {
- case ActionCommitRepo, ActionPushTag, ActionDeleteTag, ActionPublishRelease, ActionDeleteBranch:
- if !permCode[i] {
- continue
- }
- case ActionCreateIssue, ActionCommentIssue, ActionCloseIssue, ActionReopenIssue:
- if !permIssue[i] {
- continue
- }
- case ActionCreatePullRequest, ActionCommentPull, ActionMergePullRequest, ActionClosePullRequest, ActionReopenPullRequest:
- if !permPR[i] {
- continue
- }
- }
-
- if _, err = e.InsertOne(act); err != nil {
- return fmt.Errorf("insert new action: %v", err)
- }
- }
- }
- return nil
-}
-
-// NotifyWatchers creates batch of actions for every watcher.
-func NotifyWatchers(actions ...*Action) error {
- return notifyWatchers(db.DefaultContext, actions...)
-}
-
-// NotifyWatchersActions creates batch of actions for every watcher.
-func NotifyWatchersActions(acts []*Action) error {
- ctx, committer, err := db.TxContext()
- if err != nil {
- return err
- }
- defer committer.Close()
- for _, act := range acts {
- if err := notifyWatchers(ctx, act); err != nil {
- return err
- }
- }
- return committer.Commit()
-}
-
-func watchIfAuto(e db.Engine, userID, repoID int64, isWrite bool) error {
- if !isWrite || !setting.Service.AutoWatchOnChanges {
- return nil
- }
- watch, err := getWatch(e, userID, repoID)
- if err != nil {
- return err
- }
- if watch.Mode != RepoWatchModeNone {
- return nil
- }
- return watchRepoMode(e, watch, RepoWatchModeAuto)
-}
-
-// WatchIfAuto subscribes to repo if AutoWatchOnChanges is set
-func WatchIfAuto(userID, repoID int64, isWrite bool) error {
- return watchIfAuto(db.GetEngine(db.DefaultContext), userID, repoID, isWrite)
-}
diff --git a/models/statistic.go b/models/statistic.go
index 175815081f..f39cdd5eb7 100644
--- a/models/statistic.go
+++ b/models/statistic.go
@@ -50,8 +50,8 @@ func GetStatistic() (stats Statistic) {
stats.Counter.Org = CountOrganizations()
stats.Counter.PublicKey, _ = e.Count(new(asymkey_model.PublicKey))
stats.Counter.Repo = repo_model.CountRepositories(true)
- stats.Counter.Watch, _ = e.Count(new(Watch))
- stats.Counter.Star, _ = e.Count(new(Star))
+ stats.Counter.Watch, _ = e.Count(new(repo_model.Watch))
+ stats.Counter.Star, _ = e.Count(new(repo_model.Star))
stats.Counter.Action, _ = e.Count(new(Action))
stats.Counter.Access, _ = e.Count(new(Access))
diff --git a/models/update.go b/models/update.go
deleted file mode 100644
index 14333ed985..0000000000
--- a/models/update.go
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright 2014 The Gogs Authors. All rights reserved.
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file.
-
-package models
-
-import (
- "context"
- "fmt"
- "strings"
-
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
-)
-
-// PushUpdateDeleteTagsContext updates a number of delete tags with context
-func PushUpdateDeleteTagsContext(ctx context.Context, repo *repo_model.Repository, tags []string) error {
- return pushUpdateDeleteTags(db.GetEngine(ctx), repo, tags)
-}
-
-func pushUpdateDeleteTags(e db.Engine, repo *repo_model.Repository, tags []string) error {
- if len(tags) == 0 {
- return nil
- }
- lowerTags := make([]string, 0, len(tags))
- for _, tag := range tags {
- lowerTags = append(lowerTags, strings.ToLower(tag))
- }
-
- if _, err := e.
- Where("repo_id = ? AND is_tag = ?", repo.ID, true).
- In("lower_tag_name", lowerTags).
- Delete(new(Release)); err != nil {
- return fmt.Errorf("Delete: %v", err)
- }
-
- if _, err := e.
- Where("repo_id = ? AND is_tag = ?", repo.ID, false).
- In("lower_tag_name", lowerTags).
- Cols("is_draft", "num_commits", "sha1").
- Update(&Release{
- IsDraft: true,
- }); err != nil {
- return fmt.Errorf("Update: %v", err)
- }
-
- return nil
-}
-
-// PushUpdateDeleteTag must be called for any push actions to delete tag
-func PushUpdateDeleteTag(repo *repo_model.Repository, tagName string) error {
- rel, err := GetRelease(repo.ID, tagName)
- if err != nil {
- if IsErrReleaseNotExist(err) {
- return nil
- }
- return fmt.Errorf("GetRelease: %v", err)
- }
- if rel.IsTag {
- if _, err = db.GetEngine(db.DefaultContext).ID(rel.ID).Delete(new(Release)); err != nil {
- return fmt.Errorf("Delete: %v", err)
- }
- } else {
- rel.IsDraft = true
- rel.NumCommits = 0
- rel.Sha1 = ""
- if _, err = db.GetEngine(db.DefaultContext).ID(rel.ID).AllCols().Update(rel); err != nil {
- return fmt.Errorf("Update: %v", err)
- }
- }
-
- return nil
-}
-
-// SaveOrUpdateTag must be called for any push actions to add tag
-func SaveOrUpdateTag(repo *repo_model.Repository, newRel *Release) error {
- rel, err := GetRelease(repo.ID, newRel.TagName)
- if err != nil && !IsErrReleaseNotExist(err) {
- return fmt.Errorf("GetRelease: %v", err)
- }
-
- if rel == nil {
- rel = newRel
- if _, err = db.GetEngine(db.DefaultContext).Insert(rel); err != nil {
- return fmt.Errorf("InsertOne: %v", err)
- }
- } else {
- rel.Sha1 = newRel.Sha1
- rel.CreatedUnix = newRel.CreatedUnix
- rel.NumCommits = newRel.NumCommits
- rel.IsDraft = false
- if rel.IsTag && newRel.PublisherID > 0 {
- rel.PublisherID = newRel.PublisherID
- }
- if _, err = db.GetEngine(db.DefaultContext).ID(rel.ID).AllCols().Update(rel); err != nil {
- return fmt.Errorf("Update: %v", err)
- }
- }
- return nil
-}
diff --git a/models/user.go b/models/user.go
index 2a727dd124..ddd63bf5fe 100644
--- a/models/user.go
+++ b/models/user.go
@@ -150,7 +150,7 @@ func DeleteUser(ctx context.Context, u *user_model.User) (err error) {
// ***** START: Watch *****
watchedRepoIDs := make([]int64, 0, 10)
if err = e.Table("watch").Cols("watch.repo_id").
- Where("watch.user_id = ?", u.ID).And("watch.mode <>?", RepoWatchModeDont).Find(&watchedRepoIDs); err != nil {
+ Where("watch.user_id = ?", u.ID).And("watch.mode <>?", repo_model.WatchModeDont).Find(&watchedRepoIDs); err != nil {
return fmt.Errorf("get all watches: %v", err)
}
if _, err = e.Decr("num_watches").In("id", watchedRepoIDs).NoAutoTime().Update(new(repo_model.Repository)); err != nil {
@@ -190,8 +190,8 @@ func DeleteUser(ctx context.Context, u *user_model.User) (err error) {
&AccessToken{UID: u.ID},
&Collaboration{UserID: u.ID},
&Access{UserID: u.ID},
- &Watch{UserID: u.ID},
- &Star{UID: u.ID},
+ &repo_model.Watch{UserID: u.ID},
+ &repo_model.Star{UID: u.ID},
&user_model.Follow{UserID: u.ID},
&user_model.Follow{FollowID: u.ID},
&Action{UserID: u.ID},
@@ -296,7 +296,7 @@ func GetStarredRepos(userID int64, private bool, listOptions db.ListOptions) ([]
// GetWatchedRepos returns the repos watched by a particular user
func GetWatchedRepos(userID int64, private bool, listOptions db.ListOptions) ([]*repo_model.Repository, int64, error) {
sess := db.GetEngine(db.DefaultContext).Where("watch.user_id=?", userID).
- And("`watch`.mode<>?", RepoWatchModeDont).
+ And("`watch`.mode<>?", repo_model.WatchModeDont).
Join("LEFT", "watch", "`repository`.id=`watch`.repo_id")
if !private {
sess = sess.And("is_private=?", false)