diff options
author | Unknwon <u@gogs.io> | 2016-03-05 12:58:51 -0500 |
---|---|---|
committer | Unknwon <u@gogs.io> | 2016-03-05 12:58:51 -0500 |
commit | 414eb22ef97d664da790a348dab403961f365976 (patch) | |
tree | 80c897880fbcad21225c4faa9198e0d82db3dca3 /models/issue_comment.go | |
parent | a2f13eae559c71d35662b7767a8b8347a26eb53e (diff) | |
download | gitea-414eb22ef97d664da790a348dab403961f365976.tar.gz gitea-414eb22ef97d664da790a348dab403961f365976.zip |
#1597 fix activitity feeds for pull requests
Diffstat (limited to 'models/issue_comment.go')
-rw-r--r-- | models/issue_comment.go | 311 |
1 files changed, 311 insertions, 0 deletions
diff --git a/models/issue_comment.go b/models/issue_comment.go new file mode 100644 index 0000000000..9ffad62fe6 --- /dev/null +++ b/models/issue_comment.go @@ -0,0 +1,311 @@ +// Copyright 2016 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 ( + "fmt" + "strings" + "time" + + "github.com/Unknwon/com" + "github.com/go-xorm/xorm" + + "github.com/gogits/gogs/modules/log" +) + +// CommentType defines whether a comment is just a simple comment, an action (like close) or a reference. +type CommentType int + +const ( + // Plain comment, can be associated with a commit (CommitID > 0) and a line (LineNum > 0) + COMMENT_TYPE_COMMENT CommentType = iota + COMMENT_TYPE_REOPEN + COMMENT_TYPE_CLOSE + + // References. + COMMENT_TYPE_ISSUE_REF + // Reference from a commit (not part of a pull request) + COMMENT_TYPE_COMMIT_REF + // Reference from a comment + COMMENT_TYPE_COMMENT_REF + // Reference from a pull request + COMMENT_TYPE_PULL_REF +) + +type CommentTag int + +const ( + COMMENT_TAG_NONE CommentTag = iota + COMMENT_TAG_POSTER + COMMENT_TAG_ADMIN + COMMENT_TAG_OWNER +) + +// Comment represents a comment in commit and issue page. +type Comment struct { + ID int64 `xorm:"pk autoincr"` + Type CommentType + PosterID int64 + Poster *User `xorm:"-"` + IssueID int64 `xorm:"INDEX"` + CommitID int64 + Line int64 + Content string `xorm:"TEXT"` + RenderedContent string `xorm:"-"` + Created time.Time `xorm:"CREATED"` + + // Reference issue in commit message + CommitSHA string `xorm:"VARCHAR(40)"` + + Attachments []*Attachment `xorm:"-"` + + // For view issue page. + ShowTag CommentTag `xorm:"-"` +} + +func (c *Comment) AfterSet(colName string, _ xorm.Cell) { + var err error + switch colName { + case "id": + c.Attachments, err = GetAttachmentsByCommentID(c.ID) + if err != nil { + log.Error(3, "GetAttachmentsByCommentID[%d]: %v", c.ID, err) + } + + case "poster_id": + c.Poster, err = GetUserByID(c.PosterID) + if err != nil { + if IsErrUserNotExist(err) { + c.PosterID = -1 + c.Poster = NewFakeUser() + } else { + log.Error(3, "GetUserByID[%d]: %v", c.ID, err) + } + } + case "created": + c.Created = regulateTimeZone(c.Created) + } +} + +func (c *Comment) AfterDelete() { + _, err := DeleteAttachmentsByComment(c.ID, true) + + if err != nil { + log.Info("Could not delete files for comment %d on issue #%d: %s", c.ID, c.IssueID, err) + } +} + +// HashTag returns unique hash tag for comment. +func (c *Comment) HashTag() string { + return "issuecomment-" + com.ToStr(c.ID) +} + +// EventTag returns unique event hash tag for comment. +func (c *Comment) EventTag() string { + return "event-" + com.ToStr(c.ID) +} + +func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err error) { + comment := &Comment{ + Type: opts.Type, + PosterID: opts.Doer.Id, + IssueID: opts.Issue.ID, + CommitID: opts.CommitID, + CommitSHA: opts.CommitSHA, + Line: opts.LineNum, + Content: opts.Content, + } + if _, err = e.Insert(comment); err != nil { + return nil, err + } + + // Compose comment action, could be plain comment, close or reopen issue. + // This object will be used to notify watchers in the end of function. + act := &Action{ + ActUserID: opts.Doer.Id, + ActUserName: opts.Doer.Name, + ActEmail: opts.Doer.Email, + Content: fmt.Sprintf("%d|%s", opts.Issue.Index, strings.Split(opts.Content, "\n")[0]), + RepoID: opts.Repo.ID, + RepoUserName: opts.Repo.Owner.Name, + RepoName: opts.Repo.Name, + IsPrivate: opts.Repo.IsPrivate, + } + + // Check comment type. + switch opts.Type { + case COMMENT_TYPE_COMMENT: + act.OpType = ACTION_COMMENT_ISSUE + + if _, err = e.Exec("UPDATE `issue` SET num_comments=num_comments+1 WHERE id=?", opts.Issue.ID); err != nil { + return nil, err + } + + // Check attachments + attachments := make([]*Attachment, 0, len(opts.Attachments)) + for _, uuid := range opts.Attachments { + attach, err := getAttachmentByUUID(e, uuid) + if err != nil { + if IsErrAttachmentNotExist(err) { + continue + } + return nil, fmt.Errorf("getAttachmentByUUID[%s]: %v", uuid, err) + } + attachments = append(attachments, attach) + } + + 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 { + return nil, fmt.Errorf("update attachment[%d]: %v", attachments[i].ID, err) + } + } + + case COMMENT_TYPE_REOPEN: + act.OpType = ACTION_REOPEN_ISSUE + if opts.Issue.IsPull { + act.OpType = ACTION_REOPEN_PULL_REQUEST + } + + if opts.Issue.IsPull { + _, err = e.Exec("UPDATE `repository` SET num_closed_pulls=num_closed_pulls-1 WHERE id=?", opts.Repo.ID) + } else { + _, err = e.Exec("UPDATE `repository` SET num_closed_issues=num_closed_issues-1 WHERE id=?", opts.Repo.ID) + } + if err != nil { + return nil, err + } + case COMMENT_TYPE_CLOSE: + act.OpType = ACTION_CLOSE_ISSUE + if opts.Issue.IsPull { + act.OpType = ACTION_CLOSE_PULL_REQUEST + } + + if opts.Issue.IsPull { + _, err = e.Exec("UPDATE `repository` SET num_closed_pulls=num_closed_pulls+1 WHERE id=?", opts.Repo.ID) + } else { + _, err = e.Exec("UPDATE `repository` SET num_closed_issues=num_closed_issues+1 WHERE id=?", opts.Repo.ID) + } + if err != nil { + return nil, err + } + } + + // Notify watchers for whatever action comes in + if err = notifyWatchers(e, act); err != nil { + return nil, fmt.Errorf("notifyWatchers: %v", err) + } + + return comment, nil +} + +func createStatusComment(e *xorm.Session, doer *User, repo *Repository, issue *Issue) (*Comment, error) { + cmtType := COMMENT_TYPE_CLOSE + if !issue.IsClosed { + cmtType = COMMENT_TYPE_REOPEN + } + return createComment(e, &CreateCommentOptions{ + Type: cmtType, + Doer: doer, + Repo: repo, + Issue: issue, + }) +} + +type CreateCommentOptions struct { + Type CommentType + Doer *User + Repo *Repository + Issue *Issue + + CommitID int64 + CommitSHA string + LineNum int64 + Content string + Attachments []string // UUIDs of attachments +} + +// CreateComment creates comment of issue or commit. +func CreateComment(opts *CreateCommentOptions) (comment *Comment, err error) { + sess := x.NewSession() + defer sessionRelease(sess) + if err = sess.Begin(); err != nil { + return nil, err + } + + comment, err = createComment(sess, opts) + if err != nil { + return nil, err + } + + return comment, sess.Commit() +} + +// CreateIssueComment creates a plain issue comment. +func CreateIssueComment(doer *User, repo *Repository, issue *Issue, content string, attachments []string) (*Comment, error) { + return CreateComment(&CreateCommentOptions{ + Type: COMMENT_TYPE_COMMENT, + Doer: doer, + Repo: repo, + Issue: issue, + Content: content, + Attachments: attachments, + }) +} + +// CreateRefComment creates a commit reference comment to issue. +func CreateRefComment(doer *User, repo *Repository, issue *Issue, content, commitSHA string) error { + if len(commitSHA) == 0 { + return fmt.Errorf("cannot create reference with empty commit SHA") + } + + // Check if same reference from same commit has already existed. + has, err := x.Get(&Comment{ + Type: COMMENT_TYPE_COMMIT_REF, + IssueID: issue.ID, + CommitSHA: commitSHA, + }) + if err != nil { + return fmt.Errorf("check reference comment: %v", err) + } else if has { + return nil + } + + _, err = CreateComment(&CreateCommentOptions{ + Type: COMMENT_TYPE_COMMIT_REF, + Doer: doer, + Repo: repo, + Issue: issue, + CommitSHA: commitSHA, + Content: content, + }) + return err +} + +// GetCommentByID returns the comment by given ID. +func GetCommentByID(id int64) (*Comment, error) { + c := new(Comment) + has, err := x.Id(id).Get(c) + if err != nil { + return nil, err + } else if !has { + return nil, ErrCommentNotExist{id} + } + return c, nil +} + +// GetCommentsByIssueID returns all comments of issue by given ID. +func GetCommentsByIssueID(issueID int64) ([]*Comment, error) { + comments := make([]*Comment, 0, 10) + return comments, x.Where("issue_id=?", issueID).Asc("created").Find(&comments) +} + +// UpdateComment updates information of comment. +func UpdateComment(c *Comment) error { + _, err := x.Id(c.ID).AllCols().Update(c) + return err +} |