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.

watch.go 5.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. // Copyright 2017 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package repo
  4. import (
  5. "context"
  6. "code.gitea.io/gitea/models/db"
  7. user_model "code.gitea.io/gitea/models/user"
  8. "code.gitea.io/gitea/modules/setting"
  9. "code.gitea.io/gitea/modules/timeutil"
  10. )
  11. // WatchMode specifies what kind of watch the user has on a repository
  12. type WatchMode int8
  13. const (
  14. // WatchModeNone don't watch
  15. WatchModeNone WatchMode = iota // 0
  16. // WatchModeNormal watch repository (from other sources)
  17. WatchModeNormal // 1
  18. // WatchModeDont explicit don't auto-watch
  19. WatchModeDont // 2
  20. // WatchModeAuto watch repository (from AutoWatchOnChanges)
  21. WatchModeAuto // 3
  22. )
  23. // Watch is connection request for receiving repository notification.
  24. type Watch struct {
  25. ID int64 `xorm:"pk autoincr"`
  26. UserID int64 `xorm:"UNIQUE(watch)"`
  27. RepoID int64 `xorm:"UNIQUE(watch)"`
  28. Mode WatchMode `xorm:"SMALLINT NOT NULL DEFAULT 1"`
  29. CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
  30. UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
  31. }
  32. func init() {
  33. db.RegisterModel(new(Watch))
  34. }
  35. // GetWatch gets what kind of subscription a user has on a given repository; returns dummy record if none found
  36. func GetWatch(ctx context.Context, userID, repoID int64) (Watch, error) {
  37. watch := Watch{UserID: userID, RepoID: repoID}
  38. has, err := db.GetEngine(ctx).Get(&watch)
  39. if err != nil {
  40. return watch, err
  41. }
  42. if !has {
  43. watch.Mode = WatchModeNone
  44. }
  45. return watch, nil
  46. }
  47. // IsWatchMode Decodes watchability of WatchMode
  48. func IsWatchMode(mode WatchMode) bool {
  49. return mode != WatchModeNone && mode != WatchModeDont
  50. }
  51. // IsWatching checks if user has watched given repository.
  52. func IsWatching(ctx context.Context, userID, repoID int64) bool {
  53. watch, err := GetWatch(ctx, userID, repoID)
  54. return err == nil && IsWatchMode(watch.Mode)
  55. }
  56. func watchRepoMode(ctx context.Context, watch Watch, mode WatchMode) (err error) {
  57. if watch.Mode == mode {
  58. return nil
  59. }
  60. if mode == WatchModeAuto && (watch.Mode == WatchModeDont || IsWatchMode(watch.Mode)) {
  61. // Don't auto watch if already watching or deliberately not watching
  62. return nil
  63. }
  64. hadrec := watch.Mode != WatchModeNone
  65. needsrec := mode != WatchModeNone
  66. repodiff := 0
  67. if IsWatchMode(mode) && !IsWatchMode(watch.Mode) {
  68. repodiff = 1
  69. } else if !IsWatchMode(mode) && IsWatchMode(watch.Mode) {
  70. repodiff = -1
  71. }
  72. watch.Mode = mode
  73. e := db.GetEngine(ctx)
  74. if !hadrec && needsrec {
  75. watch.Mode = mode
  76. if _, err = e.Insert(watch); err != nil {
  77. return err
  78. }
  79. } else if needsrec {
  80. watch.Mode = mode
  81. if _, err := e.ID(watch.ID).AllCols().Update(watch); err != nil {
  82. return err
  83. }
  84. } else if _, err = e.Delete(Watch{ID: watch.ID}); err != nil {
  85. return err
  86. }
  87. if repodiff != 0 {
  88. _, err = e.Exec("UPDATE `repository` SET num_watches = num_watches + ? WHERE id = ?", repodiff, watch.RepoID)
  89. }
  90. return err
  91. }
  92. // WatchRepoMode watch repository in specific mode.
  93. func WatchRepoMode(userID, repoID int64, mode WatchMode) (err error) {
  94. var watch Watch
  95. if watch, err = GetWatch(db.DefaultContext, userID, repoID); err != nil {
  96. return err
  97. }
  98. return watchRepoMode(db.DefaultContext, watch, mode)
  99. }
  100. // WatchRepo watch or unwatch repository.
  101. func WatchRepo(ctx context.Context, userID, repoID int64, doWatch bool) (err error) {
  102. var watch Watch
  103. if watch, err = GetWatch(ctx, userID, repoID); err != nil {
  104. return err
  105. }
  106. if !doWatch && watch.Mode == WatchModeAuto {
  107. err = watchRepoMode(ctx, watch, WatchModeDont)
  108. } else if !doWatch {
  109. err = watchRepoMode(ctx, watch, WatchModeNone)
  110. } else {
  111. err = watchRepoMode(ctx, watch, WatchModeNormal)
  112. }
  113. return err
  114. }
  115. // GetWatchers returns all watchers of given repository.
  116. func GetWatchers(ctx context.Context, repoID int64) ([]*Watch, error) {
  117. watches := make([]*Watch, 0, 10)
  118. return watches, db.GetEngine(ctx).Where("`watch`.repo_id=?", repoID).
  119. And("`watch`.mode<>?", WatchModeDont).
  120. And("`user`.is_active=?", true).
  121. And("`user`.prohibit_login=?", false).
  122. Join("INNER", "`user`", "`user`.id = `watch`.user_id").
  123. Find(&watches)
  124. }
  125. // GetRepoWatchersIDs returns IDs of watchers for a given repo ID
  126. // but avoids joining with `user` for performance reasons
  127. // User permissions must be verified elsewhere if required
  128. func GetRepoWatchersIDs(ctx context.Context, repoID int64) ([]int64, error) {
  129. ids := make([]int64, 0, 64)
  130. return ids, db.GetEngine(ctx).Table("watch").
  131. Where("watch.repo_id=?", repoID).
  132. And("watch.mode<>?", WatchModeDont).
  133. Select("user_id").
  134. Find(&ids)
  135. }
  136. // GetRepoWatchers returns range of users watching given repository.
  137. func GetRepoWatchers(ctx context.Context, repoID int64, opts db.ListOptions) ([]*user_model.User, error) {
  138. sess := db.GetEngine(ctx).Where("watch.repo_id=?", repoID).
  139. Join("LEFT", "watch", "`user`.id=`watch`.user_id").
  140. And("`watch`.mode<>?", WatchModeDont)
  141. if opts.Page > 0 {
  142. sess = db.SetSessionPagination(sess, &opts)
  143. users := make([]*user_model.User, 0, opts.PageSize)
  144. return users, sess.Find(&users)
  145. }
  146. users := make([]*user_model.User, 0, 8)
  147. return users, sess.Find(&users)
  148. }
  149. // WatchIfAuto subscribes to repo if AutoWatchOnChanges is set
  150. func WatchIfAuto(ctx context.Context, userID, repoID int64, isWrite bool) error {
  151. if !isWrite || !setting.Service.AutoWatchOnChanges {
  152. return nil
  153. }
  154. watch, err := GetWatch(ctx, userID, repoID)
  155. if err != nil {
  156. return err
  157. }
  158. if watch.Mode != WatchModeNone {
  159. return nil
  160. }
  161. return watchRepoMode(ctx, watch, WatchModeAuto)
  162. }