mirror of https://github.com/go-gitea/gitea
Some repository refactors (#17950)
* some repository refactors * remove unnecessary code * Fix test * Remove unnecessary bannerpull/17954/head^2
parent
0a7e8327a0
commit
5723240490
@ -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}) |
||||||
|
} |
@ -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) |
||||||
|
} |
@ -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 |
||||||
|
} |
@ -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, |
||||||
|
}) |
||||||
|
} |
@ -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() |
||||||
|
} |
@ -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) |
||||||
|
} |
@ -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 |
|
||||||
} |
|
@ -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 |
|
||||||
} |
|
@ -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, |
|
||||||
}) |
|
||||||
} |
|
@ -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) |
|
||||||
} |
|
@ -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 |
|
||||||
} |
|
@ -1,43 +0,0 @@ |
|||||||
// Copyright 2019 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 repofiles |
|
||||||
|
|
||||||
import ( |
|
||||||
"fmt" |
|
||||||
|
|
||||||
"code.gitea.io/gitea/models" |
|
||||||
repo_model "code.gitea.io/gitea/models/repo" |
|
||||||
user_model "code.gitea.io/gitea/models/user" |
|
||||||
"code.gitea.io/gitea/modules/git" |
|
||||||
) |
|
||||||
|
|
||||||
// CreateCommitStatus creates a new CommitStatus given a bunch of parameters
|
|
||||||
// NOTE: All text-values will be trimmed from whitespaces.
|
|
||||||
// Requires: Repo, Creator, SHA
|
|
||||||
func CreateCommitStatus(repo *repo_model.Repository, creator *user_model.User, sha string, status *models.CommitStatus) error { |
|
||||||
repoPath := repo.RepoPath() |
|
||||||
|
|
||||||
// confirm that commit is exist
|
|
||||||
gitRepo, err := git.OpenRepository(repoPath) |
|
||||||
if err != nil { |
|
||||||
return fmt.Errorf("OpenRepository[%s]: %v", repoPath, err) |
|
||||||
} |
|
||||||
if _, err := gitRepo.GetCommit(sha); err != nil { |
|
||||||
gitRepo.Close() |
|
||||||
return fmt.Errorf("GetCommit[%s]: %v", sha, err) |
|
||||||
} |
|
||||||
gitRepo.Close() |
|
||||||
|
|
||||||
if err := models.NewCommitStatus(models.NewCommitStatusOptions{ |
|
||||||
Repo: repo, |
|
||||||
Creator: creator, |
|
||||||
SHA: sha, |
|
||||||
CommitStatus: status, |
|
||||||
}); err != nil { |
|
||||||
return fmt.Errorf("NewCommitStatus[repo_id: %d, user_id: %d, sha: %s]: %v", repo.ID, creator.ID, sha, err) |
|
||||||
} |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
@ -0,0 +1,64 @@ |
|||||||
|
// 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 repository |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"crypto/md5" |
||||||
|
"fmt" |
||||||
|
"image" |
||||||
|
"image/png" |
||||||
|
"testing" |
||||||
|
|
||||||
|
repo_model "code.gitea.io/gitea/models/repo" |
||||||
|
"code.gitea.io/gitea/models/unittest" |
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert" |
||||||
|
) |
||||||
|
|
||||||
|
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 := UploadAvatar(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 := UploadAvatar(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 := UploadAvatar(repo, buff.Bytes()) |
||||||
|
assert.NoError(t, err) |
||||||
|
|
||||||
|
err = DeleteAvatar(repo) |
||||||
|
assert.NoError(t, err) |
||||||
|
|
||||||
|
assert.Equal(t, "", repo.Avatar) |
||||||
|
} |
Loading…
Reference in new issue