You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

action.go 9.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package action
  5. import (
  6. "encoding/json"
  7. "fmt"
  8. "path"
  9. "strings"
  10. "code.gitea.io/gitea/models"
  11. "code.gitea.io/gitea/modules/log"
  12. "code.gitea.io/gitea/modules/notification/base"
  13. "code.gitea.io/gitea/modules/repository"
  14. )
  15. type actionNotifier struct {
  16. base.NullNotifier
  17. }
  18. var (
  19. _ base.Notifier = &actionNotifier{}
  20. )
  21. // NewNotifier create a new actionNotifier notifier
  22. func NewNotifier() base.Notifier {
  23. return &actionNotifier{}
  24. }
  25. func (a *actionNotifier) NotifyNewIssue(issue *models.Issue) {
  26. if err := issue.LoadPoster(); err != nil {
  27. log.Error("issue.LoadPoster: %v", err)
  28. return
  29. }
  30. if err := issue.LoadRepo(); err != nil {
  31. log.Error("issue.LoadRepo: %v", err)
  32. return
  33. }
  34. repo := issue.Repo
  35. if err := models.NotifyWatchers(&models.Action{
  36. ActUserID: issue.Poster.ID,
  37. ActUser: issue.Poster,
  38. OpType: models.ActionCreateIssue,
  39. Content: fmt.Sprintf("%d|%s", issue.Index, issue.Title),
  40. RepoID: repo.ID,
  41. Repo: repo,
  42. IsPrivate: repo.IsPrivate,
  43. }); err != nil {
  44. log.Error("NotifyWatchers: %v", err)
  45. }
  46. }
  47. // NotifyIssueChangeStatus notifies close or reopen issue to notifiers
  48. func (a *actionNotifier) NotifyIssueChangeStatus(doer *models.User, issue *models.Issue, actionComment *models.Comment, closeOrReopen bool) {
  49. // Compose comment action, could be plain comment, close or reopen issue/pull request.
  50. // This object will be used to notify watchers in the end of function.
  51. act := &models.Action{
  52. ActUserID: doer.ID,
  53. ActUser: doer,
  54. Content: fmt.Sprintf("%d|%s", issue.Index, ""),
  55. RepoID: issue.Repo.ID,
  56. Repo: issue.Repo,
  57. Comment: actionComment,
  58. CommentID: actionComment.ID,
  59. IsPrivate: issue.Repo.IsPrivate,
  60. }
  61. // Check comment type.
  62. if closeOrReopen {
  63. act.OpType = models.ActionCloseIssue
  64. if issue.IsPull {
  65. act.OpType = models.ActionClosePullRequest
  66. }
  67. } else {
  68. act.OpType = models.ActionReopenIssue
  69. if issue.IsPull {
  70. act.OpType = models.ActionReopenPullRequest
  71. }
  72. }
  73. // Notify watchers for whatever action comes in, ignore if no action type.
  74. if err := models.NotifyWatchers(act); err != nil {
  75. log.Error("NotifyWatchers: %v", err)
  76. }
  77. }
  78. // NotifyCreateIssueComment notifies comment on an issue to notifiers
  79. func (a *actionNotifier) NotifyCreateIssueComment(doer *models.User, repo *models.Repository,
  80. issue *models.Issue, comment *models.Comment) {
  81. act := &models.Action{
  82. ActUserID: doer.ID,
  83. ActUser: doer,
  84. Content: fmt.Sprintf("%d|%s", issue.Index, comment.Content),
  85. RepoID: issue.Repo.ID,
  86. Repo: issue.Repo,
  87. Comment: comment,
  88. CommentID: comment.ID,
  89. IsPrivate: issue.Repo.IsPrivate,
  90. }
  91. if issue.IsPull {
  92. act.OpType = models.ActionCommentPull
  93. } else {
  94. act.OpType = models.ActionCommentIssue
  95. }
  96. // Notify watchers for whatever action comes in, ignore if no action type.
  97. if err := models.NotifyWatchers(act); err != nil {
  98. log.Error("NotifyWatchers: %v", err)
  99. }
  100. }
  101. func (a *actionNotifier) NotifyNewPullRequest(pull *models.PullRequest) {
  102. if err := pull.LoadIssue(); err != nil {
  103. log.Error("pull.LoadIssue: %v", err)
  104. return
  105. }
  106. if err := pull.Issue.LoadRepo(); err != nil {
  107. log.Error("pull.Issue.LoadRepo: %v", err)
  108. return
  109. }
  110. if err := pull.Issue.LoadPoster(); err != nil {
  111. log.Error("pull.Issue.LoadPoster: %v", err)
  112. return
  113. }
  114. if err := models.NotifyWatchers(&models.Action{
  115. ActUserID: pull.Issue.Poster.ID,
  116. ActUser: pull.Issue.Poster,
  117. OpType: models.ActionCreatePullRequest,
  118. Content: fmt.Sprintf("%d|%s", pull.Issue.Index, pull.Issue.Title),
  119. RepoID: pull.Issue.Repo.ID,
  120. Repo: pull.Issue.Repo,
  121. IsPrivate: pull.Issue.Repo.IsPrivate,
  122. }); err != nil {
  123. log.Error("NotifyWatchers: %v", err)
  124. }
  125. }
  126. func (a *actionNotifier) NotifyRenameRepository(doer *models.User, repo *models.Repository, oldRepoName string) {
  127. log.Trace("action.ChangeRepositoryName: %s/%s", doer.Name, repo.Name)
  128. if err := models.NotifyWatchers(&models.Action{
  129. ActUserID: doer.ID,
  130. ActUser: doer,
  131. OpType: models.ActionRenameRepo,
  132. RepoID: repo.ID,
  133. Repo: repo,
  134. IsPrivate: repo.IsPrivate,
  135. Content: oldRepoName,
  136. }); err != nil {
  137. log.Error("NotifyWatchers: %v", err)
  138. }
  139. }
  140. func (a *actionNotifier) NotifyTransferRepository(doer *models.User, repo *models.Repository, oldOwnerName string) {
  141. if err := models.NotifyWatchers(&models.Action{
  142. ActUserID: doer.ID,
  143. ActUser: doer,
  144. OpType: models.ActionTransferRepo,
  145. RepoID: repo.ID,
  146. Repo: repo,
  147. IsPrivate: repo.IsPrivate,
  148. Content: path.Join(oldOwnerName, repo.Name),
  149. }); err != nil {
  150. log.Error("NotifyWatchers: %v", err)
  151. }
  152. }
  153. func (a *actionNotifier) NotifyCreateRepository(doer *models.User, u *models.User, repo *models.Repository) {
  154. if err := models.NotifyWatchers(&models.Action{
  155. ActUserID: doer.ID,
  156. ActUser: doer,
  157. OpType: models.ActionCreateRepo,
  158. RepoID: repo.ID,
  159. Repo: repo,
  160. IsPrivate: repo.IsPrivate,
  161. }); err != nil {
  162. log.Error("notify watchers '%d/%d': %v", doer.ID, repo.ID, err)
  163. }
  164. }
  165. func (a *actionNotifier) NotifyForkRepository(doer *models.User, oldRepo, repo *models.Repository) {
  166. if err := models.NotifyWatchers(&models.Action{
  167. ActUserID: doer.ID,
  168. ActUser: doer,
  169. OpType: models.ActionCreateRepo,
  170. RepoID: repo.ID,
  171. Repo: repo,
  172. IsPrivate: repo.IsPrivate,
  173. }); err != nil {
  174. log.Error("notify watchers '%d/%d': %v", doer.ID, repo.ID, err)
  175. }
  176. }
  177. func (a *actionNotifier) NotifyPullRequestReview(pr *models.PullRequest, review *models.Review, comment *models.Comment) {
  178. if err := review.LoadReviewer(); err != nil {
  179. log.Error("LoadReviewer '%d/%d': %v", review.ID, review.ReviewerID, err)
  180. return
  181. }
  182. if err := review.LoadCodeComments(); err != nil {
  183. log.Error("LoadCodeComments '%d/%d': %v", review.Reviewer.ID, review.ID, err)
  184. return
  185. }
  186. var actions = make([]*models.Action, 0, 10)
  187. for _, lines := range review.CodeComments {
  188. for _, comments := range lines {
  189. for _, comm := range comments {
  190. actions = append(actions, &models.Action{
  191. ActUserID: review.Reviewer.ID,
  192. ActUser: review.Reviewer,
  193. Content: fmt.Sprintf("%d|%s", review.Issue.Index, strings.Split(comm.Content, "\n")[0]),
  194. OpType: models.ActionCommentPull,
  195. RepoID: review.Issue.RepoID,
  196. Repo: review.Issue.Repo,
  197. IsPrivate: review.Issue.Repo.IsPrivate,
  198. Comment: comm,
  199. CommentID: comm.ID,
  200. })
  201. }
  202. }
  203. }
  204. if review.Type != models.ReviewTypeComment || strings.TrimSpace(comment.Content) != "" {
  205. action := &models.Action{
  206. ActUserID: review.Reviewer.ID,
  207. ActUser: review.Reviewer,
  208. Content: fmt.Sprintf("%d|%s", review.Issue.Index, strings.Split(comment.Content, "\n")[0]),
  209. RepoID: review.Issue.RepoID,
  210. Repo: review.Issue.Repo,
  211. IsPrivate: review.Issue.Repo.IsPrivate,
  212. Comment: comment,
  213. CommentID: comment.ID,
  214. }
  215. switch review.Type {
  216. case models.ReviewTypeApprove:
  217. action.OpType = models.ActionApprovePullRequest
  218. case models.ReviewTypeReject:
  219. action.OpType = models.ActionRejectPullRequest
  220. default:
  221. action.OpType = models.ActionCommentPull
  222. }
  223. actions = append(actions, action)
  224. }
  225. if err := models.NotifyWatchersActions(actions); err != nil {
  226. log.Error("notify watchers '%d/%d': %v", review.Reviewer.ID, review.Issue.RepoID, err)
  227. }
  228. }
  229. func (*actionNotifier) NotifyMergePullRequest(pr *models.PullRequest, doer *models.User) {
  230. if err := models.NotifyWatchers(&models.Action{
  231. ActUserID: doer.ID,
  232. ActUser: doer,
  233. OpType: models.ActionMergePullRequest,
  234. Content: fmt.Sprintf("%d|%s", pr.Issue.Index, pr.Issue.Title),
  235. RepoID: pr.Issue.Repo.ID,
  236. Repo: pr.Issue.Repo,
  237. IsPrivate: pr.Issue.Repo.IsPrivate,
  238. }); err != nil {
  239. log.Error("NotifyWatchers [%d]: %v", pr.ID, err)
  240. }
  241. }
  242. func (a *actionNotifier) NotifySyncPushCommits(pusher *models.User, repo *models.Repository, refName, oldCommitID, newCommitID string, commits *repository.PushCommits) {
  243. data, err := json.Marshal(commits)
  244. if err != nil {
  245. log.Error("json.Marshal: %v", err)
  246. return
  247. }
  248. if err := models.NotifyWatchers(&models.Action{
  249. ActUserID: repo.OwnerID,
  250. ActUser: repo.MustOwner(),
  251. OpType: models.ActionMirrorSyncPush,
  252. RepoID: repo.ID,
  253. Repo: repo,
  254. IsPrivate: repo.IsPrivate,
  255. RefName: refName,
  256. Content: string(data),
  257. }); err != nil {
  258. log.Error("notifyWatchers: %v", err)
  259. }
  260. }
  261. func (a *actionNotifier) NotifySyncCreateRef(doer *models.User, repo *models.Repository, refType, refFullName string) {
  262. if err := models.NotifyWatchers(&models.Action{
  263. ActUserID: repo.OwnerID,
  264. ActUser: repo.MustOwner(),
  265. OpType: models.ActionMirrorSyncCreate,
  266. RepoID: repo.ID,
  267. Repo: repo,
  268. IsPrivate: repo.IsPrivate,
  269. RefName: refFullName,
  270. }); err != nil {
  271. log.Error("notifyWatchers: %v", err)
  272. }
  273. }
  274. func (a *actionNotifier) NotifySyncDeleteRef(doer *models.User, repo *models.Repository, refType, refFullName string) {
  275. if err := models.NotifyWatchers(&models.Action{
  276. ActUserID: repo.OwnerID,
  277. ActUser: repo.MustOwner(),
  278. OpType: models.ActionMirrorSyncCreate,
  279. RepoID: repo.ID,
  280. Repo: repo,
  281. IsPrivate: repo.IsPrivate,
  282. RefName: refFullName,
  283. }); err != nil {
  284. log.Error("notifyWatchers: %v", err)
  285. }
  286. }