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