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.

issue_watch.go 4.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. // Copyright 2017 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package issues
  4. import (
  5. "context"
  6. "code.gitea.io/gitea/models/db"
  7. repo_model "code.gitea.io/gitea/models/repo"
  8. user_model "code.gitea.io/gitea/models/user"
  9. "code.gitea.io/gitea/modules/timeutil"
  10. )
  11. // IssueWatch is connection request for receiving issue notification.
  12. type IssueWatch struct {
  13. ID int64 `xorm:"pk autoincr"`
  14. UserID int64 `xorm:"UNIQUE(watch) NOT NULL"`
  15. IssueID int64 `xorm:"UNIQUE(watch) NOT NULL"`
  16. IsWatching bool `xorm:"NOT NULL"`
  17. CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"`
  18. UpdatedUnix timeutil.TimeStamp `xorm:"updated NOT NULL"`
  19. }
  20. func init() {
  21. db.RegisterModel(new(IssueWatch))
  22. }
  23. // IssueWatchList contains IssueWatch
  24. type IssueWatchList []*IssueWatch
  25. // CreateOrUpdateIssueWatch set watching for a user and issue
  26. func CreateOrUpdateIssueWatch(ctx context.Context, userID, issueID int64, isWatching bool) error {
  27. iw, exists, err := GetIssueWatch(ctx, userID, issueID)
  28. if err != nil {
  29. return err
  30. }
  31. if !exists {
  32. iw = &IssueWatch{
  33. UserID: userID,
  34. IssueID: issueID,
  35. IsWatching: isWatching,
  36. }
  37. if _, err := db.GetEngine(ctx).Insert(iw); err != nil {
  38. return err
  39. }
  40. } else {
  41. iw.IsWatching = isWatching
  42. if _, err := db.GetEngine(ctx).ID(iw.ID).Cols("is_watching", "updated_unix").Update(iw); err != nil {
  43. return err
  44. }
  45. }
  46. return nil
  47. }
  48. // GetIssueWatch returns all IssueWatch objects from db by user and issue
  49. // the current Web-UI need iw object for watchers AND explicit non-watchers
  50. func GetIssueWatch(ctx context.Context, userID, issueID int64) (iw *IssueWatch, exists bool, err error) {
  51. iw = new(IssueWatch)
  52. exists, err = db.GetEngine(ctx).
  53. Where("user_id = ?", userID).
  54. And("issue_id = ?", issueID).
  55. Get(iw)
  56. return iw, exists, err
  57. }
  58. // CheckIssueWatch check if an user is watching an issue
  59. // it takes participants and repo watch into account
  60. func CheckIssueWatch(ctx context.Context, user *user_model.User, issue *Issue) (bool, error) {
  61. iw, exist, err := GetIssueWatch(ctx, user.ID, issue.ID)
  62. if err != nil {
  63. return false, err
  64. }
  65. if exist {
  66. return iw.IsWatching, nil
  67. }
  68. w, err := repo_model.GetWatch(ctx, user.ID, issue.RepoID)
  69. if err != nil {
  70. return false, err
  71. }
  72. return repo_model.IsWatchMode(w.Mode) || IsUserParticipantsOfIssue(user, issue), nil
  73. }
  74. // GetIssueWatchersIDs returns IDs of subscribers or explicit unsubscribers to a given issue id
  75. // but avoids joining with `user` for performance reasons
  76. // User permissions must be verified elsewhere if required
  77. func GetIssueWatchersIDs(ctx context.Context, issueID int64, watching bool) ([]int64, error) {
  78. ids := make([]int64, 0, 64)
  79. return ids, db.GetEngine(ctx).Table("issue_watch").
  80. Where("issue_id=?", issueID).
  81. And("is_watching = ?", watching).
  82. Select("user_id").
  83. Find(&ids)
  84. }
  85. // GetIssueWatchers returns watchers/unwatchers of a given issue
  86. func GetIssueWatchers(ctx context.Context, issueID int64, listOptions db.ListOptions) (IssueWatchList, error) {
  87. sess := db.GetEngine(ctx).
  88. Where("`issue_watch`.issue_id = ?", issueID).
  89. And("`issue_watch`.is_watching = ?", true).
  90. And("`user`.is_active = ?", true).
  91. And("`user`.prohibit_login = ?", false).
  92. Join("INNER", "`user`", "`user`.id = `issue_watch`.user_id")
  93. if listOptions.Page != 0 {
  94. sess = db.SetSessionPagination(sess, &listOptions)
  95. watches := make([]*IssueWatch, 0, listOptions.PageSize)
  96. return watches, sess.Find(&watches)
  97. }
  98. watches := make([]*IssueWatch, 0, 8)
  99. return watches, sess.Find(&watches)
  100. }
  101. // CountIssueWatchers count watchers/unwatchers of a given issue
  102. func CountIssueWatchers(ctx context.Context, issueID int64) (int64, error) {
  103. return db.GetEngine(ctx).
  104. Where("`issue_watch`.issue_id = ?", issueID).
  105. And("`issue_watch`.is_watching = ?", true).
  106. And("`user`.is_active = ?", true).
  107. And("`user`.prohibit_login = ?", false).
  108. Join("INNER", "`user`", "`user`.id = `issue_watch`.user_id").Count(new(IssueWatch))
  109. }
  110. // RemoveIssueWatchersByRepoID remove issue watchers by repoID
  111. func RemoveIssueWatchersByRepoID(ctx context.Context, userID, repoID int64) error {
  112. _, err := db.GetEngine(ctx).
  113. Join("INNER", "issue", "`issue`.id = `issue_watch`.issue_id AND `issue`.repo_id = ?", repoID).
  114. Where("`issue_watch`.user_id = ?", userID).
  115. Delete(new(IssueWatch))
  116. return err
  117. }