2016-03-05 20:58:51 +03:00
// 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"
"github.com/Unknwon/com"
2017-06-21 04:00:44 +03:00
"github.com/go-xorm/builder"
2016-03-05 20:58:51 +03:00
"github.com/go-xorm/xorm"
2016-08-26 23:40:53 +03:00
2016-11-11 12:39:44 +03:00
api "code.gitea.io/sdk/gitea"
2016-03-05 20:58:51 +03:00
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/modules/log"
2017-09-16 20:17:57 +03:00
"code.gitea.io/gitea/modules/markup"
2017-12-11 07:37:04 +03:00
"code.gitea.io/gitea/modules/util"
2016-03-05 20:58:51 +03:00
)
// CommentType defines whether a comment is just a simple comment, an action (like close) or a reference.
type CommentType int
2017-06-21 04:00:44 +03:00
// define unknown comment type
const (
CommentTypeUnknown CommentType = - 1
)
2016-11-28 16:33:09 +03:00
// Enumerate all the comment types
2016-03-05 20:58:51 +03:00
const (
// Plain comment, can be associated with a commit (CommitID > 0) and a line (LineNum > 0)
2016-11-07 19:30:04 +03:00
CommentTypeComment CommentType = iota
CommentTypeReopen
2016-11-07 19:35:34 +03:00
CommentTypeClose
2016-03-05 20:58:51 +03:00
// References.
2016-11-07 19:35:34 +03:00
CommentTypeIssueRef
2016-03-05 20:58:51 +03:00
// Reference from a commit (not part of a pull request)
2016-11-07 19:35:34 +03:00
CommentTypeCommitRef
2016-03-05 20:58:51 +03:00
// Reference from a comment
2016-11-07 23:58:22 +03:00
CommentTypeCommentRef
2016-03-05 20:58:51 +03:00
// Reference from a pull request
2016-11-07 19:35:34 +03:00
CommentTypePullRef
2017-01-30 15:46:45 +03:00
// Labels changed
CommentTypeLabel
2017-02-01 05:36:08 +03:00
// Milestone changed
CommentTypeMilestone
2017-02-03 18:09:10 +03:00
// Assignees changed
CommentTypeAssignees
2017-02-05 17:36:00 +03:00
// Change Title
CommentTypeChangeTitle
2017-02-11 07:00:29 +03:00
// Delete Branch
CommentTypeDeleteBranch
2017-09-12 09:48:13 +03:00
// Start a stopwatch for time tracking
CommentTypeStartTracking
// Stop a stopwatch for time tracking
CommentTypeStopTracking
// Add time manual for time tracking
CommentTypeAddTimeManual
// Cancel a stopwatch for time tracking
CommentTypeCancelTracking
2018-05-01 22:05:28 +03:00
// Added a due date
CommentTypeAddedDeadline
// Modified the due date
CommentTypeModifiedDeadline
// Removed a due date
CommentTypeRemovedDeadline
2016-03-05 20:58:51 +03:00
)
2016-11-28 16:33:09 +03:00
// CommentTag defines comment tag type
2016-03-05 20:58:51 +03:00
type CommentTag int
2016-11-28 16:33:09 +03:00
// Enumerate all the comment tag types
2016-03-05 20:58:51 +03:00
const (
2016-11-07 19:30:04 +03:00
CommentTagNone CommentTag = iota
CommentTagPoster
CommentTagWriter
2016-11-07 19:35:34 +03:00
CommentTagOwner
2016-03-05 20:58:51 +03:00
)
// Comment represents a comment in commit and issue page.
type Comment struct {
2018-05-09 19:29:04 +03:00
ID int64 ` xorm:"pk autoincr" `
Type CommentType
PosterID int64 ` xorm:"INDEX" `
Poster * User ` xorm:"-" `
IssueID int64 ` xorm:"INDEX" `
LabelID int64
Label * Label ` xorm:"-" `
OldMilestoneID int64
MilestoneID int64
OldMilestone * Milestone ` xorm:"-" `
Milestone * Milestone ` xorm:"-" `
AssigneeID int64
RemovedAssignee bool
Assignee * User ` xorm:"-" `
OldTitle string
NewTitle string
2017-02-03 18:09:10 +03:00
2017-02-01 05:36:08 +03:00
CommitID int64
2016-03-05 20:58:51 +03:00
Line int64
2016-03-10 03:53:30 +03:00
Content string ` xorm:"TEXT" `
RenderedContent string ` xorm:"-" `
2017-12-11 07:37:04 +03:00
CreatedUnix util . TimeStamp ` xorm:"INDEX created" `
UpdatedUnix util . TimeStamp ` xorm:"INDEX updated" `
2016-03-05 20:58:51 +03:00
// Reference issue in commit message
CommitSHA string ` xorm:"VARCHAR(40)" `
Attachments [ ] * Attachment ` xorm:"-" `
2017-12-04 02:14:26 +03:00
Reactions ReactionList ` xorm:"-" `
2016-03-05 20:58:51 +03:00
// For view issue page.
ShowTag CommentTag ` xorm:"-" `
}
2017-10-01 19:52:35 +03:00
// AfterLoad is invoked from XORM after setting the values of all fields of this object.
func ( c * Comment ) AfterLoad ( session * xorm . Session ) {
2016-03-05 20:58:51 +03:00
var err error
2017-10-01 19:52:35 +03:00
c . Attachments , err = getAttachmentsByCommentID ( session , c . ID )
if err != nil {
log . Error ( 3 , "getAttachmentsByCommentID[%d]: %v" , c . ID , err )
}
2016-03-05 20:58:51 +03:00
2017-10-01 19:52:35 +03:00
c . Poster , err = getUserByID ( session , c . PosterID )
if err != nil {
if IsErrUserNotExist ( err ) {
c . PosterID = - 1
c . Poster = NewGhostUser ( )
} else {
log . Error ( 3 , "getUserByID[%d]: %v" , c . ID , err )
2016-03-05 20:58:51 +03:00
}
}
}
2016-11-28 16:33:09 +03:00
// AfterDelete is invoked from XORM after the object is deleted.
2016-03-05 20:58:51 +03:00
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 )
}
}
2016-12-22 11:29:26 +03:00
// HTMLURL formats a URL-string to the issue-comment
func ( c * Comment ) HTMLURL ( ) string {
issue , err := GetIssueByID ( c . IssueID )
if err != nil { // Silently dropping errors :unamused:
log . Error ( 4 , "GetIssueByID(%d): %v" , c . IssueID , err )
return ""
}
2017-05-25 05:38:56 +03:00
return fmt . Sprintf ( "%s#%s" , issue . HTMLURL ( ) , c . HashTag ( ) )
2016-12-22 11:29:26 +03:00
}
// IssueURL formats a URL-string to the issue
func ( c * Comment ) IssueURL ( ) string {
issue , err := GetIssueByID ( c . IssueID )
if err != nil { // Silently dropping errors :unamused:
log . Error ( 4 , "GetIssueByID(%d): %v" , c . IssueID , err )
return ""
}
if issue . IsPull {
return ""
}
return issue . HTMLURL ( )
}
// PRURL formats a URL-string to the pull-request
func ( c * Comment ) PRURL ( ) string {
issue , err := GetIssueByID ( c . IssueID )
if err != nil { // Silently dropping errors :unamused:
log . Error ( 4 , "GetIssueByID(%d): %v" , c . IssueID , err )
return ""
}
if ! issue . IsPull {
return ""
}
return issue . HTMLURL ( )
}
2016-11-28 16:33:09 +03:00
// APIFormat converts a Comment to the api.Comment format
2016-08-26 21:23:21 +03:00
func ( c * Comment ) APIFormat ( ) * api . Comment {
2016-08-26 23:40:53 +03:00
return & api . Comment {
2016-12-22 11:29:26 +03:00
ID : c . ID ,
Poster : c . Poster . APIFormat ( ) ,
HTMLURL : c . HTMLURL ( ) ,
IssueURL : c . IssueURL ( ) ,
PRURL : c . PRURL ( ) ,
Body : c . Content ,
2017-12-11 07:37:04 +03:00
Created : c . CreatedUnix . AsTime ( ) ,
Updated : c . UpdatedUnix . AsTime ( ) ,
2016-08-26 21:23:21 +03:00
}
}
2016-03-05 20:58:51 +03:00
// 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 )
}
2017-01-30 15:46:45 +03:00
// LoadLabel if comment.Type is CommentTypeLabel, then load Label
func ( c * Comment ) LoadLabel ( ) error {
var label Label
has , err := x . ID ( c . LabelID ) . Get ( & label )
if err != nil {
return err
2017-02-11 15:56:57 +03:00
} else if has {
c . Label = & label
} else {
// Ignore Label is deleted, but not clear this table
log . Warn ( "Commit %d cannot load label %d" , c . ID , c . LabelID )
2017-01-30 15:46:45 +03:00
}
2017-02-11 15:56:57 +03:00
2017-01-30 15:46:45 +03:00
return nil
}
2017-02-01 05:36:08 +03:00
// LoadMilestone if comment.Type is CommentTypeMilestone, then load milestone
func ( c * Comment ) LoadMilestone ( ) error {
if c . OldMilestoneID > 0 {
var oldMilestone Milestone
has , err := x . ID ( c . OldMilestoneID ) . Get ( & oldMilestone )
if err != nil {
return err
2017-06-17 07:51:28 +03:00
} else if has {
c . OldMilestone = & oldMilestone
2017-02-01 05:36:08 +03:00
}
}
if c . MilestoneID > 0 {
var milestone Milestone
has , err := x . ID ( c . MilestoneID ) . Get ( & milestone )
if err != nil {
return err
2017-06-17 07:51:28 +03:00
} else if has {
c . Milestone = & milestone
2017-02-01 05:36:08 +03:00
}
}
return nil
}
2018-05-09 19:29:04 +03:00
// LoadAssigneeUser if comment.Type is CommentTypeAssignees, then load assignees
func ( c * Comment ) LoadAssigneeUser ( ) error {
2017-02-03 18:09:10 +03:00
var err error
if c . AssigneeID > 0 {
c . Assignee , err = getUserByID ( x , c . AssigneeID )
if err != nil {
2018-01-07 12:13:10 +03:00
if ! IsErrUserNotExist ( err ) {
return err
}
c . Assignee = NewGhostUser ( )
2017-02-03 18:09:10 +03:00
}
}
return nil
}
2016-07-15 19:36:39 +03:00
// MailParticipants sends new comment emails to repository watchers
// and mentioned people.
2016-12-22 12:00:39 +03:00
func ( c * Comment ) MailParticipants ( e Engine , opType ActionType , issue * Issue ) ( err error ) {
2017-09-16 20:17:57 +03:00
mentions := markup . FindAllMentions ( c . Content )
2016-12-22 12:00:39 +03:00
if err = UpdateIssueMentions ( e , c . IssueID , mentions ) ; err != nil {
2016-11-28 16:33:09 +03:00
return fmt . Errorf ( "UpdateIssueMentions [%d]: %v" , c . IssueID , err )
2016-07-15 19:36:39 +03:00
}
2017-11-03 12:23:17 +03:00
content := c . Content
2016-07-15 19:36:39 +03:00
switch opType {
2016-11-07 18:37:32 +03:00
case ActionCloseIssue :
2017-11-03 12:23:17 +03:00
content = fmt . Sprintf ( "Closed #%d" , issue . Index )
2016-11-07 18:37:32 +03:00
case ActionReopenIssue :
2017-11-03 12:23:17 +03:00
content = fmt . Sprintf ( "Reopened #%d" , issue . Index )
2016-07-15 19:36:39 +03:00
}
2017-11-03 12:23:17 +03:00
if err = mailIssueCommentToParticipants ( e , issue , c . Poster , content , c , mentions ) ; err != nil {
2016-07-15 19:36:39 +03:00
log . Error ( 4 , "mailIssueCommentToParticipants: %v" , err )
}
return nil
}
2017-12-04 02:14:26 +03:00
func ( c * Comment ) loadReactions ( e Engine ) ( err error ) {
if c . Reactions != nil {
return nil
}
c . Reactions , err = findReactions ( e , FindReactionsOptions {
IssueID : c . IssueID ,
CommentID : c . ID ,
} )
if err != nil {
return err
}
// Load reaction user data
if _ , err := c . Reactions . LoadUsers ( ) ; err != nil {
return err
}
return nil
}
// LoadReactions loads comment reactions
func ( c * Comment ) LoadReactions ( ) error {
return c . loadReactions ( x )
}
2016-03-05 20:58:51 +03:00
func createComment ( e * xorm . Session , opts * CreateCommentOptions ) ( _ * Comment , err error ) {
2017-01-30 15:46:45 +03:00
var LabelID int64
if opts . Label != nil {
LabelID = opts . Label . ID
}
2016-03-05 20:58:51 +03:00
comment := & Comment {
2018-05-09 19:29:04 +03:00
Type : opts . Type ,
PosterID : opts . Doer . ID ,
Poster : opts . Doer ,
IssueID : opts . Issue . ID ,
LabelID : LabelID ,
OldMilestoneID : opts . OldMilestoneID ,
MilestoneID : opts . MilestoneID ,
RemovedAssignee : opts . RemovedAssignee ,
AssigneeID : opts . AssigneeID ,
CommitID : opts . CommitID ,
CommitSHA : opts . CommitSHA ,
Line : opts . LineNum ,
Content : opts . Content ,
OldTitle : opts . OldTitle ,
NewTitle : opts . NewTitle ,
2016-03-05 20:58:51 +03:00
}
if _ , err = e . Insert ( comment ) ; err != nil {
return nil , err
}
2017-01-30 15:46:45 +03:00
if err = opts . Repo . getOwner ( e ) ; err != nil {
return nil , err
}
2016-03-06 02:08:42 +03:00
// Compose comment action, could be plain comment, close or reopen issue/pull request.
2016-03-05 20:58:51 +03:00
// This object will be used to notify watchers in the end of function.
act := & Action {
2017-05-26 04:38:18 +03:00
ActUserID : opts . Doer . ID ,
ActUser : opts . Doer ,
Content : fmt . Sprintf ( "%d|%s" , opts . Issue . Index , strings . Split ( opts . Content , "\n" ) [ 0 ] ) ,
RepoID : opts . Repo . ID ,
Repo : opts . Repo ,
2017-06-25 21:20:29 +03:00
Comment : comment ,
CommentID : comment . ID ,
2017-05-26 04:38:18 +03:00
IsPrivate : opts . Repo . IsPrivate ,
2016-03-05 20:58:51 +03:00
}
// Check comment type.
switch opts . Type {
2016-11-07 19:30:04 +03:00
case CommentTypeComment :
2016-11-07 18:37:32 +03:00
act . OpType = ActionCommentIssue
2016-03-05 20:58:51 +03:00
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
}
2016-07-15 19:36:39 +03:00
return nil , fmt . Errorf ( "getAttachmentByUUID [%s]: %v" , uuid , err )
2016-03-05 20:58:51 +03:00
}
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().
2017-10-05 07:43:04 +03:00
if _ , err = e . ID ( attachments [ i ] . ID ) . Update ( attachments [ i ] ) ; err != nil {
2016-07-15 19:36:39 +03:00
return nil , fmt . Errorf ( "update attachment [%d]: %v" , attachments [ i ] . ID , err )
2016-03-05 20:58:51 +03:00
}
}
2016-11-07 19:30:04 +03:00
case CommentTypeReopen :
2016-11-07 18:37:32 +03:00
act . OpType = ActionReopenIssue
2016-03-05 20:58:51 +03:00
if opts . Issue . IsPull {
2016-11-07 18:37:32 +03:00
act . OpType = ActionReopenPullRequest
2016-03-05 20:58:51 +03:00
}
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
}
2016-03-06 02:08:42 +03:00
2016-11-07 19:35:34 +03:00
case CommentTypeClose :
2016-11-07 18:37:32 +03:00
act . OpType = ActionCloseIssue
2016-03-05 20:58:51 +03:00
if opts . Issue . IsPull {
2016-11-07 18:37:32 +03:00
act . OpType = ActionClosePullRequest
2016-03-05 20:58:51 +03:00
}
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
}
2017-07-27 04:20:38 +03:00
}
2016-07-15 19:36:39 +03:00
2017-07-27 04:20:38 +03:00
// update the issue's updated_unix column
2018-04-29 07:48:14 +03:00
if err = updateIssueCols ( e , opts . Issue , "updated_unix" ) ; err != nil {
2017-07-27 04:20:38 +03:00
return nil , err
2016-03-05 20:58:51 +03:00
}
2016-07-15 19:36:39 +03:00
// Notify watchers for whatever action comes in, ignore if no action type.
2016-03-11 13:11:58 +03:00
if act . OpType > 0 {
if err = notifyWatchers ( e , act ) ; err != nil {
2016-07-15 19:36:39 +03:00
log . Error ( 4 , "notifyWatchers: %v" , err )
2016-03-11 13:11:58 +03:00
}
2016-12-22 12:00:39 +03:00
if err = comment . MailParticipants ( e , act . OpType , opts . Issue ) ; err != nil {
log . Error ( 4 , "MailParticipants: %v" , err )
}
2016-03-05 20:58:51 +03:00
}
return comment , nil
}
func createStatusComment ( e * xorm . Session , doer * User , repo * Repository , issue * Issue ) ( * Comment , error ) {
2016-11-07 19:35:34 +03:00
cmtType := CommentTypeClose
2016-03-05 20:58:51 +03:00
if ! issue . IsClosed {
2016-11-07 19:30:04 +03:00
cmtType = CommentTypeReopen
2016-03-05 20:58:51 +03:00
}
return createComment ( e , & CreateCommentOptions {
Type : cmtType ,
Doer : doer ,
Repo : repo ,
Issue : issue ,
} )
}
2017-01-30 15:46:45 +03:00
func createLabelComment ( e * xorm . Session , doer * User , repo * Repository , issue * Issue , label * Label , add bool ) ( * Comment , error ) {
var content string
if add {
content = "1"
}
return createComment ( e , & CreateCommentOptions {
Type : CommentTypeLabel ,
Doer : doer ,
Repo : repo ,
Issue : issue ,
Label : label ,
Content : content ,
} )
}
2017-02-01 05:36:08 +03:00
func createMilestoneComment ( e * xorm . Session , doer * User , repo * Repository , issue * Issue , oldMilestoneID , milestoneID int64 ) ( * Comment , error ) {
return createComment ( e , & CreateCommentOptions {
Type : CommentTypeMilestone ,
Doer : doer ,
Repo : repo ,
Issue : issue ,
OldMilestoneID : oldMilestoneID ,
MilestoneID : milestoneID ,
} )
}
2018-05-09 19:29:04 +03:00
func createAssigneeComment ( e * xorm . Session , doer * User , repo * Repository , issue * Issue , assigneeID int64 , removedAssignee bool ) ( * Comment , error ) {
2017-02-03 18:09:10 +03:00
return createComment ( e , & CreateCommentOptions {
2018-05-09 19:29:04 +03:00
Type : CommentTypeAssignees ,
Doer : doer ,
Repo : repo ,
Issue : issue ,
RemovedAssignee : removedAssignee ,
AssigneeID : assigneeID ,
2017-02-03 18:09:10 +03:00
} )
}
2018-05-01 22:05:28 +03:00
func createDeadlineComment ( e * xorm . Session , doer * User , issue * Issue , newDeadlineUnix util . TimeStamp ) ( * Comment , error ) {
var content string
var commentType CommentType
// newDeadline = 0 means deleting
if newDeadlineUnix == 0 {
commentType = CommentTypeRemovedDeadline
content = issue . DeadlineUnix . Format ( "2006-01-02" )
} else if issue . DeadlineUnix == 0 {
// Check if the new date was added or modified
// If the actual deadline is 0 => deadline added
commentType = CommentTypeAddedDeadline
content = newDeadlineUnix . Format ( "2006-01-02" )
} else { // Otherwise modified
commentType = CommentTypeModifiedDeadline
content = newDeadlineUnix . Format ( "2006-01-02" ) + "|" + issue . DeadlineUnix . Format ( "2006-01-02" )
}
return createComment ( e , & CreateCommentOptions {
Type : commentType ,
Doer : doer ,
Repo : issue . Repo ,
Issue : issue ,
Content : content ,
} )
}
2017-02-05 17:36:00 +03:00
func createChangeTitleComment ( e * xorm . Session , doer * User , repo * Repository , issue * Issue , oldTitle , newTitle string ) ( * Comment , error ) {
return createComment ( e , & CreateCommentOptions {
Type : CommentTypeChangeTitle ,
Doer : doer ,
Repo : repo ,
Issue : issue ,
OldTitle : oldTitle ,
NewTitle : newTitle ,
} )
}
2017-02-11 07:00:29 +03:00
func createDeleteBranchComment ( e * xorm . Session , doer * User , repo * Repository , issue * Issue , branchName string ) ( * Comment , error ) {
return createComment ( e , & CreateCommentOptions {
Type : CommentTypeDeleteBranch ,
Doer : doer ,
Repo : repo ,
Issue : issue ,
CommitSHA : branchName ,
} )
}
2016-11-28 16:33:09 +03:00
// CreateCommentOptions defines options for creating comment
2016-03-05 20:58:51 +03:00
type CreateCommentOptions struct {
Type CommentType
Doer * User
Repo * Repository
Issue * Issue
2017-01-30 15:46:45 +03:00
Label * Label
2016-03-05 20:58:51 +03:00
2018-05-09 19:29:04 +03:00
OldMilestoneID int64
MilestoneID int64
AssigneeID int64
RemovedAssignee bool
OldTitle string
NewTitle string
CommitID int64
CommitSHA string
LineNum int64
Content string
Attachments [ ] string // UUIDs of attachments
2016-03-05 20:58:51 +03:00
}
// CreateComment creates comment of issue or commit.
func CreateComment ( opts * CreateCommentOptions ) ( comment * Comment , err error ) {
sess := x . NewSession ( )
2017-06-21 03:57:05 +03:00
defer sess . Close ( )
2016-03-05 20:58:51 +03:00
if err = sess . Begin ( ) ; err != nil {
return nil , err
}
comment , err = createComment ( sess , opts )
if err != nil {
return nil , err
}
2017-09-16 23:16:21 +03:00
if err = sess . Commit ( ) ; err != nil {
return nil , err
}
if opts . Type == CommentTypeComment {
UpdateIssueIndexer ( opts . Issue . ID )
}
return comment , nil
2016-03-05 20:58:51 +03:00
}
// CreateIssueComment creates a plain issue comment.
func CreateIssueComment ( doer * User , repo * Repository , issue * Issue , content string , attachments [ ] string ) ( * Comment , error ) {
return CreateComment ( & CreateCommentOptions {
2016-11-07 19:30:04 +03:00
Type : CommentTypeComment ,
2016-03-05 20:58:51 +03:00
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 {
2016-11-07 19:35:34 +03:00
Type : CommentTypeCommitRef ,
2016-03-05 20:58:51 +03:00
IssueID : issue . ID ,
CommitSHA : commitSHA ,
} )
if err != nil {
return fmt . Errorf ( "check reference comment: %v" , err )
} else if has {
return nil
}
_ , err = CreateComment ( & CreateCommentOptions {
2016-11-07 19:35:34 +03:00
Type : CommentTypeCommitRef ,
2016-03-05 20:58:51 +03:00
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 )
2017-10-05 07:43:04 +03:00
has , err := x . ID ( id ) . Get ( c )
2016-03-05 20:58:51 +03:00
if err != nil {
return nil , err
} else if ! has {
2016-08-26 23:40:53 +03:00
return nil , ErrCommentNotExist { id , 0 }
2016-03-05 20:58:51 +03:00
}
return c , nil
}
2017-06-21 04:00:44 +03:00
// FindCommentsOptions describes the conditions to Find comments
type FindCommentsOptions struct {
RepoID int64
IssueID int64
Since int64
Type CommentType
}
func ( opts * FindCommentsOptions ) toConds ( ) builder . Cond {
var cond = builder . NewCond ( )
if opts . RepoID > 0 {
cond = cond . And ( builder . Eq { "issue.repo_id" : opts . RepoID } )
2016-08-26 23:40:53 +03:00
}
2017-06-21 04:00:44 +03:00
if opts . IssueID > 0 {
cond = cond . And ( builder . Eq { "comment.issue_id" : opts . IssueID } )
}
if opts . Since > 0 {
cond = cond . And ( builder . Gte { "comment.updated_unix" : opts . Since } )
}
if opts . Type != CommentTypeUnknown {
cond = cond . And ( builder . Eq { "comment.type" : opts . Type } )
}
return cond
2016-08-26 23:40:53 +03:00
}
2017-06-21 04:00:44 +03:00
func findComments ( e Engine , opts FindCommentsOptions ) ( [ ] * Comment , error ) {
2016-12-22 11:29:26 +03:00
comments := make ( [ ] * Comment , 0 , 10 )
2017-06-21 04:00:44 +03:00
sess := e . Where ( opts . toConds ( ) )
if opts . RepoID > 0 {
sess . Join ( "INNER" , "issue" , "issue.id = comment.issue_id" )
2016-12-22 11:29:26 +03:00
}
2017-06-21 04:00:44 +03:00
return comments , sess .
Asc ( "comment.created_unix" ) .
2017-11-03 06:11:42 +03:00
Asc ( "comment.id" ) .
2017-06-21 04:00:44 +03:00
Find ( & comments )
2016-12-22 11:29:26 +03:00
}
2017-06-21 04:00:44 +03:00
// FindComments returns all comments according options
func FindComments ( opts FindCommentsOptions ) ( [ ] * Comment , error ) {
return findComments ( x , opts )
2016-08-26 23:40:53 +03:00
}
// GetCommentsByIssueID returns all comments of an issue.
func GetCommentsByIssueID ( issueID int64 ) ( [ ] * Comment , error ) {
2017-06-21 04:00:44 +03:00
return findComments ( x , FindCommentsOptions {
IssueID : issueID ,
Type : CommentTypeUnknown ,
} )
2016-08-26 23:40:53 +03:00
}
2016-11-28 16:33:09 +03:00
// GetCommentsByIssueIDSince returns a list of comments of an issue since a given time point.
2016-08-26 23:40:53 +03:00
func GetCommentsByIssueIDSince ( issueID , since int64 ) ( [ ] * Comment , error ) {
2017-06-21 04:00:44 +03:00
return findComments ( x , FindCommentsOptions {
IssueID : issueID ,
Type : CommentTypeUnknown ,
Since : since ,
} )
2016-03-05 20:58:51 +03:00
}
2016-12-22 11:29:26 +03:00
// GetCommentsByRepoIDSince returns a list of comments for all issues in a repo since a given time point.
func GetCommentsByRepoIDSince ( repoID , since int64 ) ( [ ] * Comment , error ) {
2017-06-21 04:00:44 +03:00
return findComments ( x , FindCommentsOptions {
RepoID : repoID ,
Type : CommentTypeUnknown ,
Since : since ,
} )
2016-12-22 11:29:26 +03:00
}
2016-03-05 20:58:51 +03:00
// UpdateComment updates information of comment.
func UpdateComment ( c * Comment ) error {
2017-10-05 07:43:04 +03:00
if _ , err := x . ID ( c . ID ) . AllCols ( ) . Update ( c ) ; err != nil {
2017-09-16 23:16:21 +03:00
return err
} else if c . Type == CommentTypeComment {
UpdateIssueIndexer ( c . IssueID )
}
return nil
2016-03-05 20:58:51 +03:00
}
2016-07-25 21:48:17 +03:00
2017-01-25 05:43:02 +03:00
// DeleteComment deletes the comment
func DeleteComment ( comment * Comment ) error {
2016-07-25 21:48:17 +03:00
sess := x . NewSession ( )
2017-06-21 03:57:05 +03:00
defer sess . Close ( )
2017-01-25 05:43:02 +03:00
if err := sess . Begin ( ) ; err != nil {
2016-07-25 21:48:17 +03:00
return err
}
2017-01-25 17:54:52 +03:00
if _ , err := sess . Delete ( & Comment {
ID : comment . ID ,
} ) ; err != nil {
2016-07-25 21:48:17 +03:00
return err
}
2016-11-07 19:30:04 +03:00
if comment . Type == CommentTypeComment {
2017-01-25 05:43:02 +03:00
if _ , err := sess . Exec ( "UPDATE `issue` SET num_comments = num_comments - 1 WHERE id = ?" , comment . IssueID ) ; err != nil {
2016-07-25 21:48:17 +03:00
return err
}
}
2017-07-04 04:30:41 +03:00
if _ , err := sess . Where ( "comment_id = ?" , comment . ID ) . Cols ( "is_deleted" ) . Update ( & Action { IsDeleted : true } ) ; err != nil {
return err
}
2016-07-25 21:48:17 +03:00
2017-09-16 23:16:21 +03:00
if err := sess . Commit ( ) ; err != nil {
return err
} else if comment . Type == CommentTypeComment {
UpdateIssueIndexer ( comment . IssueID )
}
return nil
2016-07-25 21:48:17 +03:00
}