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.

344 lines
9.8 KiB

  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. RepoID: issue.Repo.ID,
  85. Repo: issue.Repo,
  86. Comment: comment,
  87. CommentID: comment.ID,
  88. IsPrivate: issue.Repo.IsPrivate,
  89. }
  90. content := ""
  91. if len(comment.Content) > 200 {
  92. content = content[:strings.LastIndex(comment.Content[0:200], " ")] + "…"
  93. } else {
  94. content = comment.Content
  95. }
  96. act.Content = fmt.Sprintf("%d|%s", issue.Index, content)
  97. if issue.IsPull {
  98. act.OpType = models.ActionCommentPull
  99. } else {
  100. act.OpType = models.ActionCommentIssue
  101. }
  102. // Notify watchers for whatever action comes in, ignore if no action type.
  103. if err := models.NotifyWatchers(act); err != nil {
  104. log.Error("NotifyWatchers: %v", err)
  105. }
  106. }
  107. func (a *actionNotifier) NotifyNewPullRequest(pull *models.PullRequest) {
  108. if err := pull.LoadIssue(); err != nil {
  109. log.Error("pull.LoadIssue: %v", err)
  110. return
  111. }
  112. if err := pull.Issue.LoadRepo(); err != nil {
  113. log.Error("pull.Issue.LoadRepo: %v", err)
  114. return
  115. }
  116. if err := pull.Issue.LoadPoster(); err != nil {
  117. log.Error("pull.Issue.LoadPoster: %v", err)
  118. return
  119. }
  120. if err := models.NotifyWatchers(&models.Action{
  121. ActUserID: pull.Issue.Poster.ID,
  122. ActUser: pull.Issue.Poster,
  123. OpType: models.ActionCreatePullRequest,
  124. Content: fmt.Sprintf("%d|%s", pull.Issue.Index, pull.Issue.Title),
  125. RepoID: pull.Issue.Repo.ID,
  126. Repo: pull.Issue.Repo,
  127. IsPrivate: pull.Issue.Repo.IsPrivate,
  128. }); err != nil {
  129. log.Error("NotifyWatchers: %v", err)
  130. }
  131. }
  132. func (a *actionNotifier) NotifyRenameRepository(doer *models.User, repo *models.Repository, oldRepoName string) {
  133. log.Trace("action.ChangeRepositoryName: %s/%s", doer.Name, repo.Name)
  134. if err := models.NotifyWatchers(&models.Action{
  135. ActUserID: doer.ID,
  136. ActUser: doer,
  137. OpType: models.ActionRenameRepo,
  138. RepoID: repo.ID,
  139. Repo: repo,
  140. IsPrivate: repo.IsPrivate,
  141. Content: oldRepoName,
  142. }); err != nil {
  143. log.Error("NotifyWatchers: %v", err)
  144. }
  145. }
  146. func (a *actionNotifier) NotifyTransferRepository(doer *models.User, repo *models.Repository, oldOwnerName string) {
  147. if err := models.NotifyWatchers(&models.Action{
  148. ActUserID: doer.ID,
  149. ActUser: doer,
  150. OpType: models.ActionTransferRepo,
  151. RepoID: repo.ID,
  152. Repo: repo,
  153. IsPrivate: repo.IsPrivate,
  154. Content: path.Join(oldOwnerName, repo.Name),
  155. }); err != nil {
  156. log.Error("NotifyWatchers: %v", err)
  157. }
  158. }
  159. func (a *actionNotifier) NotifyCreateRepository(doer *models.User, u *models.User, repo *models.Repository) {
  160. if err := models.NotifyWatchers(&models.Action{
  161. ActUserID: doer.ID,
  162. ActUser: doer,
  163. OpType: models.ActionCreateRepo,
  164. RepoID: repo.ID,
  165. Repo: repo,
  166. IsPrivate: repo.IsPrivate,
  167. }); err != nil {
  168. log.Error("notify watchers '%d/%d': %v", doer.ID, repo.ID, err)
  169. }
  170. }
  171. func (a *actionNotifier) NotifyForkRepository(doer *models.User, oldRepo, repo *models.Repository) {
  172. if err := models.NotifyWatchers(&models.Action{
  173. ActUserID: doer.ID,
  174. ActUser: doer,
  175. OpType: models.ActionCreateRepo,
  176. RepoID: repo.ID,
  177. Repo: repo,
  178. IsPrivate: repo.IsPrivate,
  179. }); err != nil {
  180. log.Error("notify watchers '%d/%d': %v", doer.ID, repo.ID, err)
  181. }
  182. }
  183. func (a *actionNotifier) NotifyPullRequestReview(pr *models.PullRequest, review *models.Review, comment *models.Comment) {
  184. if err := review.LoadReviewer(); err != nil {
  185. log.Error("LoadReviewer '%d/%d': %v", review.ID, review.ReviewerID, err)
  186. return
  187. }
  188. if err := review.LoadCodeComments(); err != nil {
  189. log.Error("LoadCodeComments '%d/%d': %v", review.Reviewer.ID, review.ID, err)
  190. return
  191. }
  192. var actions = make([]*models.Action, 0, 10)
  193. for _, lines := range review.CodeComments {
  194. for _, comments := range lines {
  195. for _, comm := range comments {
  196. actions = append(actions, &models.Action{
  197. ActUserID: review.Reviewer.ID,
  198. ActUser: review.Reviewer,
  199. Content: fmt.Sprintf("%d|%s", review.Issue.Index, strings.Split(comm.Content, "\n")[0]),
  200. OpType: models.ActionCommentPull,
  201. RepoID: review.Issue.RepoID,
  202. Repo: review.Issue.Repo,
  203. IsPrivate: review.Issue.Repo.IsPrivate,
  204. Comment: comm,
  205. CommentID: comm.ID,
  206. })
  207. }
  208. }
  209. }
  210. if review.Type != models.ReviewTypeComment || strings.TrimSpace(comment.Content) != "" {
  211. action := &models.Action{
  212. ActUserID: review.Reviewer.ID,
  213. ActUser: review.Reviewer,
  214. Content: fmt.Sprintf("%d|%s", review.Issue.Index, strings.Split(comment.Content, "\n")[0]),
  215. RepoID: review.Issue.RepoID,
  216. Repo: review.Issue.Repo,
  217. IsPrivate: review.Issue.Repo.IsPrivate,
  218. Comment: comment,
  219. CommentID: comment.ID,
  220. }
  221. switch review.Type {
  222. case models.ReviewTypeApprove:
  223. action.OpType = models.ActionApprovePullRequest
  224. case models.ReviewTypeReject:
  225. action.OpType = models.ActionRejectPullRequest
  226. default:
  227. action.OpType = models.ActionCommentPull
  228. }
  229. actions = append(actions, action)
  230. }
  231. if err := models.NotifyWatchersActions(actions); err != nil {
  232. log.Error("notify watchers '%d/%d': %v", review.Reviewer.ID, review.Issue.RepoID, err)
  233. }
  234. }
  235. func (*actionNotifier) NotifyMergePullRequest(pr *models.PullRequest, doer *models.User) {
  236. if err := models.NotifyWatchers(&models.Action{
  237. ActUserID: doer.ID,
  238. ActUser: doer,
  239. OpType: models.ActionMergePullRequest,
  240. Content: fmt.Sprintf("%d|%s", pr.Issue.Index, pr.Issue.Title),
  241. RepoID: pr.Issue.Repo.ID,
  242. Repo: pr.Issue.Repo,
  243. IsPrivate: pr.Issue.Repo.IsPrivate,
  244. }); err != nil {
  245. log.Error("NotifyWatchers [%d]: %v", pr.ID, err)
  246. }
  247. }
  248. func (a *actionNotifier) NotifySyncPushCommits(pusher *models.User, repo *models.Repository, refName, oldCommitID, newCommitID string, commits *repository.PushCommits) {
  249. data, err := json.Marshal(commits)
  250. if err != nil {
  251. log.Error("json.Marshal: %v", err)
  252. return
  253. }
  254. if err := models.NotifyWatchers(&models.Action{
  255. ActUserID: repo.OwnerID,
  256. ActUser: repo.MustOwner(),
  257. OpType: models.ActionMirrorSyncPush,
  258. RepoID: repo.ID,
  259. Repo: repo,
  260. IsPrivate: repo.IsPrivate,
  261. RefName: refName,
  262. Content: string(data),
  263. }); err != nil {
  264. log.Error("notifyWatchers: %v", err)
  265. }
  266. }
  267. func (a *actionNotifier) NotifySyncCreateRef(doer *models.User, repo *models.Repository, refType, refFullName string) {
  268. if err := models.NotifyWatchers(&models.Action{
  269. ActUserID: repo.OwnerID,
  270. ActUser: repo.MustOwner(),
  271. OpType: models.ActionMirrorSyncCreate,
  272. RepoID: repo.ID,
  273. Repo: repo,
  274. IsPrivate: repo.IsPrivate,
  275. RefName: refFullName,
  276. }); err != nil {
  277. log.Error("notifyWatchers: %v", err)
  278. }
  279. }
  280. func (a *actionNotifier) NotifySyncDeleteRef(doer *models.User, repo *models.Repository, refType, refFullName string) {
  281. if err := models.NotifyWatchers(&models.Action{
  282. ActUserID: repo.OwnerID,
  283. ActUser: repo.MustOwner(),
  284. OpType: models.ActionMirrorSyncCreate,
  285. RepoID: repo.ID,
  286. Repo: repo,
  287. IsPrivate: repo.IsPrivate,
  288. RefName: refFullName,
  289. }); err != nil {
  290. log.Error("notifyWatchers: %v", err)
  291. }
  292. }
  293. func (a *actionNotifier) NotifyNewRelease(rel *models.Release) {
  294. if err := rel.LoadAttributes(); err != nil {
  295. log.Error("NotifyNewRelease: %v", err)
  296. return
  297. }
  298. if err := models.NotifyWatchers(&models.Action{
  299. ActUserID: rel.PublisherID,
  300. ActUser: rel.Publisher,
  301. OpType: models.ActionPublishRelease,
  302. RepoID: rel.RepoID,
  303. Repo: rel.Repo,
  304. IsPrivate: rel.Repo.IsPrivate,
  305. Content: rel.Title,
  306. RefName: rel.TagName,
  307. }); err != nil {
  308. log.Error("notifyWatchers: %v", err)
  309. }
  310. }