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 12 kB

3 years ago
3 years ago
3 years ago
2 years ago
2 years ago
2 years ago
3 years ago
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  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. "code.gitea.io/gitea/modules/auth"
  7. "encoding/json"
  8. "fmt"
  9. "path"
  10. "strings"
  11. "code.gitea.io/gitea/models"
  12. "code.gitea.io/gitea/modules/log"
  13. "code.gitea.io/gitea/modules/notification/base"
  14. "code.gitea.io/gitea/modules/repository"
  15. )
  16. type actionNotifier struct {
  17. base.NullNotifier
  18. }
  19. var (
  20. _ base.Notifier = &actionNotifier{}
  21. )
  22. // NewNotifier create a new actionNotifier notifier
  23. func NewNotifier() base.Notifier {
  24. return &actionNotifier{}
  25. }
  26. func (a *actionNotifier) NotifyNewIssue(issue *models.Issue) {
  27. if err := issue.LoadPoster(); err != nil {
  28. log.Error("issue.LoadPoster: %v", err)
  29. return
  30. }
  31. if err := issue.LoadRepo(); err != nil {
  32. log.Error("issue.LoadRepo: %v", err)
  33. return
  34. }
  35. repo := issue.Repo
  36. if err := models.NotifyWatchers(&models.Action{
  37. ActUserID: issue.Poster.ID,
  38. ActUser: issue.Poster,
  39. OpType: models.ActionCreateIssue,
  40. Content: fmt.Sprintf("%d|%s", issue.Index, issue.Title),
  41. RepoID: repo.ID,
  42. Repo: repo,
  43. IsPrivate: repo.IsPrivate,
  44. }); err != nil {
  45. log.Error("NotifyWatchers: %v", err)
  46. }
  47. }
  48. // NotifyIssueChangeStatus notifies close or reopen issue to notifiers
  49. func (a *actionNotifier) NotifyIssueChangeStatus(doer *models.User, issue *models.Issue, actionComment *models.Comment, closeOrReopen bool) {
  50. // Compose comment action, could be plain comment, close or reopen issue/pull request.
  51. // This object will be used to notify watchers in the end of function.
  52. act := &models.Action{
  53. ActUserID: doer.ID,
  54. ActUser: doer,
  55. Content: fmt.Sprintf("%d|%s", issue.Index, ""),
  56. RepoID: issue.Repo.ID,
  57. Repo: issue.Repo,
  58. Comment: actionComment,
  59. CommentID: actionComment.ID,
  60. IsPrivate: issue.Repo.IsPrivate,
  61. }
  62. // Check comment type.
  63. if closeOrReopen {
  64. act.OpType = models.ActionCloseIssue
  65. if issue.IsPull {
  66. act.OpType = models.ActionClosePullRequest
  67. }
  68. } else {
  69. act.OpType = models.ActionReopenIssue
  70. if issue.IsPull {
  71. act.OpType = models.ActionReopenPullRequest
  72. }
  73. }
  74. // Notify watchers for whatever action comes in, ignore if no action type.
  75. if err := models.NotifyWatchers(act); err != nil {
  76. log.Error("NotifyWatchers: %v", err)
  77. }
  78. }
  79. // NotifyCreateIssueComment notifies comment on an issue to notifiers
  80. func (a *actionNotifier) NotifyCreateIssueComment(doer *models.User, repo *models.Repository,
  81. issue *models.Issue, comment *models.Comment) {
  82. act := &models.Action{
  83. ActUserID: doer.ID,
  84. ActUser: doer,
  85. Content: fmt.Sprintf("%d|%s", issue.Index, comment.Content),
  86. RepoID: issue.Repo.ID,
  87. Repo: issue.Repo,
  88. Comment: comment,
  89. CommentID: comment.ID,
  90. IsPrivate: issue.Repo.IsPrivate,
  91. }
  92. if issue.IsPull {
  93. act.OpType = models.ActionCommentPull
  94. } else {
  95. act.OpType = models.ActionCommentIssue
  96. }
  97. // Notify watchers for whatever action comes in, ignore if no action type.
  98. if err := models.NotifyWatchers(act); err != nil {
  99. log.Error("NotifyWatchers: %v", err)
  100. }
  101. }
  102. func (a *actionNotifier) NotifyNewPullRequest(pull *models.PullRequest) {
  103. if err := pull.LoadIssue(); err != nil {
  104. log.Error("pull.LoadIssue: %v", err)
  105. return
  106. }
  107. if err := pull.Issue.LoadRepo(); err != nil {
  108. log.Error("pull.Issue.LoadRepo: %v", err)
  109. return
  110. }
  111. if err := pull.Issue.LoadPoster(); err != nil {
  112. log.Error("pull.Issue.LoadPoster: %v", err)
  113. return
  114. }
  115. if err := models.NotifyWatchers(&models.Action{
  116. ActUserID: pull.Issue.Poster.ID,
  117. ActUser: pull.Issue.Poster,
  118. OpType: models.ActionCreatePullRequest,
  119. Content: fmt.Sprintf("%d|%s", pull.Issue.Index, pull.Issue.Title),
  120. RepoID: pull.Issue.Repo.ID,
  121. Repo: pull.Issue.Repo,
  122. IsPrivate: pull.Issue.Repo.IsPrivate,
  123. }); err != nil {
  124. log.Error("NotifyWatchers: %v", err)
  125. }
  126. }
  127. func (a *actionNotifier) NotifyRenameRepository(doer *models.User, repo *models.Repository, oldRepoName string) {
  128. log.Trace("action.ChangeRepositoryName: %s/%s", doer.Name, repo.Name)
  129. if err := models.NotifyWatchers(&models.Action{
  130. ActUserID: doer.ID,
  131. ActUser: doer,
  132. OpType: models.ActionRenameRepo,
  133. RepoID: repo.ID,
  134. Repo: repo,
  135. IsPrivate: repo.IsPrivate,
  136. Content: oldRepoName,
  137. }); err != nil {
  138. log.Error("NotifyWatchers: %v", err)
  139. }
  140. }
  141. func (a *actionNotifier) NotifyAliasRepository(doer *models.User, repo *models.Repository, oldAlias string) {
  142. log.Trace("action.ChangeRepositoryAlias: %s/%s", doer.Name, repo.Alias)
  143. if err := models.NotifyWatchers(&models.Action{
  144. ActUserID: doer.ID,
  145. ActUser: doer,
  146. OpType: models.ActionRenameRepo,
  147. RepoID: repo.ID,
  148. Repo: repo,
  149. IsPrivate: repo.IsPrivate,
  150. Content: oldAlias,
  151. }); err != nil {
  152. log.Error("NotifyWatchers: %v", err)
  153. }
  154. }
  155. func (a *actionNotifier) NotifyTransferRepository(doer *models.User, repo *models.Repository, oldOwnerName string) {
  156. if err := models.NotifyWatchers(&models.Action{
  157. ActUserID: doer.ID,
  158. ActUser: doer,
  159. OpType: models.ActionTransferRepo,
  160. RepoID: repo.ID,
  161. Repo: repo,
  162. IsPrivate: repo.IsPrivate,
  163. Content: path.Join(oldOwnerName, repo.Name),
  164. }); err != nil {
  165. log.Error("NotifyWatchers: %v", err)
  166. }
  167. }
  168. func (a *actionNotifier) NotifyCreateRepository(doer *models.User, u *models.User, repo *models.Repository) {
  169. if err := models.NotifyWatchers(&models.Action{
  170. ActUserID: doer.ID,
  171. ActUser: doer,
  172. OpType: models.ActionCreateRepo,
  173. RepoID: repo.ID,
  174. Repo: repo,
  175. IsPrivate: repo.IsPrivate,
  176. }); err != nil {
  177. log.Error("notify watchers '%d/%d': %v", doer.ID, repo.ID, err)
  178. }
  179. }
  180. func (a *actionNotifier) NotifyForkRepository(doer *models.User, oldRepo, repo *models.Repository) {
  181. if err := models.NotifyWatchers(&models.Action{
  182. ActUserID: doer.ID,
  183. ActUser: doer,
  184. OpType: models.ActionCreateRepo,
  185. RepoID: repo.ID,
  186. Repo: repo,
  187. IsPrivate: repo.IsPrivate,
  188. }); err != nil {
  189. log.Error("notify watchers '%d/%d': %v", doer.ID, repo.ID, err)
  190. }
  191. }
  192. func (a *actionNotifier) NotifyPullRequestReview(pr *models.PullRequest, review *models.Review, comment *models.Comment) {
  193. if err := review.LoadReviewer(); err != nil {
  194. log.Error("LoadReviewer '%d/%d': %v", review.ID, review.ReviewerID, err)
  195. return
  196. }
  197. if err := review.LoadCodeComments(); err != nil {
  198. log.Error("LoadCodeComments '%d/%d': %v", review.Reviewer.ID, review.ID, err)
  199. return
  200. }
  201. var actions = make([]*models.Action, 0, 10)
  202. for _, lines := range review.CodeComments {
  203. for _, comments := range lines {
  204. for _, comm := range comments {
  205. actions = append(actions, &models.Action{
  206. ActUserID: review.Reviewer.ID,
  207. ActUser: review.Reviewer,
  208. Content: fmt.Sprintf("%d|%s", review.Issue.Index, strings.Split(comm.Content, "\n")[0]),
  209. OpType: models.ActionCommentPull,
  210. RepoID: review.Issue.RepoID,
  211. Repo: review.Issue.Repo,
  212. IsPrivate: review.Issue.Repo.IsPrivate,
  213. Comment: comm,
  214. CommentID: comm.ID,
  215. })
  216. }
  217. }
  218. }
  219. if review.Type != models.ReviewTypeComment || strings.TrimSpace(comment.Content) != "" {
  220. action := &models.Action{
  221. ActUserID: review.Reviewer.ID,
  222. ActUser: review.Reviewer,
  223. Content: fmt.Sprintf("%d|%s", review.Issue.Index, strings.Split(comment.Content, "\n")[0]),
  224. RepoID: review.Issue.RepoID,
  225. Repo: review.Issue.Repo,
  226. IsPrivate: review.Issue.Repo.IsPrivate,
  227. Comment: comment,
  228. CommentID: comment.ID,
  229. }
  230. switch review.Type {
  231. case models.ReviewTypeApprove:
  232. action.OpType = models.ActionApprovePullRequest
  233. case models.ReviewTypeReject:
  234. action.OpType = models.ActionRejectPullRequest
  235. default:
  236. action.OpType = models.ActionCommentPull
  237. }
  238. actions = append(actions, action)
  239. }
  240. if err := models.NotifyWatchersActions(actions); err != nil {
  241. log.Error("notify watchers '%d/%d': %v", review.Reviewer.ID, review.Issue.RepoID, err)
  242. }
  243. }
  244. func (*actionNotifier) NotifyMergePullRequest(pr *models.PullRequest, doer *models.User) {
  245. if err := models.NotifyWatchers(&models.Action{
  246. ActUserID: doer.ID,
  247. ActUser: doer,
  248. OpType: models.ActionMergePullRequest,
  249. Content: fmt.Sprintf("%d|%s", pr.Issue.Index, pr.Issue.Title),
  250. RepoID: pr.Issue.Repo.ID,
  251. Repo: pr.Issue.Repo,
  252. IsPrivate: pr.Issue.Repo.IsPrivate,
  253. }); err != nil {
  254. log.Error("NotifyWatchers [%d]: %v", pr.ID, err)
  255. }
  256. }
  257. func (a *actionNotifier) NotifySyncPushCommits(pusher *models.User, repo *models.Repository, refName, oldCommitID, newCommitID string, commits *repository.PushCommits) {
  258. data, err := json.Marshal(commits)
  259. if err != nil {
  260. log.Error("json.Marshal: %v", err)
  261. return
  262. }
  263. if err := models.NotifyWatchers(&models.Action{
  264. ActUserID: repo.OwnerID,
  265. ActUser: repo.MustOwner(),
  266. OpType: models.ActionMirrorSyncPush,
  267. RepoID: repo.ID,
  268. Repo: repo,
  269. IsPrivate: repo.IsPrivate,
  270. RefName: refName,
  271. Content: string(data),
  272. }); err != nil {
  273. log.Error("notifyWatchers: %v", err)
  274. }
  275. }
  276. func (a *actionNotifier) NotifySyncCreateRef(doer *models.User, repo *models.Repository, refType, refFullName string) {
  277. if err := models.NotifyWatchers(&models.Action{
  278. ActUserID: repo.OwnerID,
  279. ActUser: repo.MustOwner(),
  280. OpType: models.ActionMirrorSyncCreate,
  281. RepoID: repo.ID,
  282. Repo: repo,
  283. IsPrivate: repo.IsPrivate,
  284. RefName: refFullName,
  285. }); err != nil {
  286. log.Error("notifyWatchers: %v", err)
  287. }
  288. }
  289. func (a *actionNotifier) NotifySyncDeleteRef(doer *models.User, repo *models.Repository, refType, refFullName string) {
  290. if err := models.NotifyWatchers(&models.Action{
  291. ActUserID: repo.OwnerID,
  292. ActUser: repo.MustOwner(),
  293. OpType: models.ActionMirrorSyncCreate,
  294. RepoID: repo.ID,
  295. Repo: repo,
  296. IsPrivate: repo.IsPrivate,
  297. RefName: refFullName,
  298. }); err != nil {
  299. log.Error("notifyWatchers: %v", err)
  300. }
  301. }
  302. func (a *actionNotifier) NotifyOtherTask(doer *models.User, repo *models.Repository, id string, name string, optype models.ActionType) {
  303. if err := models.NotifyWatchers(&models.Action{
  304. ActUserID: doer.ID,
  305. ActUser: doer,
  306. OpType: optype,
  307. RepoID: repo.ID,
  308. Repo: repo,
  309. IsPrivate: repo.IsPrivate,
  310. RefName: name,
  311. Content: id,
  312. }); err != nil {
  313. log.Error("notifyWatchers: %v", err)
  314. }
  315. }
  316. func (t *actionNotifier) NotifyWechatBind(user *models.User, wechatOpenId string) {
  317. act := &models.Action{
  318. ActUserID: user.ID,
  319. ActUser: user,
  320. OpType: models.ActionBindWechat,
  321. IsPrivate: true,
  322. Content: wechatOpenId,
  323. }
  324. if err := models.NotifyWatchers(act); err != nil {
  325. log.Error("notifyWatchers: %v", err)
  326. }
  327. }
  328. func (t *actionNotifier) NotifyDatasetRecommend(optUser *models.User, dataset *models.Dataset, action string) {
  329. switch action {
  330. case "recommend":
  331. act := &models.Action{
  332. OpType: models.ActionDatasetRecommended,
  333. ActUserID: dataset.UserID,
  334. RepoID: dataset.RepoID,
  335. IsPrivate: false,
  336. Content: fmt.Sprintf("%d|%s", dataset.ID, dataset.Title),
  337. }
  338. if err := models.NotifyWatchers(act); err != nil {
  339. log.Error("notifyWatchers: %v", err)
  340. }
  341. }
  342. }
  343. func (t *actionNotifier) NotifyCreateImage(doer *models.User, image models.Image) {
  344. act := &models.Action{
  345. ActUserID: doer.ID,
  346. ActUser: doer,
  347. OpType: models.ActionCreateImage,
  348. IsPrivate: image.IsPrivate,
  349. Content: fmt.Sprintf("%d|%s", image.ID, image.Tag),
  350. }
  351. if err := models.NotifyWatchers(act); err != nil {
  352. log.Error("notifyWatchers: %v", err)
  353. }
  354. }
  355. func (t *actionNotifier) NotifyImageRecommend(optUser *models.User, image *models.Image, action string) {
  356. u, err := models.GetUserByID(image.UID)
  357. if err != nil {
  358. return
  359. }
  360. switch action {
  361. case "recommend":
  362. act := &models.Action{
  363. ActUserID: u.ID,
  364. ActUser: u,
  365. OpType: models.ActionImageRecommend,
  366. IsPrivate: false,
  367. Content: fmt.Sprintf("%d|%s", image.ID, image.Tag),
  368. }
  369. if err := models.NotifyWatchers(act); err != nil {
  370. log.Error("notifyWatchers: %v", err)
  371. }
  372. }
  373. }
  374. func (t *actionNotifier) NotifyChangeUserAvatar(user *models.User, form auth.AvatarForm) {
  375. act := &models.Action{
  376. ActUserID: user.ID,
  377. ActUser: user,
  378. OpType: models.ActionChangeUserAvatar,
  379. IsPrivate: true,
  380. }
  381. if err := models.NotifyWatchers(act); err != nil {
  382. log.Error("notifyWatchers: %v", err)
  383. }
  384. }