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.5KB

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