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