Merge branch 'master' into refactor_issues-subscription
This commit is contained in:
@@ -30,26 +30,28 @@ type ActionType int
|
||||
|
||||
// Possible action types.
|
||||
const (
|
||||
ActionCreateRepo ActionType = iota + 1 // 1
|
||||
ActionRenameRepo // 2
|
||||
ActionStarRepo // 3
|
||||
ActionWatchRepo // 4
|
||||
ActionCommitRepo // 5
|
||||
ActionCreateIssue // 6
|
||||
ActionCreatePullRequest // 7
|
||||
ActionTransferRepo // 8
|
||||
ActionPushTag // 9
|
||||
ActionCommentIssue // 10
|
||||
ActionMergePullRequest // 11
|
||||
ActionCloseIssue // 12
|
||||
ActionReopenIssue // 13
|
||||
ActionClosePullRequest // 14
|
||||
ActionReopenPullRequest // 15
|
||||
ActionDeleteTag // 16
|
||||
ActionDeleteBranch // 17
|
||||
ActionMirrorSyncPush // 18
|
||||
ActionMirrorSyncCreate // 19
|
||||
ActionMirrorSyncDelete // 20
|
||||
ActionCreateRepo ActionType = iota + 1 // 1
|
||||
ActionRenameRepo // 2
|
||||
ActionStarRepo // 3
|
||||
ActionWatchRepo // 4
|
||||
ActionCommitRepo // 5
|
||||
ActionCreateIssue // 6
|
||||
ActionCreatePullRequest // 7
|
||||
ActionTransferRepo // 8
|
||||
ActionPushTag // 9
|
||||
ActionCommentIssue // 10
|
||||
ActionMergePullRequest // 11
|
||||
ActionCloseIssue // 12
|
||||
ActionReopenIssue // 13
|
||||
ActionClosePullRequest // 14
|
||||
ActionReopenPullRequest // 15
|
||||
ActionDeleteTag // 16
|
||||
ActionDeleteBranch // 17
|
||||
ActionMirrorSyncPush // 18
|
||||
ActionMirrorSyncCreate // 19
|
||||
ActionMirrorSyncDelete // 20
|
||||
ActionApprovePullRequest // 21
|
||||
ActionRejectPullRequest // 22
|
||||
)
|
||||
|
||||
// Action represents user operation type and other information to
|
||||
@@ -520,52 +522,6 @@ func UpdateIssuesCommit(doer *User, repo *Repository, commits []*PushCommit, bra
|
||||
return nil
|
||||
}
|
||||
|
||||
func transferRepoAction(e Engine, doer, oldOwner *User, repo *Repository) (err error) {
|
||||
if err = notifyWatchers(e, &Action{
|
||||
ActUserID: doer.ID,
|
||||
ActUser: doer,
|
||||
OpType: ActionTransferRepo,
|
||||
RepoID: repo.ID,
|
||||
Repo: repo,
|
||||
IsPrivate: repo.IsPrivate,
|
||||
Content: path.Join(oldOwner.Name, repo.Name),
|
||||
}); err != nil {
|
||||
return fmt.Errorf("notifyWatchers: %v", err)
|
||||
}
|
||||
|
||||
// Remove watch for organization.
|
||||
if oldOwner.IsOrganization() {
|
||||
if err = watchRepo(e, oldOwner.ID, repo.ID, false); err != nil {
|
||||
return fmt.Errorf("watchRepo [false]: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TransferRepoAction adds new action for transferring repository,
|
||||
// the Owner field of repository is assumed to be new owner.
|
||||
func TransferRepoAction(doer, oldOwner *User, repo *Repository) error {
|
||||
return transferRepoAction(x, doer, oldOwner, repo)
|
||||
}
|
||||
|
||||
func mergePullRequestAction(e Engine, doer *User, repo *Repository, issue *Issue) error {
|
||||
return notifyWatchers(e, &Action{
|
||||
ActUserID: doer.ID,
|
||||
ActUser: doer,
|
||||
OpType: ActionMergePullRequest,
|
||||
Content: fmt.Sprintf("%d|%s", issue.Index, issue.Title),
|
||||
RepoID: repo.ID,
|
||||
Repo: repo,
|
||||
IsPrivate: repo.IsPrivate,
|
||||
})
|
||||
}
|
||||
|
||||
// MergePullRequestAction adds new action for merging pull request.
|
||||
func MergePullRequestAction(actUser *User, repo *Repository, pull *Issue) error {
|
||||
return mergePullRequestAction(x, actUser, repo, pull)
|
||||
}
|
||||
|
||||
// GetFeedsOptions options for retrieving feeds
|
||||
type GetFeedsOptions struct {
|
||||
RequestedUser *User
|
||||
|
||||
@@ -332,54 +332,6 @@ func TestUpdateIssuesCommit_AnotherRepoNoPermission(t *testing.T) {
|
||||
CheckConsistencyFor(t, &Action{})
|
||||
}
|
||||
|
||||
func TestTransferRepoAction(t *testing.T) {
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
|
||||
user2 := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
|
||||
user4 := AssertExistsAndLoadBean(t, &User{ID: 4}).(*User)
|
||||
repo := AssertExistsAndLoadBean(t, &Repository{ID: 1, OwnerID: user2.ID}).(*Repository)
|
||||
|
||||
repo.OwnerID = user4.ID
|
||||
repo.Owner = user4
|
||||
|
||||
actionBean := &Action{
|
||||
OpType: ActionTransferRepo,
|
||||
ActUserID: user2.ID,
|
||||
ActUser: user2,
|
||||
RepoID: repo.ID,
|
||||
Repo: repo,
|
||||
IsPrivate: repo.IsPrivate,
|
||||
}
|
||||
AssertNotExistsBean(t, actionBean)
|
||||
assert.NoError(t, TransferRepoAction(user2, user2, repo))
|
||||
AssertExistsAndLoadBean(t, actionBean)
|
||||
|
||||
_, err := x.ID(repo.ID).Cols("owner_id").Update(repo)
|
||||
assert.NoError(t, err)
|
||||
CheckConsistencyFor(t, &Action{})
|
||||
}
|
||||
|
||||
func TestMergePullRequestAction(t *testing.T) {
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
user := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
|
||||
repo := AssertExistsAndLoadBean(t, &Repository{ID: 1, OwnerID: user.ID}).(*Repository)
|
||||
repo.Owner = user
|
||||
issue := AssertExistsAndLoadBean(t, &Issue{ID: 3, RepoID: repo.ID}).(*Issue)
|
||||
|
||||
actionBean := &Action{
|
||||
OpType: ActionMergePullRequest,
|
||||
ActUserID: user.ID,
|
||||
ActUser: user,
|
||||
RepoID: repo.ID,
|
||||
Repo: repo,
|
||||
IsPrivate: repo.IsPrivate,
|
||||
}
|
||||
AssertNotExistsBean(t, actionBean)
|
||||
assert.NoError(t, MergePullRequestAction(user, repo, issue))
|
||||
AssertExistsAndLoadBean(t, actionBean)
|
||||
CheckConsistencyFor(t, &Action{})
|
||||
}
|
||||
|
||||
func TestGetFeeds(t *testing.T) {
|
||||
// test with an individual user
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
|
||||
@@ -539,8 +539,10 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = sendCreateCommentAction(e, opts, comment); err != nil {
|
||||
return nil, err
|
||||
if !opts.NoAction {
|
||||
if err = sendCreateCommentAction(e, opts, comment); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err = comment.addCrossReferences(e, opts.Doer); err != nil {
|
||||
@@ -816,6 +818,7 @@ type CreateCommentOptions struct {
|
||||
RefCommentID int64
|
||||
RefAction references.XRefAction
|
||||
RefIsPull bool
|
||||
NoAction bool
|
||||
}
|
||||
|
||||
// CreateComment creates comment of issue or commit.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// 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.
|
||||
|
||||
@@ -403,6 +404,19 @@ func LoginViaLDAP(user *User, login, password string, source *LoginSource, autoR
|
||||
|
||||
var isAttributeSSHPublicKeySet = len(strings.TrimSpace(source.LDAP().AttributeSSHPublicKey)) > 0
|
||||
|
||||
// Update User admin flag if exist
|
||||
if isExist, err := IsUserExist(0, sr.Username); err != nil {
|
||||
return nil, err
|
||||
} else if isExist &&
|
||||
!user.ProhibitLogin && len(source.LDAP().AdminFilter) > 0 && user.IsAdmin != sr.IsAdmin {
|
||||
// Change existing admin flag only if AdminFilter option is set
|
||||
user.IsAdmin = sr.IsAdmin
|
||||
err = UpdateUserCols(user, "is_admin")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if !autoRegister {
|
||||
if isAttributeSSHPublicKeySet && synchronizeLdapSSHPublicKeys(user, source, sr.SSHPublicKey) {
|
||||
return user, RewriteAllPublicKeys()
|
||||
|
||||
@@ -1792,8 +1792,13 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error
|
||||
|
||||
if err = watchRepo(sess, doer.ID, repo.ID, true); err != nil {
|
||||
return fmt.Errorf("watchRepo: %v", err)
|
||||
} else if err = transferRepoAction(sess, doer, owner, repo); err != nil {
|
||||
return fmt.Errorf("transferRepoAction: %v", err)
|
||||
}
|
||||
|
||||
// Remove watch for organization.
|
||||
if owner.IsOrganization() {
|
||||
if err = watchRepo(sess, owner.ID, repo.ID, false); err != nil {
|
||||
return fmt.Errorf("watchRepo [false]: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Rename remote repository to new path and delete local copy.
|
||||
@@ -1824,23 +1829,21 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error
|
||||
}
|
||||
|
||||
// ChangeRepositoryName changes all corresponding setting from old repository name to new one.
|
||||
func ChangeRepositoryName(u *User, oldRepoName, newRepoName string) (err error) {
|
||||
oldRepoName = strings.ToLower(oldRepoName)
|
||||
func ChangeRepositoryName(doer *User, repo *Repository, newRepoName string) (err error) {
|
||||
newRepoName = strings.ToLower(newRepoName)
|
||||
if err = IsUsableRepoName(newRepoName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
has, err := IsRepositoryExist(u, newRepoName)
|
||||
if err := repo.GetOwner(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
has, err := IsRepositoryExist(repo.Owner, newRepoName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("IsRepositoryExist: %v", err)
|
||||
} else if has {
|
||||
return ErrRepoAlreadyExist{u.Name, newRepoName}
|
||||
}
|
||||
|
||||
repo, err := GetRepositoryByName(u.ID, oldRepoName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetRepositoryByName: %v", err)
|
||||
return ErrRepoAlreadyExist{repo.Owner.Name, newRepoName}
|
||||
}
|
||||
|
||||
// Change repository directory name. We must lock the local copy of the
|
||||
@@ -1849,14 +1852,14 @@ func ChangeRepositoryName(u *User, oldRepoName, newRepoName string) (err error)
|
||||
repoWorkingPool.CheckIn(com.ToStr(repo.ID))
|
||||
defer repoWorkingPool.CheckOut(com.ToStr(repo.ID))
|
||||
|
||||
newRepoPath := RepoPath(u.Name, newRepoName)
|
||||
newRepoPath := RepoPath(repo.Owner.Name, newRepoName)
|
||||
if err = os.Rename(repo.RepoPath(), newRepoPath); err != nil {
|
||||
return fmt.Errorf("rename repository directory: %v", err)
|
||||
}
|
||||
|
||||
wikiPath := repo.WikiPath()
|
||||
if com.IsExist(wikiPath) {
|
||||
if err = os.Rename(wikiPath, WikiPath(u.Name, newRepoName)); err != nil {
|
||||
if err = os.Rename(wikiPath, WikiPath(repo.Owner.Name, newRepoName)); err != nil {
|
||||
return fmt.Errorf("rename repository wiki: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -1868,7 +1871,7 @@ func ChangeRepositoryName(u *User, oldRepoName, newRepoName string) (err error)
|
||||
}
|
||||
|
||||
// If there was previously a redirect at this location, remove it.
|
||||
if err = deleteRepoRedirect(sess, u.ID, newRepoName); err != nil {
|
||||
if err = deleteRepoRedirect(sess, repo.OwnerID, newRepoName); err != nil {
|
||||
return fmt.Errorf("delete repo redirect: %v", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/unknwon/com"
|
||||
)
|
||||
|
||||
func TestRepo(t *testing.T) {
|
||||
@@ -142,29 +141,6 @@ func TestRepoAPIURL(t *testing.T) {
|
||||
assert.Equal(t, "https://try.gitea.io/api/v1/repos/user12/repo10", repo.APIURL())
|
||||
}
|
||||
|
||||
func TestTransferOwnership(t *testing.T) {
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
|
||||
doer := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
|
||||
repo := AssertExistsAndLoadBean(t, &Repository{ID: 3}).(*Repository)
|
||||
repo.Owner = AssertExistsAndLoadBean(t, &User{ID: repo.OwnerID}).(*User)
|
||||
assert.NoError(t, TransferOwnership(doer, "user2", repo))
|
||||
|
||||
transferredRepo := AssertExistsAndLoadBean(t, &Repository{ID: 3}).(*Repository)
|
||||
assert.EqualValues(t, 2, transferredRepo.OwnerID)
|
||||
|
||||
assert.False(t, com.IsExist(RepoPath("user3", "repo3")))
|
||||
assert.True(t, com.IsExist(RepoPath("user2", "repo3")))
|
||||
AssertExistsAndLoadBean(t, &Action{
|
||||
OpType: ActionTransferRepo,
|
||||
ActUserID: 2,
|
||||
RepoID: 3,
|
||||
Content: "user3/repo3",
|
||||
})
|
||||
|
||||
CheckConsistencyFor(t, &Repository{}, &User{}, &Team{})
|
||||
}
|
||||
|
||||
func TestUploadAvatar(t *testing.T) {
|
||||
|
||||
// Generate image
|
||||
|
||||
@@ -216,6 +216,21 @@ func NotifyWatchers(act *Action) error {
|
||||
return notifyWatchers(x, act)
|
||||
}
|
||||
|
||||
// NotifyWatchersActions creates batch of actions for every watcher.
|
||||
func NotifyWatchersActions(acts []*Action) error {
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, act := range acts {
|
||||
if err := notifyWatchers(sess, act); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
func watchIfAuto(e Engine, userID, repoID int64, isWrite bool) error {
|
||||
if !isWrite || !setting.Service.AutoWatchOnChanges {
|
||||
return nil
|
||||
|
||||
134
models/review.go
134
models/review.go
@@ -5,14 +5,12 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
"xorm.io/builder"
|
||||
"xorm.io/core"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// ReviewType defines the sort of feedback a review gives
|
||||
@@ -86,6 +84,11 @@ func (r *Review) loadReviewer(e Engine) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// LoadReviewer loads reviewer
|
||||
func (r *Review) LoadReviewer() error {
|
||||
return r.loadReviewer(x)
|
||||
}
|
||||
|
||||
func (r *Review) loadAttributes(e Engine) (err error) {
|
||||
if err = r.loadReviewer(e); err != nil {
|
||||
return
|
||||
@@ -101,54 +104,6 @@ func (r *Review) LoadAttributes() error {
|
||||
return r.loadAttributes(x)
|
||||
}
|
||||
|
||||
// Publish will send notifications / actions to participants for all code comments; parts are concurrent
|
||||
func (r *Review) Publish() error {
|
||||
return r.publish(x)
|
||||
}
|
||||
|
||||
func (r *Review) publish(e *xorm.Engine) error {
|
||||
if r.Type == ReviewTypePending || r.Type == ReviewTypeUnknown {
|
||||
return fmt.Errorf("review cannot be published if type is pending or unknown")
|
||||
}
|
||||
if r.Issue == nil {
|
||||
if err := r.loadIssue(e); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := r.Issue.loadRepo(e); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(r.CodeComments) == 0 {
|
||||
if err := r.loadCodeComments(e); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, lines := range r.CodeComments {
|
||||
for _, comments := range lines {
|
||||
for _, comment := range comments {
|
||||
go func(en *xorm.Engine, review *Review, comm *Comment) {
|
||||
sess := en.NewSession()
|
||||
defer sess.Close()
|
||||
opts := &CreateCommentOptions{
|
||||
Doer: comm.Poster,
|
||||
Issue: review.Issue,
|
||||
Repo: review.Issue.Repo,
|
||||
Type: comm.Type,
|
||||
Content: comm.Content,
|
||||
}
|
||||
if err := updateCommentInfos(sess, opts, comm); err != nil {
|
||||
log.Warn("updateCommentInfos: %v", err)
|
||||
}
|
||||
if err := sendCreateCommentAction(sess, opts, comm); err != nil {
|
||||
log.Warn("sendCreateCommentAction: %v", err)
|
||||
}
|
||||
}(e, r, comment)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getReviewByID(e Engine, id int64) (*Review, error) {
|
||||
review := new(Review)
|
||||
if has, err := e.ID(id).Get(review); err != nil {
|
||||
@@ -271,12 +226,79 @@ func GetCurrentReview(reviewer *User, issue *Issue) (*Review, error) {
|
||||
return getCurrentReview(x, reviewer, issue)
|
||||
}
|
||||
|
||||
// UpdateReview will update all cols of the given review in db
|
||||
func UpdateReview(r *Review) error {
|
||||
if _, err := x.ID(r.ID).AllCols().Update(r); err != nil {
|
||||
return err
|
||||
// ContentEmptyErr represents an content empty error
|
||||
type ContentEmptyErr struct {
|
||||
}
|
||||
|
||||
func (ContentEmptyErr) Error() string {
|
||||
return "Review content is empty"
|
||||
}
|
||||
|
||||
// IsContentEmptyErr returns true if err is a ContentEmptyErr
|
||||
func IsContentEmptyErr(err error) bool {
|
||||
_, ok := err.(ContentEmptyErr)
|
||||
return ok
|
||||
}
|
||||
|
||||
// SubmitReview creates a review out of the existing pending review or creates a new one if no pending review exist
|
||||
func SubmitReview(doer *User, issue *Issue, reviewType ReviewType, content string) (*Review, *Comment, error) {
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err := sess.Begin(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return nil
|
||||
|
||||
review, err := getCurrentReview(sess, doer, issue)
|
||||
if err != nil {
|
||||
if !IsErrReviewNotExist(err) {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if reviewType != ReviewTypeApprove && len(strings.TrimSpace(content)) == 0 {
|
||||
return nil, nil, ContentEmptyErr{}
|
||||
}
|
||||
|
||||
// No current review. Create a new one!
|
||||
review, err = createReview(sess, CreateReviewOptions{
|
||||
Type: reviewType,
|
||||
Issue: issue,
|
||||
Reviewer: doer,
|
||||
Content: content,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
} else {
|
||||
if err := review.loadCodeComments(sess); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if reviewType != ReviewTypeApprove && len(review.CodeComments) == 0 && len(strings.TrimSpace(content)) == 0 {
|
||||
return nil, nil, ContentEmptyErr{}
|
||||
}
|
||||
|
||||
review.Issue = issue
|
||||
review.Content = content
|
||||
review.Type = reviewType
|
||||
if _, err := sess.ID(review.ID).Cols("content, type").Update(review); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
comm, err := createComment(sess, &CreateCommentOptions{
|
||||
Type: CommentTypeReview,
|
||||
Doer: doer,
|
||||
Content: review.Content,
|
||||
Issue: issue,
|
||||
Repo: issue.Repo,
|
||||
ReviewID: review.ID,
|
||||
NoAction: true,
|
||||
})
|
||||
if err != nil || comm == nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
comm.Review = review
|
||||
return review, comm, sess.Commit()
|
||||
}
|
||||
|
||||
// PullReviewersWithType represents the type used to display a review overview
|
||||
|
||||
@@ -98,14 +98,6 @@ func TestCreateReview(t *testing.T) {
|
||||
AssertExistsAndLoadBean(t, &Review{Content: "New Review"})
|
||||
}
|
||||
|
||||
func TestUpdateReview(t *testing.T) {
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
review := AssertExistsAndLoadBean(t, &Review{ID: 1}).(*Review)
|
||||
review.Content = "Updated Review"
|
||||
assert.NoError(t, UpdateReview(review))
|
||||
AssertExistsAndLoadBean(t, &Review{ID: 1, Content: "Updated Review"})
|
||||
}
|
||||
|
||||
func TestGetReviewersByPullID(t *testing.T) {
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
|
||||
|
||||
Reference in New Issue
Block a user