diff options
Diffstat (limited to 'models/issue.go')
-rw-r--r-- | models/issue.go | 566 |
1 files changed, 409 insertions, 157 deletions
diff --git a/models/issue.go b/models/issue.go index 37fc125c..1e49fda7 100644 --- a/models/issue.go +++ b/models/issue.go @@ -8,7 +8,6 @@ import ( "bytes" "errors" "fmt" - "html/template" "io" "mime/multipart" "os" @@ -27,7 +26,6 @@ import ( ) var ( - ErrIssueNotExist = errors.New("Issue does not exist") ErrWrongIssueCounter = errors.New("Invalid number of issues for this milestone") ErrAttachmentNotLinked = errors.New("Attachment does not belong to this issue") ErrMissingIssueNumber = errors.New("No issue number specified") @@ -57,11 +55,30 @@ type Issue struct { Deadline time.Time Created time.Time `xorm:"CREATED"` Updated time.Time `xorm:"UPDATED"` + + Attachments []*Attachment `xorm:"-"` + Comments []*Comment `xorm:"-"` +} + +// HashTag returns unique hash tag for issue. +func (i *Issue) HashTag() string { + return "issue-" + com.ToStr(i.ID) } func (i *Issue) AfterSet(colName string, _ xorm.Cell) { var err error switch colName { + case "id": + i.Attachments, err = GetAttachmentsByIssueID(i.ID) + if err != nil { + log.Error(3, "GetAttachmentsByIssueID[%d]: %v", i.ID, err) + } + + i.Comments, err = GetCommentsByIssueID(i.ID) + if err != nil { + log.Error(3, "GetCommentsByIssueID[%d]: %v", i.ID, err) + } + case "milestone_id": if i.MilestoneID == 0 { return @@ -69,7 +86,7 @@ func (i *Issue) AfterSet(colName string, _ xorm.Cell) { i.Milestone, err = GetMilestoneByID(i.MilestoneID) if err != nil { - log.Error(3, "GetMilestoneById: %v", err) + log.Error(3, "GetMilestoneById[%d]: %v", i.ID, err) } case "assignee_id": if i.AssigneeID == 0 { @@ -78,15 +95,21 @@ func (i *Issue) AfterSet(colName string, _ xorm.Cell) { i.Assignee, err = GetUserByID(i.AssigneeID) if err != nil { - log.Error(3, "GetUserByID: %v", err) + log.Error(3, "GetUserByID[%d]: %v", i.ID, err) } } } +// IsPoster returns true if given user by ID is the poster. +func (i *Issue) IsPoster(uid int64) bool { + return i.PosterID == uid +} + func (i *Issue) GetPoster() (err error) { i.Poster, err = GetUserByID(i.PosterID) if IsErrUserNotExist(err) { - i.Poster = &User{Name: "FakeUser"} + i.PosterID = -1 + i.Poster = NewFakeUser() return nil } return err @@ -101,13 +124,23 @@ func (i *Issue) HasLabel(labelID int64) bool { return i.hasLabel(x, labelID) } -func (i *Issue) addLabel(e Engine, labelID int64) error { - return newIssueLabel(e, i.ID, labelID) +func (i *Issue) addLabel(e *xorm.Session, label *Label) error { + return newIssueLabel(e, i, label) } // AddLabel adds new label to issue by given ID. -func (i *Issue) AddLabel(labelID int64) error { - return i.addLabel(x, labelID) +func (i *Issue) AddLabel(label *Label) (err error) { + sess := x.NewSession() + defer sessionRelease(sess) + if err = sess.Begin(); err != nil { + return err + } + + if err = i.addLabel(sess, label); err != nil { + return err + } + + return sess.Commit() } func (i *Issue) getLabels(e Engine) (err error) { @@ -127,13 +160,43 @@ func (i *Issue) GetLabels() error { return i.getLabels(x) } -func (i *Issue) removeLabel(e Engine, labelID int64) error { - return deleteIssueLabel(e, i.ID, labelID) +func (i *Issue) removeLabel(e *xorm.Session, label *Label) error { + return deleteIssueLabel(e, i, label) } // RemoveLabel removes a label from issue by given ID. -func (i *Issue) RemoveLabel(labelID int64) error { - return i.removeLabel(x, labelID) +func (i *Issue) RemoveLabel(label *Label) (err error) { + sess := x.NewSession() + defer sessionRelease(sess) + if err = sess.Begin(); err != nil { + return err + } + + if err = i.removeLabel(sess, label); err != nil { + return err + } + + return sess.Commit() +} + +func (i *Issue) ClearLabels() (err error) { + sess := x.NewSession() + defer sessionRelease(sess) + if err = sess.Begin(); err != nil { + return err + } + + if err = i.getLabels(sess); err != nil { + return err + } + + for idx := range i.Labels { + if err = i.removeLabel(sess, i.Labels[idx]); err != nil { + return err + } + } + + return sess.Commit() } func (i *Issue) GetAssignee() (err error) { @@ -148,17 +211,64 @@ func (i *Issue) GetAssignee() (err error) { return err } -func (i *Issue) Attachments() []*Attachment { - a, _ := GetAttachmentsForIssue(i.ID) - return a +// ReadBy sets issue to be read by given user. +func (i *Issue) ReadBy(uid int64) error { + return UpdateIssueUserByRead(uid, i.ID) } -func (i *Issue) AfterDelete() { - _, err := DeleteAttachmentsByIssue(i.ID, true) +func (i *Issue) changeStatus(e *xorm.Session, doer *User, isClosed bool) (err error) { + if i.IsClosed == isClosed { + return nil + } + i.IsClosed = isClosed + + if err = updateIssue(e, i); err != nil { + return err + } else if err = updateIssueUsersByStatus(e, i.ID, isClosed); err != nil { + return err + } - if err != nil { - log.Info("Could not delete files for issue #%d: %s", i.ID, err) + // Update labels. + if err = i.getLabels(e); err != nil { + return err } + for idx := range i.Labels { + if i.IsClosed { + i.Labels[idx].NumClosedIssues++ + } else { + i.Labels[idx].NumClosedIssues-- + } + if err = updateLabel(e, i.Labels[idx]); err != nil { + return err + } + } + + // Update milestone. + if err = changeMilestoneIssueStats(e, i); err != nil { + return err + } + + // New action comment. + if _, err = createStatusComment(e, doer, i.Repo, i); err != nil { + return err + } + + return nil +} + +// ChangeStatus changes issue status to open/closed. +func (i *Issue) ChangeStatus(doer *User, isClosed bool) (err error) { + sess := x.NewSession() + defer sessionRelease(sess) + if err = sess.Begin(); err != nil { + return err + } + + if err = i.changeStatus(sess, doer, isClosed); err != nil { + return err + } + + return sess.Commit() } // CreateIssue creates new issue with labels for repository. @@ -188,10 +298,20 @@ func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, uuids []string) return err } + var label *Label for _, id := range labelIDs { - if err = issue.addLabel(sess, id); err != nil { + if id == 0 { + continue + } + + label, err = getLabelByID(sess, id) + if err != nil { + return err + } + if err = issue.addLabel(sess, label); err != nil { return fmt.Errorf("addLabel: %v", err) } + } if issue.MilestoneID > 0 { @@ -255,25 +375,28 @@ func GetIssueByRef(ref string) (issue *Issue, err error) { } // GetIssueByIndex returns issue by given index in repository. -func GetIssueByIndex(rid, index int64) (*Issue, error) { - issue := &Issue{RepoID: rid, Index: index} +func GetIssueByIndex(repoID, index int64) (*Issue, error) { + issue := &Issue{ + RepoID: repoID, + Index: index, + } has, err := x.Get(issue) if err != nil { return nil, err } else if !has { - return nil, ErrIssueNotExist + return nil, ErrIssueNotExist{0, repoID, index} } return issue, nil } -// GetIssueById returns an issue by ID. -func GetIssueById(id int64) (*Issue, error) { - issue := &Issue{ID: id} - has, err := x.Get(issue) +// GetIssueByID returns an issue by given ID. +func GetIssueByID(id int64) (*Issue, error) { + issue := new(Issue) + has, err := x.Id(id).Get(issue) if err != nil { return nil, err } else if !has { - return nil, ErrIssueNotExist + return nil, ErrIssueNotExist{id, 0, 0} } return issue, nil } @@ -516,7 +639,7 @@ func parseCountResult(results []map[string][]byte) int64 { } // GetIssueStats returns issue statistic information by given conditions. -func GetIssueStats(repoID, uid, labelID, milestoneID int64, isShowClosed bool, filterMode int) *IssueStats { +func GetIssueStats(repoID, uid, labelID, milestoneID, assigneeID int64, isShowClosed bool, filterMode int) *IssueStats { stats := &IssueStats{} // issue := new(Issue) @@ -529,20 +652,16 @@ func GetIssueStats(repoID, uid, labelID, milestoneID int64, isShowClosed bool, f if milestoneID > 0 { baseCond += " AND issue.milestone_id=" + com.ToStr(milestoneID) } + if assigneeID > 0 { + baseCond += " AND assignee_id=" + com.ToStr(assigneeID) + } switch filterMode { - case FM_ALL: + case FM_ALL, FM_ASSIGN: resutls, _ := x.Query(queryStr+baseCond, repoID, false) stats.OpenCount = parseCountResult(resutls) resutls, _ = x.Query(queryStr+baseCond, repoID, true) stats.ClosedCount = parseCountResult(resutls) - case FM_ASSIGN: - baseCond += " AND assignee_id=?" - resutls, _ := x.Query(queryStr+baseCond, repoID, false, uid) - stats.OpenCount = parseCountResult(resutls) - resutls, _ = x.Query(queryStr+baseCond, repoID, true, uid) - stats.ClosedCount = parseCountResult(resutls) - case FM_CREATE: baseCond += " AND poster_id=?" resutls, _ := x.Query(queryStr+baseCond, repoID, false, uid) @@ -580,45 +699,49 @@ func UpdateIssue(issue *Issue) error { return updateIssue(x, issue) } -// UpdateIssueUserByStatus updates issue-user pairs by issue status. -func UpdateIssueUserPairsByStatus(iid int64, isClosed bool) error { - rawSql := "UPDATE `issue_user` SET is_closed = ? WHERE issue_id = ?" - _, err := x.Exec(rawSql, isClosed, iid) +func updateIssueUsersByStatus(e Engine, issueID int64, isClosed bool) error { + _, err := e.Exec("UPDATE `issue_user` SET is_closed=? WHERE issue_id=?", isClosed, issueID) return err } -func updateIssueUserByAssignee(e *xorm.Session, issueID, assigneeID int64) (err error) { - if _, err = e.Exec("UPDATE `issue_user` SET is_assigned=? WHERE issue_id=?", false, issueID); err != nil { +// UpdateIssueUsersByStatus updates issue-user relations by issue status. +func UpdateIssueUsersByStatus(issueID int64, isClosed bool) error { + return updateIssueUsersByStatus(x, issueID, isClosed) +} + +func updateIssueUserByAssignee(e *xorm.Session, issue *Issue) (err error) { + if _, err = e.Exec("UPDATE `issue_user` SET is_assigned=? WHERE issue_id=?", false, issue.ID); err != nil { return err } // Assignee ID equals to 0 means clear assignee. - if assigneeID == 0 { - return nil + if issue.AssigneeID > 0 { + if _, err = e.Exec("UPDATE `issue_user` SET is_assigned=? WHERE uid=? AND issue_id=?", true, issue.AssigneeID, issue.ID); err != nil { + return err + } } - _, err = e.Exec("UPDATE `issue_user` SET is_assigned=? WHERE uid=? AND issue_id=?", true, assigneeID, issueID) - return err + + return updateIssue(e, issue) } // UpdateIssueUserByAssignee updates issue-user relation for assignee. -func UpdateIssueUserByAssignee(issueID, assigneeID int64) (err error) { +func UpdateIssueUserByAssignee(issue *Issue) (err error) { sess := x.NewSession() defer sessionRelease(sess) if err = sess.Begin(); err != nil { return err } - if err = updateIssueUserByAssignee(sess, issueID, assigneeID); err != nil { + if err = updateIssueUserByAssignee(sess, issue); err != nil { return err } return sess.Commit() } -// UpdateIssueUserPairByRead updates issue-user pair for reading. -func UpdateIssueUserPairByRead(uid, iid int64) error { - rawSql := "UPDATE `issue_user` SET is_read = ? WHERE uid = ? AND issue_id = ?" - _, err := x.Exec(rawSql, true, uid, iid) +// UpdateIssueUserByRead updates issue-user relation for reading. +func UpdateIssueUserByRead(uid, issueID int64) error { + _, err := x.Exec("UPDATE `issue_user` SET is_read=? WHERE uid=? AND issue_id=?", true, uid, issueID) return err } @@ -723,10 +846,14 @@ func GetLabelsByIssueID(issueID int64) ([]*Label, error) { return getLabelsByIssueID(x, issueID) } +func updateLabel(e Engine, l *Label) error { + _, err := e.Id(l.ID).AllCols().Update(l) + return err +} + // UpdateLabel updates label information. func UpdateLabel(l *Label) error { - _, err := x.Id(l.ID).AllCols().Update(l) - return err + return updateLabel(x, l) } // DeleteLabel delete a label of given repository. @@ -777,21 +904,34 @@ func HasIssueLabel(issueID, labelID int64) bool { return hasIssueLabel(x, issueID, labelID) } -func newIssueLabel(e Engine, issueID, labelID int64) error { - if issueID == 0 || labelID == 0 { - return nil +func newIssueLabel(e *xorm.Session, issue *Issue, label *Label) (err error) { + if _, err = e.Insert(&IssueLabel{ + IssueID: issue.ID, + LabelID: label.ID, + }); err != nil { + return err } - _, err := e.Insert(&IssueLabel{ - IssueID: issueID, - LabelID: labelID, - }) - return err + label.NumIssues++ + if issue.IsClosed { + label.NumClosedIssues++ + } + return updateLabel(e, label) } // NewIssueLabel creates a new issue-label relation. -func NewIssueLabel(issueID, labelID int64) error { - return newIssueLabel(x, issueID, labelID) +func NewIssueLabel(issue *Issue, label *Label) (err error) { + sess := x.NewSession() + defer sessionRelease(sess) + if err = sess.Begin(); err != nil { + return err + } + + if err = newIssueLabel(sess, issue, label); err != nil { + return err + } + + return sess.Commit() } func getIssueLabels(e Engine, issueID int64) ([]*IssueLabel, error) { @@ -804,17 +944,34 @@ func GetIssueLabels(issueID int64) ([]*IssueLabel, error) { return getIssueLabels(x, issueID) } -func deleteIssueLabel(e Engine, issueID, labelID int64) error { - _, err := e.Delete(&IssueLabel{ - IssueID: issueID, - LabelID: labelID, - }) - return err +func deleteIssueLabel(e *xorm.Session, issue *Issue, label *Label) (err error) { + if _, err = e.Delete(&IssueLabel{ + IssueID: issue.ID, + LabelID: label.ID, + }); err != nil { + return err + } + + label.NumIssues-- + if issue.IsClosed { + label.NumClosedIssues-- + } + return updateLabel(e, label) } // DeleteIssueLabel deletes issue-label relation. -func DeleteIssueLabel(issueID, labelID int64) error { - return deleteIssueLabel(x, issueID, labelID) +func DeleteIssueLabel(issue *Issue, label *Label) (err error) { + sess := x.NewSession() + defer sessionRelease(sess) + if err = sess.Begin(); err != nil { + return err + } + + if err = deleteIssueLabel(sess, issue, label); err != nil { + return err + } + + return sess.Commit() } // _____ .__.__ __ @@ -888,7 +1045,7 @@ func NewMilestone(m *Milestone) (err error) { func getMilestoneByID(e Engine, id int64) (*Milestone, error) { m := &Milestone{ID: id} - has, err := x.Get(m) + has, err := e.Get(m) if err != nil { return nil, err } else if !has { @@ -992,14 +1149,12 @@ func ChangeMilestoneStatus(m *Milestone, isClosed bool) (err error) { return sess.Commit() } -// ChangeMilestoneIssueStats updates the open/closed issues counter and progress -// for the milestone associated witht the given issue. -func ChangeMilestoneIssueStats(issue *Issue) error { +func changeMilestoneIssueStats(e *xorm.Session, issue *Issue) error { if issue.MilestoneID == 0 { return nil } - m, err := GetMilestoneByID(issue.MilestoneID) + m, err := getMilestoneByID(e, issue.MilestoneID) if err != nil { return err } @@ -1012,7 +1167,23 @@ func ChangeMilestoneIssueStats(issue *Issue) error { m.NumClosedIssues-- } - return UpdateMilestone(m) + return updateMilestone(e, m) +} + +// ChangeMilestoneIssueStats updates the open/closed issues counter and progress +// for the milestone associated witht the given issue. +func ChangeMilestoneIssueStats(issue *Issue) (err error) { + sess := x.NewSession() + defer sessionRelease(sess) + if err = sess.Begin(); err != nil { + return err + } + + if err = changeMilestoneIssueStats(sess, issue); err != nil { + return err + } + + return sess.Commit() } func changeMilestoneAssign(e *xorm.Session, oldMid int64, issue *Issue) error { @@ -1035,7 +1206,7 @@ func changeMilestoneAssign(e *xorm.Session, oldMid int64, issue *Issue) error { } if issue.MilestoneID > 0 { - m, err := GetMilestoneByID(issue.MilestoneID) + m, err := getMilestoneByID(e, issue.MilestoneID) if err != nil { return err } @@ -1056,7 +1227,7 @@ func changeMilestoneAssign(e *xorm.Session, oldMid int64, issue *Issue) error { } } - return nil + return updateIssue(e, issue) } // ChangeMilestoneAssign changes assignment of milestone for issue. @@ -1070,7 +1241,6 @@ func ChangeMilestoneAssign(oldMid int64, issue *Issue) (err error) { if err = changeMilestoneAssign(sess, oldMid, issue); err != nil { return err } - return sess.Commit() } @@ -1130,107 +1300,193 @@ const ( COMMENT_TYPE_CLOSE // References. - COMMENT_TYPE_ISSUE - // Reference from some commit (not part of a pull request) - COMMENT_TYPE_COMMIT - // Reference from some pull request - COMMENT_TYPE_PULL + 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 - Type CommentType - PosterId int64 - Poster *User `xorm:"-"` - IssueId int64 - CommitId int64 - Line int64 - Content string `xorm:"TEXT"` - Created time.Time `xorm:"CREATED"` + 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"` + + Attachments []*Attachment `xorm:"-"` + + // For view issue page. + ShowTag CommentTag `xorm:"-"` } -// CreateComment creates comment of issue or commit. -func CreateComment(userId, repoId, issueId, commitId, line int64, cmtType CommentType, content string, attachments []int64) (*Comment, error) { - sess := x.NewSession() - defer sessionRelease(sess) - if err := sess.Begin(); err != nil { - return nil, 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 (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) + } - comment := &Comment{PosterId: userId, Type: cmtType, IssueId: issueId, - CommitId: commitId, Line: line, Content: content} + 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) + } + } + } +} - if _, err := sess.Insert(comment); err != nil { +func createComment(e *xorm.Session, u *User, repo *Repository, issue *Issue, commitID, line int64, cmtType CommentType, content string, uuids []string) (_ *Comment, err error) { + comment := &Comment{ + PosterID: u.Id, + Type: cmtType, + IssueID: issue.ID, + CommitID: commitID, + Line: line, + Content: content, + } + if _, err = e.Insert(comment); err != nil { return nil, err } // Check comment type. switch cmtType { case COMMENT_TYPE_COMMENT: - rawSql := "UPDATE `issue` SET num_comments = num_comments + 1 WHERE id = ?" - if _, err := sess.Exec(rawSql, issueId); err != nil { + if _, err = e.Exec("UPDATE `issue` SET num_comments=num_comments+1 WHERE id=?", issue.ID); err != nil { return nil, err } - if len(attachments) > 0 { - rawSql = "UPDATE `attachment` SET comment_id = ? WHERE id IN (?)" - - astrs := make([]string, 0, len(attachments)) - - for _, a := range attachments { - astrs = append(astrs, strconv.FormatInt(a, 10)) + // Check attachments. + attachments := make([]*Attachment, 0, len(uuids)) + for _, uuid := range uuids { + 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) + } - if _, err := sess.Exec(rawSql, comment.Id, strings.Join(astrs, ",")); err != nil { - return nil, err + for i := range attachments { + attachments[i].IssueID = 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) } } + + // Notify watchers. + act := &Action{ + ActUserID: u.Id, + ActUserName: u.LowerName, + ActEmail: u.Email, + OpType: COMMENT_ISSUE, + Content: fmt.Sprintf("%d|%s", issue.Index, strings.Split(content, "\n")[0]), + RepoID: repo.ID, + RepoUserName: repo.Owner.LowerName, + RepoName: repo.LowerName, + IsPrivate: repo.IsPrivate, + } + if err = notifyWatchers(e, act); err != nil { + return nil, err + } + case COMMENT_TYPE_REOPEN: - rawSql := "UPDATE `repository` SET num_closed_issues = num_closed_issues - 1 WHERE id = ?" - if _, err := sess.Exec(rawSql, repoId); err != nil { + if _, err = e.Exec("UPDATE `repository` SET num_closed_issues=num_closed_issues-1 WHERE id=?", repo.ID); err != nil { return nil, err } case COMMENT_TYPE_CLOSE: - rawSql := "UPDATE `repository` SET num_closed_issues = num_closed_issues + 1 WHERE id = ?" - if _, err := sess.Exec(rawSql, repoId); err != nil { + if _, err = e.Exec("UPDATE `repository` SET num_closed_issues=num_closed_issues+1 WHERE id=?", repo.ID); err != nil { return nil, err } } - return comment, sess.Commit() + return comment, nil } -// GetCommentById returns the comment with the given id -func GetCommentById(commentId int64) (*Comment, error) { - c := &Comment{Id: commentId} - _, err := x.Get(c) +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, doer, repo, issue, 0, 0, cmtType, "", nil) +} - return c, err +// CreateComment creates comment of issue or commit. +func CreateComment(doer *User, repo *Repository, issue *Issue, commitID, line int64, cmtType CommentType, content string, attachments []string) (comment *Comment, err error) { + sess := x.NewSession() + defer sessionRelease(sess) + if err = sess.Begin(); err != nil { + return nil, err + } + + comment, err = createComment(sess, doer, repo, issue, commitID, line, cmtType, content, attachments) + if err != nil { + return nil, err + } + + return comment, sess.Commit() } -func (c *Comment) ContentHtml() template.HTML { - return template.HTML(c.Content) +// CreateIssueComment creates a plain issue comment. +func CreateIssueComment(doer *User, repo *Repository, issue *Issue, content string, attachments []string) (*Comment, error) { + return CreateComment(doer, repo, issue, 0, 0, COMMENT_TYPE_COMMENT, content, attachments) } -// GetIssueComments returns list of comment by given issue id. -func GetIssueComments(issueId int64) ([]Comment, error) { - comments := make([]Comment, 0, 10) - err := x.Asc("created").Find(&comments, &Comment{IssueId: issueId}) - return comments, err +// GetCommentById returns the comment with the given id +func GetCommentById(id int64) (*Comment, error) { + c := new(Comment) + _, err := x.Id(id).Get(c) + return c, err } -// Attachments returns the attachments for this comment. -func (c *Comment) Attachments() []*Attachment { - a, _ := GetAttachmentsByComment(c.Id) - return a +// 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) } func (c *Comment) AfterDelete() { - _, err := DeleteAttachmentsByComment(c.Id, true) + _, 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) + log.Info("Could not delete files for comment %d on issue #%d: %s", c.ID, c.IssueID, err) } } @@ -1291,8 +1547,7 @@ func NewAttachment(name string, buf []byte, file multipart.File) (_ *Attachment, return attach, sess.Commit() } -// GetAttachmentByUUID returns attachment by given UUID. -func GetAttachmentByUUID(uuid string) (*Attachment, error) { +func getAttachmentByUUID(e Engine, uuid string) (*Attachment, error) { attach := &Attachment{UUID: uuid} has, err := x.Get(attach) if err != nil { @@ -1303,24 +1558,21 @@ func GetAttachmentByUUID(uuid string) (*Attachment, error) { return attach, nil } -func GetAttachmentsForIssue(issueId int64) ([]*Attachment, error) { - attachments := make([]*Attachment, 0, 10) - err := x.Where("issue_id = ?", issueId).And("comment_id = 0").Find(&attachments) - return attachments, err +// GetAttachmentByUUID returns attachment by given UUID. +func GetAttachmentByUUID(uuid string) (*Attachment, error) { + return getAttachmentByUUID(x, uuid) } -// GetAttachmentsByIssue returns a list of attachments for the given issue -func GetAttachmentsByIssue(issueId int64) ([]*Attachment, error) { +// GetAttachmentsByIssueID returns all attachments for given issue by ID. +func GetAttachmentsByIssueID(issueID int64) ([]*Attachment, error) { attachments := make([]*Attachment, 0, 10) - err := x.Where("issue_id = ?", issueId).And("comment_id > 0").Find(&attachments) - return attachments, err + return attachments, x.Where("issue_id=? AND comment_id=0", issueID).Find(&attachments) } -// GetAttachmentsByComment returns a list of attachments for the given comment -func GetAttachmentsByComment(commentId int64) ([]*Attachment, error) { +// GetAttachmentsByCommentID returns all attachments if comment by given ID. +func GetAttachmentsByCommentID(commentID int64) ([]*Attachment, error) { attachments := make([]*Attachment, 0, 10) - err := x.Where("comment_id = ?", commentId).Find(&attachments) - return attachments, err + return attachments, x.Where("comment_id=?", commentID).Find(&attachments) } // DeleteAttachment deletes the given attachment and optionally the associated file. @@ -1348,7 +1600,7 @@ func DeleteAttachments(attachments []*Attachment, remove bool) (int, error) { // DeleteAttachmentsByIssue deletes all attachments associated with the given issue. func DeleteAttachmentsByIssue(issueId int64, remove bool) (int, error) { - attachments, err := GetAttachmentsByIssue(issueId) + attachments, err := GetAttachmentsByIssueID(issueId) if err != nil { return 0, err @@ -1359,7 +1611,7 @@ func DeleteAttachmentsByIssue(issueId int64, remove bool) (int, error) { // DeleteAttachmentsByComment deletes all attachments associated with the given comment. func DeleteAttachmentsByComment(commentId int64, remove bool) (int, error) { - attachments, err := GetAttachmentsByComment(commentId) + attachments, err := GetAttachmentsByCommentID(commentId) if err != nil { return 0, err |