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