|
|
@@ -7,11 +7,10 @@ import (
|
|
|
"time"
|
|
|
|
|
|
"github.com/cockroachdb/errors"
|
|
|
+ api "github.com/gogs/go-gogs-client"
|
|
|
"github.com/unknwon/com"
|
|
|
+ "gorm.io/gorm"
|
|
|
log "unknwon.dev/clog/v2"
|
|
|
- "xorm.io/xorm"
|
|
|
-
|
|
|
- api "github.com/gogs/go-gogs-client"
|
|
|
|
|
|
"gogs.io/gogs/internal/errutil"
|
|
|
"gogs.io/gogs/internal/markup"
|
|
|
@@ -50,47 +49,50 @@ type Comment struct {
|
|
|
ID int64
|
|
|
Type CommentType
|
|
|
PosterID int64
|
|
|
- Poster *User `xorm:"-" json:"-" gorm:"-"`
|
|
|
- IssueID int64 `xorm:"INDEX"`
|
|
|
- Issue *Issue `xorm:"-" json:"-" gorm:"-"`
|
|
|
+ Poster *User `gorm:"-" json:"-"`
|
|
|
+ IssueID int64 `gorm:"index"`
|
|
|
+ Issue *Issue `gorm:"-" json:"-"`
|
|
|
CommitID int64
|
|
|
Line int64
|
|
|
- Content string `xorm:"TEXT"`
|
|
|
- RenderedContent string `xorm:"-" json:"-" gorm:"-"`
|
|
|
+ Content string `gorm:"type:text"`
|
|
|
+ RenderedContent string `gorm:"-" json:"-"`
|
|
|
|
|
|
- Created time.Time `xorm:"-" json:"-" gorm:"-"`
|
|
|
+ Created time.Time `gorm:"-" json:"-"`
|
|
|
CreatedUnix int64
|
|
|
- Updated time.Time `xorm:"-" json:"-" gorm:"-"`
|
|
|
+ Updated time.Time `gorm:"-" json:"-"`
|
|
|
UpdatedUnix int64
|
|
|
|
|
|
// Reference issue in commit message
|
|
|
- CommitSHA string `xorm:"VARCHAR(40)"`
|
|
|
+ CommitSHA string `gorm:"type:varchar(40)"`
|
|
|
|
|
|
- Attachments []*Attachment `xorm:"-" json:"-" gorm:"-"`
|
|
|
+ Attachments []*Attachment `gorm:"-" json:"-"`
|
|
|
|
|
|
// For view issue page.
|
|
|
- ShowTag CommentTag `xorm:"-" json:"-" gorm:"-"`
|
|
|
+ ShowTag CommentTag `gorm:"-" json:"-"`
|
|
|
}
|
|
|
|
|
|
-func (c *Comment) BeforeInsert() {
|
|
|
- c.CreatedUnix = time.Now().Unix()
|
|
|
- c.UpdatedUnix = c.CreatedUnix
|
|
|
+func (c *Comment) BeforeCreate(tx *gorm.DB) error {
|
|
|
+ if c.CreatedUnix == 0 {
|
|
|
+ c.CreatedUnix = tx.NowFunc().Unix()
|
|
|
+ }
|
|
|
+ if c.UpdatedUnix == 0 {
|
|
|
+ c.UpdatedUnix = c.CreatedUnix
|
|
|
+ }
|
|
|
+ return nil
|
|
|
}
|
|
|
|
|
|
-func (c *Comment) BeforeUpdate() {
|
|
|
- c.UpdatedUnix = time.Now().Unix()
|
|
|
+func (c *Comment) BeforeUpdate(tx *gorm.DB) error {
|
|
|
+ c.UpdatedUnix = tx.NowFunc().Unix()
|
|
|
+ return nil
|
|
|
}
|
|
|
|
|
|
-func (c *Comment) AfterSet(colName string, _ xorm.Cell) {
|
|
|
- switch colName {
|
|
|
- case "created_unix":
|
|
|
- c.Created = time.Unix(c.CreatedUnix, 0).Local()
|
|
|
- case "updated_unix":
|
|
|
- c.Updated = time.Unix(c.UpdatedUnix, 0).Local()
|
|
|
- }
|
|
|
+func (c *Comment) AfterFind(tx *gorm.DB) error {
|
|
|
+ c.Created = time.Unix(c.CreatedUnix, 0).Local()
|
|
|
+ c.Updated = time.Unix(c.UpdatedUnix, 0).Local()
|
|
|
+ return nil
|
|
|
}
|
|
|
|
|
|
-func (c *Comment) loadAttributes(e Engine) (err error) {
|
|
|
+func (c *Comment) loadAttributes(tx *gorm.DB) (err error) {
|
|
|
if c.Poster == nil {
|
|
|
c.Poster, err = Handle.Users().GetByID(context.TODO(), c.PosterID)
|
|
|
if err != nil {
|
|
|
@@ -104,12 +106,12 @@ func (c *Comment) loadAttributes(e Engine) (err error) {
|
|
|
}
|
|
|
|
|
|
if c.Issue == nil {
|
|
|
- c.Issue, err = getRawIssueByID(e, c.IssueID)
|
|
|
+ c.Issue, err = getRawIssueByID(tx, c.IssueID)
|
|
|
if err != nil {
|
|
|
return errors.Newf("getIssueByID [%d]: %v", c.IssueID, err)
|
|
|
}
|
|
|
if c.Issue.Repo == nil {
|
|
|
- c.Issue.Repo, err = getRepositoryByID(e, c.Issue.RepoID)
|
|
|
+ c.Issue.Repo, err = getRepositoryByID(tx, c.Issue.RepoID)
|
|
|
if err != nil {
|
|
|
return errors.Newf("getRepositoryByID [%d]: %v", c.Issue.RepoID, err)
|
|
|
}
|
|
|
@@ -117,7 +119,7 @@ func (c *Comment) loadAttributes(e Engine) (err error) {
|
|
|
}
|
|
|
|
|
|
if c.Attachments == nil {
|
|
|
- c.Attachments, err = getAttachmentsByCommentID(e, c.ID)
|
|
|
+ c.Attachments, err = getAttachmentsByCommentID(tx, c.ID)
|
|
|
if err != nil {
|
|
|
return errors.Newf("getAttachmentsByCommentID [%d]: %v", c.ID, err)
|
|
|
}
|
|
|
@@ -127,7 +129,7 @@ func (c *Comment) loadAttributes(e Engine) (err error) {
|
|
|
}
|
|
|
|
|
|
func (c *Comment) LoadAttributes() error {
|
|
|
- return c.loadAttributes(x)
|
|
|
+ return c.loadAttributes(db)
|
|
|
}
|
|
|
|
|
|
func (c *Comment) HTMLURL() string {
|
|
|
@@ -163,9 +165,9 @@ func (c *Comment) EventTag() string {
|
|
|
|
|
|
// mailParticipants sends new comment emails to repository watchers
|
|
|
// and mentioned people.
|
|
|
-func (c *Comment) mailParticipants(e Engine, opType ActionType, issue *Issue) (err error) {
|
|
|
+func (c *Comment) mailParticipants(tx *gorm.DB, opType ActionType, issue *Issue) (err error) {
|
|
|
mentions := markup.FindAllMentions(c.Content)
|
|
|
- if err = updateIssueMentions(e, c.IssueID, mentions); err != nil {
|
|
|
+ if err = updateIssueMentions(tx, c.IssueID, mentions); err != nil {
|
|
|
return errors.Newf("UpdateIssueMentions [%d]: %v", c.IssueID, err)
|
|
|
}
|
|
|
|
|
|
@@ -184,7 +186,7 @@ func (c *Comment) mailParticipants(e Engine, opType ActionType, issue *Issue) (e
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err error) {
|
|
|
+func createComment(tx *gorm.DB, opts *CreateCommentOptions) (_ *Comment, err error) {
|
|
|
comment := &Comment{
|
|
|
Type: opts.Type,
|
|
|
PosterID: opts.Doer.ID,
|
|
|
@@ -195,7 +197,7 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
|
|
|
Line: opts.LineNum,
|
|
|
Content: opts.Content,
|
|
|
}
|
|
|
- if _, err = e.Insert(comment); err != nil {
|
|
|
+ if err = tx.Create(comment).Error; err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
|
|
|
@@ -216,14 +218,14 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
|
|
|
case CommentTypeComment:
|
|
|
act.OpType = ActionCommentIssue
|
|
|
|
|
|
- if _, err = e.Exec("UPDATE `issue` SET num_comments=num_comments+1 WHERE id=?", opts.Issue.ID); err != nil {
|
|
|
+ if err = tx.Exec("UPDATE `issue` SET num_comments=num_comments+1 WHERE id=?", opts.Issue.ID).Error; err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
|
|
|
// Check attachments
|
|
|
attachments := make([]*Attachment, 0, len(opts.Attachments))
|
|
|
for _, uuid := range opts.Attachments {
|
|
|
- attach, err := getAttachmentByUUID(e, uuid)
|
|
|
+ attach, err := getAttachmentByUUID(tx, uuid)
|
|
|
if err != nil {
|
|
|
if IsErrAttachmentNotExist(err) {
|
|
|
continue
|
|
|
@@ -236,8 +238,10 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
|
|
|
for i := range attachments {
|
|
|
attachments[i].IssueID = opts.Issue.ID
|
|
|
attachments[i].CommentID = comment.ID
|
|
|
- // No assign value could be 0, so ignore AllCols().
|
|
|
- if _, err = e.ID(attachments[i].ID).Update(attachments[i]); err != nil {
|
|
|
+ if err = tx.Model(attachments[i]).Where("id = ?", attachments[i].ID).Updates(map[string]any{
|
|
|
+ "issue_id": attachments[i].IssueID,
|
|
|
+ "comment_id": attachments[i].CommentID,
|
|
|
+ }).Error; err != nil {
|
|
|
return nil, errors.Newf("update attachment [%d]: %v", attachments[i].ID, err)
|
|
|
}
|
|
|
}
|
|
|
@@ -249,9 +253,9 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
|
|
|
}
|
|
|
|
|
|
if opts.Issue.IsPull {
|
|
|
- _, err = e.Exec("UPDATE `repository` SET num_closed_pulls=num_closed_pulls-1 WHERE id=?", opts.Repo.ID)
|
|
|
+ err = tx.Exec("UPDATE `repository` SET num_closed_pulls=num_closed_pulls-1 WHERE id=?", opts.Repo.ID).Error
|
|
|
} else {
|
|
|
- _, err = e.Exec("UPDATE `repository` SET num_closed_issues=num_closed_issues-1 WHERE id=?", opts.Repo.ID)
|
|
|
+ err = tx.Exec("UPDATE `repository` SET num_closed_issues=num_closed_issues-1 WHERE id=?", opts.Repo.ID).Error
|
|
|
}
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
|
@@ -264,38 +268,38 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
|
|
|
}
|
|
|
|
|
|
if opts.Issue.IsPull {
|
|
|
- _, err = e.Exec("UPDATE `repository` SET num_closed_pulls=num_closed_pulls+1 WHERE id=?", opts.Repo.ID)
|
|
|
+ err = tx.Exec("UPDATE `repository` SET num_closed_pulls=num_closed_pulls+1 WHERE id=?", opts.Repo.ID).Error
|
|
|
} else {
|
|
|
- _, err = e.Exec("UPDATE `repository` SET num_closed_issues=num_closed_issues+1 WHERE id=?", opts.Repo.ID)
|
|
|
+ err = tx.Exec("UPDATE `repository` SET num_closed_issues=num_closed_issues+1 WHERE id=?", opts.Repo.ID).Error
|
|
|
}
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if _, err = e.Exec("UPDATE `issue` SET updated_unix = ? WHERE id = ?", time.Now().Unix(), opts.Issue.ID); err != nil {
|
|
|
+ if err = tx.Exec("UPDATE `issue` SET updated_unix = ? WHERE id = ?", tx.NowFunc().Unix(), opts.Issue.ID).Error; err != nil {
|
|
|
return nil, errors.Newf("update issue 'updated_unix': %v", err)
|
|
|
}
|
|
|
|
|
|
// Notify watchers for whatever action comes in, ignore if no action type.
|
|
|
if act.OpType > 0 {
|
|
|
- if err = notifyWatchers(e, act); err != nil {
|
|
|
+ if err = notifyWatchers(tx, act); err != nil {
|
|
|
log.Error("notifyWatchers: %v", err)
|
|
|
}
|
|
|
- if err = comment.mailParticipants(e, act.OpType, opts.Issue); err != nil {
|
|
|
+ if err = comment.mailParticipants(tx, act.OpType, opts.Issue); err != nil {
|
|
|
log.Error("MailParticipants: %v", err)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- return comment, comment.loadAttributes(e)
|
|
|
+ return comment, comment.loadAttributes(tx)
|
|
|
}
|
|
|
|
|
|
-func createStatusComment(e *xorm.Session, doer *User, repo *Repository, issue *Issue) (*Comment, error) {
|
|
|
+func createStatusComment(tx *gorm.DB, doer *User, repo *Repository, issue *Issue) (*Comment, error) {
|
|
|
cmtType := CommentTypeClose
|
|
|
if !issue.IsClosed {
|
|
|
cmtType = CommentTypeReopen
|
|
|
}
|
|
|
- return createComment(e, &CreateCommentOptions{
|
|
|
+ return createComment(tx, &CreateCommentOptions{
|
|
|
Type: cmtType,
|
|
|
Doer: doer,
|
|
|
Repo: repo,
|
|
|
@@ -318,18 +322,12 @@ type CreateCommentOptions struct {
|
|
|
|
|
|
// CreateComment creates comment of issue or commit.
|
|
|
func CreateComment(opts *CreateCommentOptions) (comment *Comment, err error) {
|
|
|
- sess := x.NewSession()
|
|
|
- defer sess.Close()
|
|
|
- if err = sess.Begin(); err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
-
|
|
|
- comment, err = createComment(sess, opts)
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
-
|
|
|
- return comment, sess.Commit()
|
|
|
+ err = db.Transaction(func(tx *gorm.DB) error {
|
|
|
+ var err error
|
|
|
+ comment, err = createComment(tx, opts)
|
|
|
+ return err
|
|
|
+ })
|
|
|
+ return comment, err
|
|
|
}
|
|
|
|
|
|
// CreateIssueComment creates a plain issue comment.
|
|
|
@@ -367,14 +365,12 @@ func CreateRefComment(doer *User, repo *Repository, issue *Issue, content, commi
|
|
|
}
|
|
|
|
|
|
// Check if same reference from same commit has already existed.
|
|
|
- has, err := x.Get(&Comment{
|
|
|
- Type: CommentTypeCommitRef,
|
|
|
- IssueID: issue.ID,
|
|
|
- CommitSHA: commitSHA,
|
|
|
- })
|
|
|
+ var count int64
|
|
|
+ err := db.Model(new(Comment)).Where("type = ? AND issue_id = ? AND commit_sha = ?",
|
|
|
+ CommentTypeCommitRef, issue.ID, commitSHA).Count(&count).Error
|
|
|
if err != nil {
|
|
|
return errors.Newf("check reference comment: %v", err)
|
|
|
- } else if has {
|
|
|
+ } else if count > 0 {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
@@ -411,19 +407,20 @@ func (ErrCommentNotExist) NotFound() bool {
|
|
|
// GetCommentByID returns the comment by given ID.
|
|
|
func GetCommentByID(id int64) (*Comment, error) {
|
|
|
c := new(Comment)
|
|
|
- has, err := x.Id(id).Get(c)
|
|
|
+ err := db.Where("id = ?", id).First(c).Error
|
|
|
if err != nil {
|
|
|
+ if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
|
+ return nil, ErrCommentNotExist{args: map[string]any{"commentID": id}}
|
|
|
+ }
|
|
|
return nil, err
|
|
|
- } else if !has {
|
|
|
- return nil, ErrCommentNotExist{args: map[string]any{"commentID": id}}
|
|
|
}
|
|
|
return c, c.LoadAttributes()
|
|
|
}
|
|
|
|
|
|
// FIXME: use CommentList to improve performance.
|
|
|
-func loadCommentsAttributes(e Engine, comments []*Comment) (err error) {
|
|
|
+func loadCommentsAttributes(tx *gorm.DB, comments []*Comment) (err error) {
|
|
|
for i := range comments {
|
|
|
- if err = comments[i].loadAttributes(e); err != nil {
|
|
|
+ if err = comments[i].loadAttributes(tx); err != nil {
|
|
|
return errors.Newf("loadAttributes [%d]: %v", comments[i].ID, err)
|
|
|
}
|
|
|
}
|
|
|
@@ -431,53 +428,55 @@ func loadCommentsAttributes(e Engine, comments []*Comment) (err error) {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func getCommentsByIssueIDSince(e Engine, issueID, since int64) ([]*Comment, error) {
|
|
|
+func getCommentsByIssueIDSince(tx *gorm.DB, issueID, since int64) ([]*Comment, error) {
|
|
|
comments := make([]*Comment, 0, 10)
|
|
|
- sess := e.Where("issue_id = ?", issueID).Asc("created_unix")
|
|
|
+ query := tx.Where("issue_id = ?", issueID).Order("created_unix ASC")
|
|
|
if since > 0 {
|
|
|
- sess.And("updated_unix >= ?", since)
|
|
|
+ query = query.Where("updated_unix >= ?", since)
|
|
|
}
|
|
|
|
|
|
- if err := sess.Find(&comments); err != nil {
|
|
|
+ if err := query.Find(&comments).Error; err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
- return comments, loadCommentsAttributes(e, comments)
|
|
|
+ return comments, loadCommentsAttributes(tx, comments)
|
|
|
}
|
|
|
|
|
|
-func getCommentsByRepoIDSince(e Engine, repoID, since int64) ([]*Comment, error) {
|
|
|
+func getCommentsByRepoIDSince(tx *gorm.DB, repoID, since int64) ([]*Comment, error) {
|
|
|
comments := make([]*Comment, 0, 10)
|
|
|
- sess := e.Where("issue.repo_id = ?", repoID).Join("INNER", "issue", "issue.id = comment.issue_id").Asc("comment.created_unix")
|
|
|
+ query := tx.Joins("INNER JOIN issue ON issue.id = comment.issue_id").
|
|
|
+ Where("issue.repo_id = ?", repoID).
|
|
|
+ Order("comment.created_unix ASC")
|
|
|
if since > 0 {
|
|
|
- sess.And("comment.updated_unix >= ?", since)
|
|
|
+ query = query.Where("comment.updated_unix >= ?", since)
|
|
|
}
|
|
|
- if err := sess.Find(&comments); err != nil {
|
|
|
+ if err := query.Find(&comments).Error; err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
- return comments, loadCommentsAttributes(e, comments)
|
|
|
+ return comments, loadCommentsAttributes(tx, comments)
|
|
|
}
|
|
|
|
|
|
-func getCommentsByIssueID(e Engine, issueID int64) ([]*Comment, error) {
|
|
|
- return getCommentsByIssueIDSince(e, issueID, -1)
|
|
|
+func getCommentsByIssueID(tx *gorm.DB, issueID int64) ([]*Comment, error) {
|
|
|
+ return getCommentsByIssueIDSince(tx, issueID, -1)
|
|
|
}
|
|
|
|
|
|
// GetCommentsByIssueID returns all comments of an issue.
|
|
|
func GetCommentsByIssueID(issueID int64) ([]*Comment, error) {
|
|
|
- return getCommentsByIssueID(x, issueID)
|
|
|
+ return getCommentsByIssueID(db, issueID)
|
|
|
}
|
|
|
|
|
|
// GetCommentsByIssueIDSince returns a list of comments of an issue since a given time point.
|
|
|
func GetCommentsByIssueIDSince(issueID, since int64) ([]*Comment, error) {
|
|
|
- return getCommentsByIssueIDSince(x, issueID, since)
|
|
|
+ return getCommentsByIssueIDSince(db, issueID, since)
|
|
|
}
|
|
|
|
|
|
// GetCommentsByRepoIDSince returns a list of comments for all issues in a repo since a given time point.
|
|
|
func GetCommentsByRepoIDSince(repoID, since int64) ([]*Comment, error) {
|
|
|
- return getCommentsByRepoIDSince(x, repoID, since)
|
|
|
+ return getCommentsByRepoIDSince(db, repoID, since)
|
|
|
}
|
|
|
|
|
|
// UpdateComment updates information of comment.
|
|
|
func UpdateComment(doer *User, c *Comment, oldContent string) (err error) {
|
|
|
- if _, err = x.Id(c.ID).AllCols().Update(c); err != nil {
|
|
|
+ if err = db.Model(c).Where("id = ?", c.ID).Updates(c).Error; err != nil {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
@@ -511,24 +510,21 @@ func DeleteCommentByID(doer *User, id int64) error {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
- sess := x.NewSession()
|
|
|
- defer sess.Close()
|
|
|
- if err = sess.Begin(); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- if _, err = sess.ID(comment.ID).Delete(new(Comment)); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- if comment.Type == CommentTypeComment {
|
|
|
- if _, err = sess.Exec("UPDATE `issue` SET num_comments = num_comments - 1 WHERE id = ?", comment.IssueID); err != nil {
|
|
|
+ err = db.Transaction(func(tx *gorm.DB) error {
|
|
|
+ if err := tx.Where("id = ?", comment.ID).Delete(new(Comment)).Error; err != nil {
|
|
|
return err
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- if err = sess.Commit(); err != nil {
|
|
|
- return errors.Newf("commit: %v", err)
|
|
|
+ if comment.Type == CommentTypeComment {
|
|
|
+ if err := tx.Exec("UPDATE `issue` SET num_comments = num_comments - 1 WHERE id = ?", comment.IssueID).Error; err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+ })
|
|
|
+ if err != nil {
|
|
|
+ return errors.Newf("transaction: %v", err)
|
|
|
}
|
|
|
|
|
|
_, err = DeleteAttachmentsByComment(comment.ID, true)
|