|
@@ -0,0 +1,249 @@
|
|
1
|
+// Copyright 2016 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
|
+
|
|
5
|
+package models
|
|
6
|
+
|
|
7
|
+import (
|
|
8
|
+ "time"
|
|
9
|
+)
|
|
10
|
+
|
|
11
|
+type (
|
|
12
|
+ // NotificationStatus is the status of the notification (read or unread)
|
|
13
|
+ NotificationStatus uint8
|
|
14
|
+ // NotificationSource is the source of the notification (issue, PR, commit, etc)
|
|
15
|
+ NotificationSource uint8
|
|
16
|
+)
|
|
17
|
+
|
|
18
|
+const (
|
|
19
|
+ // NotificationStatusUnread represents an unread notification
|
|
20
|
+ NotificationStatusUnread NotificationStatus = iota + 1
|
|
21
|
+ // NotificationStatusRead represents a read notification
|
|
22
|
+ NotificationStatusRead
|
|
23
|
+)
|
|
24
|
+
|
|
25
|
+const (
|
|
26
|
+ // NotificationSourceIssue is a notification of an issue
|
|
27
|
+ NotificationSourceIssue NotificationSource = iota + 1
|
|
28
|
+ // NotificationSourcePullRequest is a notification of a pull request
|
|
29
|
+ NotificationSourcePullRequest
|
|
30
|
+ // NotificationSourceCommit is a notification of a commit
|
|
31
|
+ NotificationSourceCommit
|
|
32
|
+)
|
|
33
|
+
|
|
34
|
+// Notification represents a notification
|
|
35
|
+type Notification struct {
|
|
36
|
+ ID int64 `xorm:"pk autoincr"`
|
|
37
|
+ UserID int64 `xorm:"INDEX NOT NULL"`
|
|
38
|
+ RepoID int64 `xorm:"INDEX NOT NULL"`
|
|
39
|
+
|
|
40
|
+ Status NotificationStatus `xorm:"SMALLINT INDEX NOT NULL"`
|
|
41
|
+ Source NotificationSource `xorm:"SMALLINT INDEX NOT NULL"`
|
|
42
|
+
|
|
43
|
+ IssueID int64 `xorm:"INDEX NOT NULL"`
|
|
44
|
+ CommitID string `xorm:"INDEX"`
|
|
45
|
+
|
|
46
|
+ UpdatedBy int64 `xorm:"INDEX NOT NULL"`
|
|
47
|
+
|
|
48
|
+ Issue *Issue `xorm:"-"`
|
|
49
|
+ Repository *Repository `xorm:"-"`
|
|
50
|
+
|
|
51
|
+ Created time.Time `xorm:"-"`
|
|
52
|
+ CreatedUnix int64 `xorm:"INDEX NOT NULL"`
|
|
53
|
+ Updated time.Time `xorm:"-"`
|
|
54
|
+ UpdatedUnix int64 `xorm:"INDEX NOT NULL"`
|
|
55
|
+}
|
|
56
|
+
|
|
57
|
+// BeforeInsert runs while inserting a record
|
|
58
|
+func (n *Notification) BeforeInsert() {
|
|
59
|
+ var (
|
|
60
|
+ now = time.Now()
|
|
61
|
+ nowUnix = now.Unix()
|
|
62
|
+ )
|
|
63
|
+ n.Created = now
|
|
64
|
+ n.CreatedUnix = nowUnix
|
|
65
|
+ n.Updated = now
|
|
66
|
+ n.UpdatedUnix = nowUnix
|
|
67
|
+}
|
|
68
|
+
|
|
69
|
+// BeforeUpdate runs while updateing a record
|
|
70
|
+func (n *Notification) BeforeUpdate() {
|
|
71
|
+ var (
|
|
72
|
+ now = time.Now()
|
|
73
|
+ nowUnix = now.Unix()
|
|
74
|
+ )
|
|
75
|
+ n.Updated = now
|
|
76
|
+ n.UpdatedUnix = nowUnix
|
|
77
|
+}
|
|
78
|
+
|
|
79
|
+// CreateOrUpdateIssueNotifications creates an issue notification
|
|
80
|
+// for each watcher, or updates it if already exists
|
|
81
|
+func CreateOrUpdateIssueNotifications(issue *Issue, notificationAuthorID int64) error {
|
|
82
|
+ sess := x.NewSession()
|
|
83
|
+ defer sess.Close()
|
|
84
|
+ if err := sess.Begin(); err != nil {
|
|
85
|
+ return err
|
|
86
|
+ }
|
|
87
|
+
|
|
88
|
+ if err := createOrUpdateIssueNotifications(sess, issue, notificationAuthorID); err != nil {
|
|
89
|
+ return err
|
|
90
|
+ }
|
|
91
|
+
|
|
92
|
+ return sess.Commit()
|
|
93
|
+}
|
|
94
|
+
|
|
95
|
+func createOrUpdateIssueNotifications(e Engine, issue *Issue, notificationAuthorID int64) error {
|
|
96
|
+ watches, err := getWatchers(e, issue.RepoID)
|
|
97
|
+ if err != nil {
|
|
98
|
+ return err
|
|
99
|
+ }
|
|
100
|
+
|
|
101
|
+ notifications, err := getNotificationsByIssueID(e, issue.ID)
|
|
102
|
+ if err != nil {
|
|
103
|
+ return err
|
|
104
|
+ }
|
|
105
|
+
|
|
106
|
+ for _, watch := range watches {
|
|
107
|
+ // do not send notification for the own issuer/commenter
|
|
108
|
+ if watch.UserID == notificationAuthorID {
|
|
109
|
+ continue
|
|
110
|
+ }
|
|
111
|
+
|
|
112
|
+ if notificationExists(notifications, issue.ID, watch.UserID) {
|
|
113
|
+ err = updateIssueNotification(e, watch.UserID, issue.ID, notificationAuthorID)
|
|
114
|
+ } else {
|
|
115
|
+ err = createIssueNotification(e, watch.UserID, issue, notificationAuthorID)
|
|
116
|
+ }
|
|
117
|
+
|
|
118
|
+ if err != nil {
|
|
119
|
+ return err
|
|
120
|
+ }
|
|
121
|
+ }
|
|
122
|
+
|
|
123
|
+ return nil
|
|
124
|
+}
|
|
125
|
+
|
|
126
|
+func getNotificationsByIssueID(e Engine, issueID int64) (notifications []*Notification, err error) {
|
|
127
|
+ err = e.
|
|
128
|
+ Where("issue_id = ?", issueID).
|
|
129
|
+ Find(¬ifications)
|
|
130
|
+ return
|
|
131
|
+}
|
|
132
|
+
|
|
133
|
+func notificationExists(notifications []*Notification, issueID, userID int64) bool {
|
|
134
|
+ for _, notification := range notifications {
|
|
135
|
+ if notification.IssueID == issueID && notification.UserID == userID {
|
|
136
|
+ return true
|
|
137
|
+ }
|
|
138
|
+ }
|
|
139
|
+
|
|
140
|
+ return false
|
|
141
|
+}
|
|
142
|
+
|
|
143
|
+func createIssueNotification(e Engine, userID int64, issue *Issue, updatedByID int64) error {
|
|
144
|
+ notification := &Notification{
|
|
145
|
+ UserID: userID,
|
|
146
|
+ RepoID: issue.RepoID,
|
|
147
|
+ Status: NotificationStatusUnread,
|
|
148
|
+ IssueID: issue.ID,
|
|
149
|
+ UpdatedBy: updatedByID,
|
|
150
|
+ }
|
|
151
|
+
|
|
152
|
+ if issue.IsPull {
|
|
153
|
+ notification.Source = NotificationSourcePullRequest
|
|
154
|
+ } else {
|
|
155
|
+ notification.Source = NotificationSourceIssue
|
|
156
|
+ }
|
|
157
|
+
|
|
158
|
+ _, err := e.Insert(notification)
|
|
159
|
+ return err
|
|
160
|
+}
|
|
161
|
+
|
|
162
|
+func updateIssueNotification(e Engine, userID, issueID, updatedByID int64) error {
|
|
163
|
+ notification, err := getIssueNotification(e, userID, issueID)
|
|
164
|
+ if err != nil {
|
|
165
|
+ return err
|
|
166
|
+ }
|
|
167
|
+
|
|
168
|
+ notification.Status = NotificationStatusUnread
|
|
169
|
+ notification.UpdatedBy = updatedByID
|
|
170
|
+
|
|
171
|
+ _, err = e.Id(notification.ID).Update(notification)
|
|
172
|
+ return err
|
|
173
|
+}
|
|
174
|
+
|
|
175
|
+func getIssueNotification(e Engine, userID, issueID int64) (*Notification, error) {
|
|
176
|
+ notification := new(Notification)
|
|
177
|
+ _, err := e.
|
|
178
|
+ Where("user_id = ?", userID).
|
|
179
|
+ And("issue_id = ?", issueID).
|
|
180
|
+ Get(notification)
|
|
181
|
+ return notification, err
|
|
182
|
+}
|
|
183
|
+
|
|
184
|
+// NotificationsForUser returns notifications for a given user and status
|
|
185
|
+func NotificationsForUser(user *User, status NotificationStatus) ([]*Notification, error) {
|
|
186
|
+ return notificationsForUser(x, user, status)
|
|
187
|
+}
|
|
188
|
+func notificationsForUser(e Engine, user *User, status NotificationStatus) (notifications []*Notification, err error) {
|
|
189
|
+ err = e.
|
|
190
|
+ Where("user_id = ?", user.ID).
|
|
191
|
+ And("status = ?", status).
|
|
192
|
+ OrderBy("updated_unix DESC").
|
|
193
|
+ Find(¬ifications)
|
|
194
|
+ return
|
|
195
|
+}
|
|
196
|
+
|
|
197
|
+// GetRepo returns the repo of the notification
|
|
198
|
+func (n *Notification) GetRepo() (*Repository, error) {
|
|
199
|
+ n.Repository = new(Repository)
|
|
200
|
+ _, err := x.
|
|
201
|
+ Where("id = ?", n.RepoID).
|
|
202
|
+ Get(n.Repository)
|
|
203
|
+ return n.Repository, err
|
|
204
|
+}
|
|
205
|
+
|
|
206
|
+// GetIssue returns the issue of the notification
|
|
207
|
+func (n *Notification) GetIssue() (*Issue, error) {
|
|
208
|
+ n.Issue = new(Issue)
|
|
209
|
+ _, err := x.
|
|
210
|
+ Where("id = ?", n.IssueID).
|
|
211
|
+ Get(n.Issue)
|
|
212
|
+ return n.Issue, err
|
|
213
|
+}
|
|
214
|
+
|
|
215
|
+// GetNotificationReadCount returns the notification read count for user
|
|
216
|
+func GetNotificationReadCount(user *User) (int64, error) {
|
|
217
|
+ return GetNotificationCount(user, NotificationStatusRead)
|
|
218
|
+}
|
|
219
|
+
|
|
220
|
+// GetNotificationUnreadCount returns the notification unread count for user
|
|
221
|
+func GetNotificationUnreadCount(user *User) (int64, error) {
|
|
222
|
+ return GetNotificationCount(user, NotificationStatusUnread)
|
|
223
|
+}
|
|
224
|
+
|
|
225
|
+// GetNotificationCount returns the notification count for user
|
|
226
|
+func GetNotificationCount(user *User, status NotificationStatus) (int64, error) {
|
|
227
|
+ return getNotificationCount(x, user, status)
|
|
228
|
+}
|
|
229
|
+
|
|
230
|
+func getNotificationCount(e Engine, user *User, status NotificationStatus) (count int64, err error) {
|
|
231
|
+ count, err = e.
|
|
232
|
+ Where("user_id = ?", user.ID).
|
|
233
|
+ And("status = ?", status).
|
|
234
|
+ Count(&Notification{})
|
|
235
|
+ return
|
|
236
|
+}
|
|
237
|
+
|
|
238
|
+func setNotificationStatusRead(e Engine, userID, issueID int64) error {
|
|
239
|
+ notification, err := getIssueNotification(e, userID, issueID)
|
|
240
|
+ // ignore if not exists
|
|
241
|
+ if err != nil {
|
|
242
|
+ return nil
|
|
243
|
+ }
|
|
244
|
+
|
|
245
|
+ notification.Status = NotificationStatusRead
|
|
246
|
+
|
|
247
|
+ _, err = e.Id(notification.ID).Update(notification)
|
|
248
|
+ return err
|
|
249
|
+}
|