1// Copyright 2019 The Gitea Authors. All rights reserved.
2// SPDX-License-Identifier: MIT
3
4package issue
5
6import (
7 "context"
8 "fmt"
9
10 "forgejo.org/models/db"
11 issues_model "forgejo.org/models/issues"
12 repo_model "forgejo.org/models/repo"
13 user_model "forgejo.org/models/user"
14 "forgejo.org/modules/timeutil"
15 notify_service "forgejo.org/services/notify"
16)
17
18// CreateRefComment creates a commit reference comment to issue.
19func CreateRefComment(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, issue *issues_model.Issue, content, commitSHA string) error {
20 if len(commitSHA) == 0 {
21 return fmt.Errorf("cannot create reference with empty commit SHA")
22 }
23
24 // Check if same reference from same commit has already existed.
25 has, err := db.GetEngine(ctx).Get(&issues_model.Comment{
26 Type: issues_model.CommentTypeCommitRef,
27 IssueID: issue.ID,
28 CommitSHA: commitSHA,
29 })
30 if err != nil {
31 return fmt.Errorf("check reference comment: %w", err)
32 } else if has {
33 return nil
34 }
35
36 _, err = issues_model.CreateComment(ctx, &issues_model.CreateCommentOptions{
37 Type: issues_model.CommentTypeCommitRef,
38 Doer: doer,
39 Repo: repo,
40 Issue: issue,
41 CommitSHA: commitSHA,
42 Content: content,
43 })
44 return err
45}
46
47// CreateIssueComment creates a plain issue comment.
48func CreateIssueComment(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, issue *issues_model.Issue, content string, attachments []string) (*issues_model.Comment, error) {
49 // Check if doer is blocked by the poster of the issue or by the owner of the repository.
50 if user_model.IsBlockedMultiple(ctx, []int64{issue.PosterID, repo.OwnerID}, doer.ID) {
51 return nil, user_model.ErrBlockedByUser
52 }
53
54 comment, err := issues_model.CreateComment(ctx, &issues_model.CreateCommentOptions{
55 Type: issues_model.CommentTypeComment,
56 Doer: doer,
57 Repo: repo,
58 Issue: issue,
59 Content: content,
60 Attachments: attachments,
61 })
62 if err != nil {
63 return nil, err
64 }
65
66 mentions, err := issues_model.FindAndUpdateIssueMentions(ctx, issue, doer, comment.Content)
67 if err != nil {
68 return nil, err
69 }
70
71 notify_service.CreateIssueComment(ctx, doer, repo, issue, comment, mentions)
72
73 return comment, nil
74}
75
76// UpdateComment updates information of comment.
77func UpdateComment(ctx context.Context, c *issues_model.Comment, contentVersion int, doer *user_model.User, oldContent string) error {
78 if err := c.LoadReview(ctx); err != nil {
79 return err
80 }
81 isPartOfPendingReview := c.Review != nil && c.Review.Type == issues_model.ReviewTypePending
82
83 needsContentHistory := c.Content != oldContent && c.Type.HasContentSupport() && !isPartOfPendingReview
84 if needsContentHistory {
85 hasContentHistory, err := issues_model.HasIssueContentHistory(ctx, c.IssueID, c.ID)
86 if err != nil {
87 return err
88 }
89 if !hasContentHistory {
90 if err = issues_model.SaveIssueContentHistory(ctx, c.PosterID, c.IssueID, c.ID,
91 c.CreatedUnix, oldContent, true); err != nil {
92 return err
93 }
94 }
95 }
96
97 if err := issues_model.UpdateComment(ctx, c, contentVersion, doer); err != nil {
98 return err
99 }
100
101 if needsContentHistory {
102 historyDate := timeutil.TimeStampNow()
103 if c.Issue.NoAutoTime {
104 historyDate = c.Issue.UpdatedUnix
105 }
106 err := issues_model.SaveIssueContentHistory(ctx, doer.ID, c.IssueID, c.ID, historyDate, c.Content, false)
107 if err != nil {
108 return err
109 }
110 }
111
112 if !isPartOfPendingReview {
113 notify_service.UpdateComment(ctx, doer, c, oldContent)
114 }
115
116 return nil
117}
118
119// DeleteComment deletes the comment
120func DeleteComment(ctx context.Context, doer *user_model.User, comment *issues_model.Comment) error {
121 err := db.WithTx(ctx, func(ctx context.Context) error {
122 return issues_model.DeleteComment(ctx, comment)
123 })
124 if err != nil {
125 return err
126 }
127
128 if err := comment.LoadReview(ctx); err != nil {
129 return err
130 }
131 if comment.Review == nil || comment.Review.Type != issues_model.ReviewTypePending {
132 notify_service.DeleteComment(ctx, doer, comment)
133 }
134
135 return nil
136}