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

action.go 13 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  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, mentions []*models.User) {
  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, mentions []*models.User) {
  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 = comment.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, mentions []*models.User) {
  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, mentions []*models.User) {
  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 (*actionNotifier) NotifyPullRevieweDismiss(doer *models.User, review *models.Review, comment *models.Comment) {
  249. reviewerName := review.Reviewer.Name
  250. if len(review.OriginalAuthor) > 0 {
  251. reviewerName = review.OriginalAuthor
  252. }
  253. if err := models.NotifyWatchers(&models.Action{
  254. ActUserID: doer.ID,
  255. ActUser: doer,
  256. OpType: models.ActionPullReviewDismissed,
  257. Content: fmt.Sprintf("%d|%s|%s", review.Issue.Index, reviewerName, comment.Content),
  258. RepoID: review.Issue.Repo.ID,
  259. Repo: review.Issue.Repo,
  260. IsPrivate: review.Issue.Repo.IsPrivate,
  261. CommentID: comment.ID,
  262. Comment: comment,
  263. }); err != nil {
  264. log.Error("NotifyWatchers [%d]: %v", review.Issue.ID, err)
  265. }
  266. }
  267. func (a *actionNotifier) NotifyPushCommits(pusher *models.User, repo *models.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
  268. data, err := json.Marshal(commits)
  269. if err != nil {
  270. log.Error("Marshal: %v", err)
  271. return
  272. }
  273. opType := models.ActionCommitRepo
  274. // Check it's tag push or branch.
  275. if opts.IsTag() {
  276. opType = models.ActionPushTag
  277. if opts.IsDelRef() {
  278. opType = models.ActionDeleteTag
  279. }
  280. } else if opts.IsDelRef() {
  281. opType = models.ActionDeleteBranch
  282. }
  283. if err = models.NotifyWatchers(&models.Action{
  284. ActUserID: pusher.ID,
  285. ActUser: pusher,
  286. OpType: opType,
  287. Content: string(data),
  288. RepoID: repo.ID,
  289. Repo: repo,
  290. RefName: opts.RefFullName,
  291. IsPrivate: repo.IsPrivate,
  292. }); err != nil {
  293. log.Error("notifyWatchers: %v", err)
  294. }
  295. }
  296. func (a *actionNotifier) NotifyCreateRef(doer *models.User, repo *models.Repository, refType, refFullName string) {
  297. opType := models.ActionCommitRepo
  298. if refType == "tag" {
  299. opType = models.ActionPushTag
  300. }
  301. if err := models.NotifyWatchers(&models.Action{
  302. ActUserID: doer.ID,
  303. ActUser: doer,
  304. OpType: opType,
  305. RepoID: repo.ID,
  306. Repo: repo,
  307. IsPrivate: repo.IsPrivate,
  308. RefName: refFullName,
  309. }); err != nil {
  310. log.Error("notifyWatchers: %v", err)
  311. }
  312. }
  313. func (a *actionNotifier) NotifyDeleteRef(doer *models.User, repo *models.Repository, refType, refFullName string) {
  314. opType := models.ActionDeleteBranch
  315. if refType == "tag" {
  316. opType = models.ActionDeleteTag
  317. }
  318. if err := models.NotifyWatchers(&models.Action{
  319. ActUserID: doer.ID,
  320. ActUser: doer,
  321. OpType: opType,
  322. RepoID: repo.ID,
  323. Repo: repo,
  324. IsPrivate: repo.IsPrivate,
  325. RefName: refFullName,
  326. }); err != nil {
  327. log.Error("notifyWatchers: %v", err)
  328. }
  329. }
  330. func (a *actionNotifier) NotifySyncPushCommits(pusher *models.User, repo *models.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
  331. data, err := json.Marshal(commits)
  332. if err != nil {
  333. log.Error("json.Marshal: %v", err)
  334. return
  335. }
  336. if err := models.NotifyWatchers(&models.Action{
  337. ActUserID: repo.OwnerID,
  338. ActUser: repo.MustOwner(),
  339. OpType: models.ActionMirrorSyncPush,
  340. RepoID: repo.ID,
  341. Repo: repo,
  342. IsPrivate: repo.IsPrivate,
  343. RefName: opts.RefFullName,
  344. Content: string(data),
  345. }); err != nil {
  346. log.Error("notifyWatchers: %v", err)
  347. }
  348. }
  349. func (a *actionNotifier) NotifySyncCreateRef(doer *models.User, repo *models.Repository, refType, refFullName string) {
  350. if err := models.NotifyWatchers(&models.Action{
  351. ActUserID: repo.OwnerID,
  352. ActUser: repo.MustOwner(),
  353. OpType: models.ActionMirrorSyncCreate,
  354. RepoID: repo.ID,
  355. Repo: repo,
  356. IsPrivate: repo.IsPrivate,
  357. RefName: refFullName,
  358. }); err != nil {
  359. log.Error("notifyWatchers: %v", err)
  360. }
  361. }
  362. func (a *actionNotifier) NotifySyncDeleteRef(doer *models.User, repo *models.Repository, refType, refFullName string) {
  363. if err := models.NotifyWatchers(&models.Action{
  364. ActUserID: repo.OwnerID,
  365. ActUser: repo.MustOwner(),
  366. OpType: models.ActionMirrorSyncDelete,
  367. RepoID: repo.ID,
  368. Repo: repo,
  369. IsPrivate: repo.IsPrivate,
  370. RefName: refFullName,
  371. }); err != nil {
  372. log.Error("notifyWatchers: %v", err)
  373. }
  374. }
  375. func (a *actionNotifier) NotifyNewRelease(rel *models.Release) {
  376. if err := rel.LoadAttributes(); err != nil {
  377. log.Error("NotifyNewRelease: %v", err)
  378. return
  379. }
  380. if err := models.NotifyWatchers(&models.Action{
  381. ActUserID: rel.PublisherID,
  382. ActUser: rel.Publisher,
  383. OpType: models.ActionPublishRelease,
  384. RepoID: rel.RepoID,
  385. Repo: rel.Repo,
  386. IsPrivate: rel.Repo.IsPrivate,
  387. Content: rel.Title,
  388. RefName: rel.TagName,
  389. }); err != nil {
  390. log.Error("notifyWatchers: %v", err)
  391. }
  392. }