diff options
author | Andrey Nering <andrey.nering@gmail.com> | 2016-12-30 14:44:54 -0200 |
---|---|---|
committer | Lunny Xiao <xiaolunwen@gmail.com> | 2016-12-31 00:44:54 +0800 |
commit | 42904cb98a4b8e7accdac90bc9f06347cb0521f7 (patch) | |
tree | ff7b39817584c93ad9afd2d2136f51f5d4ddb0a0 /models | |
parent | 37eec6c9b7056e1e05d0067aef86d2e61d67f757 (diff) | |
download | gitea-42904cb98a4b8e7accdac90bc9f06347cb0521f7.tar.gz gitea-42904cb98a4b8e7accdac90bc9f06347cb0521f7.zip |
Notification - Step 1 (#523)
* Notification - Step 1
* Add copyright headers
* Cache issue and repository on notification model
Diffstat (limited to 'models')
-rw-r--r-- | models/issue.go | 12 | ||||
-rw-r--r-- | models/models.go | 44 | ||||
-rw-r--r-- | models/notification.go | 249 |
3 files changed, 294 insertions, 11 deletions
diff --git a/models/issue.go b/models/issue.go index 86becdbbae..6303911808 100644 --- a/models/issue.go +++ b/models/issue.go @@ -443,8 +443,16 @@ func (issue *Issue) GetAssignee() (err error) { } // ReadBy sets issue to be read by given user. -func (issue *Issue) ReadBy(uid int64) error { - return UpdateIssueUserByRead(uid, issue.ID) +func (issue *Issue) ReadBy(userID int64) error { + if err := UpdateIssueUserByRead(userID, issue.ID); err != nil { + return err + } + + if err := setNotificationStatusRead(x, userID, issue.ID); err != nil { + return err + } + + return nil } func updateIssueCols(e Engine, issue *Issue, cols ...string) error { diff --git a/models/models.go b/models/models.go index 20812d0ebf..1bebaa602c 100644 --- a/models/models.go +++ b/models/models.go @@ -71,15 +71,41 @@ var ( func init() { tables = append(tables, - new(User), new(PublicKey), new(AccessToken), - new(Repository), new(DeployKey), new(Collaboration), new(Access), new(Upload), - new(Watch), new(Star), new(Follow), new(Action), - new(Issue), new(PullRequest), new(Comment), new(Attachment), new(IssueUser), - new(Label), new(IssueLabel), new(Milestone), - new(Mirror), new(Release), new(LoginSource), new(Webhook), - new(UpdateTask), new(HookTask), - new(Team), new(OrgUser), new(TeamUser), new(TeamRepo), - new(Notice), new(EmailAddress), new(LFSMetaObject)) + new(User), + new(PublicKey), + new(AccessToken), + new(Repository), + new(DeployKey), + new(Collaboration), + new(Access), + new(Upload), + new(Watch), + new(Star), + new(Follow), + new(Action), + new(Issue), + new(PullRequest), + new(Comment), + new(Attachment), + new(Label), + new(IssueLabel), + new(Milestone), + new(Mirror), + new(Release), + new(LoginSource), + new(Webhook), + new(UpdateTask), + new(HookTask), + new(Team), + new(OrgUser), + new(TeamUser), + new(TeamRepo), + new(Notice), + new(EmailAddress), + new(Notification), + new(IssueUser), + new(LFSMetaObject), + ) gonicNames := []string{"SSL", "UID"} for _, name := range gonicNames { diff --git a/models/notification.go b/models/notification.go new file mode 100644 index 0000000000..46d63b4823 --- /dev/null +++ b/models/notification.go @@ -0,0 +1,249 @@ +// Copyright 2016 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package models + +import ( + "time" +) + +type ( + // NotificationStatus is the status of the notification (read or unread) + NotificationStatus uint8 + // NotificationSource is the source of the notification (issue, PR, commit, etc) + NotificationSource uint8 +) + +const ( + // NotificationStatusUnread represents an unread notification + NotificationStatusUnread NotificationStatus = iota + 1 + // NotificationStatusRead represents a read notification + NotificationStatusRead +) + +const ( + // NotificationSourceIssue is a notification of an issue + NotificationSourceIssue NotificationSource = iota + 1 + // NotificationSourcePullRequest is a notification of a pull request + NotificationSourcePullRequest + // NotificationSourceCommit is a notification of a commit + NotificationSourceCommit +) + +// Notification represents a notification +type Notification struct { + ID int64 `xorm:"pk autoincr"` + UserID int64 `xorm:"INDEX NOT NULL"` + RepoID int64 `xorm:"INDEX NOT NULL"` + + Status NotificationStatus `xorm:"SMALLINT INDEX NOT NULL"` + Source NotificationSource `xorm:"SMALLINT INDEX NOT NULL"` + + IssueID int64 `xorm:"INDEX NOT NULL"` + CommitID string `xorm:"INDEX"` + + UpdatedBy int64 `xorm:"INDEX NOT NULL"` + + Issue *Issue `xorm:"-"` + Repository *Repository `xorm:"-"` + + Created time.Time `xorm:"-"` + CreatedUnix int64 `xorm:"INDEX NOT NULL"` + Updated time.Time `xorm:"-"` + UpdatedUnix int64 `xorm:"INDEX NOT NULL"` +} + +// BeforeInsert runs while inserting a record +func (n *Notification) BeforeInsert() { + var ( + now = time.Now() + nowUnix = now.Unix() + ) + n.Created = now + n.CreatedUnix = nowUnix + n.Updated = now + n.UpdatedUnix = nowUnix +} + +// BeforeUpdate runs while updateing a record +func (n *Notification) BeforeUpdate() { + var ( + now = time.Now() + nowUnix = now.Unix() + ) + n.Updated = now + n.UpdatedUnix = nowUnix +} + +// CreateOrUpdateIssueNotifications creates an issue notification +// for each watcher, or updates it if already exists +func CreateOrUpdateIssueNotifications(issue *Issue, notificationAuthorID int64) error { + sess := x.NewSession() + defer sess.Close() + if err := sess.Begin(); err != nil { + return err + } + + if err := createOrUpdateIssueNotifications(sess, issue, notificationAuthorID); err != nil { + return err + } + + return sess.Commit() +} + +func createOrUpdateIssueNotifications(e Engine, issue *Issue, notificationAuthorID int64) error { + watches, err := getWatchers(e, issue.RepoID) + if err != nil { + return err + } + + notifications, err := getNotificationsByIssueID(e, issue.ID) + if err != nil { + return err + } + + for _, watch := range watches { + // do not send notification for the own issuer/commenter + if watch.UserID == notificationAuthorID { + continue + } + + if notificationExists(notifications, issue.ID, watch.UserID) { + err = updateIssueNotification(e, watch.UserID, issue.ID, notificationAuthorID) + } else { + err = createIssueNotification(e, watch.UserID, issue, notificationAuthorID) + } + + if err != nil { + return err + } + } + + return nil +} + +func getNotificationsByIssueID(e Engine, issueID int64) (notifications []*Notification, err error) { + err = e. + Where("issue_id = ?", issueID). + Find(¬ifications) + return +} + +func notificationExists(notifications []*Notification, issueID, userID int64) bool { + for _, notification := range notifications { + if notification.IssueID == issueID && notification.UserID == userID { + return true + } + } + + return false +} + +func createIssueNotification(e Engine, userID int64, issue *Issue, updatedByID int64) error { + notification := &Notification{ + UserID: userID, + RepoID: issue.RepoID, + Status: NotificationStatusUnread, + IssueID: issue.ID, + UpdatedBy: updatedByID, + } + + if issue.IsPull { + notification.Source = NotificationSourcePullRequest + } else { + notification.Source = NotificationSourceIssue + } + + _, err := e.Insert(notification) + return err +} + +func updateIssueNotification(e Engine, userID, issueID, updatedByID int64) error { + notification, err := getIssueNotification(e, userID, issueID) + if err != nil { + return err + } + + notification.Status = NotificationStatusUnread + notification.UpdatedBy = updatedByID + + _, err = e.Id(notification.ID).Update(notification) + return err +} + +func getIssueNotification(e Engine, userID, issueID int64) (*Notification, error) { + notification := new(Notification) + _, err := e. + Where("user_id = ?", userID). + And("issue_id = ?", issueID). + Get(notification) + return notification, err +} + +// NotificationsForUser returns notifications for a given user and status +func NotificationsForUser(user *User, status NotificationStatus) ([]*Notification, error) { + return notificationsForUser(x, user, status) +} +func notificationsForUser(e Engine, user *User, status NotificationStatus) (notifications []*Notification, err error) { + err = e. + Where("user_id = ?", user.ID). + And("status = ?", status). + OrderBy("updated_unix DESC"). + Find(¬ifications) + return +} + +// GetRepo returns the repo of the notification +func (n *Notification) GetRepo() (*Repository, error) { + n.Repository = new(Repository) + _, err := x. + Where("id = ?", n.RepoID). + Get(n.Repository) + return n.Repository, err +} + +// GetIssue returns the issue of the notification +func (n *Notification) GetIssue() (*Issue, error) { + n.Issue = new(Issue) + _, err := x. + Where("id = ?", n.IssueID). + Get(n.Issue) + return n.Issue, err +} + +// GetNotificationReadCount returns the notification read count for user +func GetNotificationReadCount(user *User) (int64, error) { + return GetNotificationCount(user, NotificationStatusRead) +} + +// GetNotificationUnreadCount returns the notification unread count for user +func GetNotificationUnreadCount(user *User) (int64, error) { + return GetNotificationCount(user, NotificationStatusUnread) +} + +// GetNotificationCount returns the notification count for user +func GetNotificationCount(user *User, status NotificationStatus) (int64, error) { + return getNotificationCount(x, user, status) +} + +func getNotificationCount(e Engine, user *User, status NotificationStatus) (count int64, err error) { + count, err = e. + Where("user_id = ?", user.ID). + And("status = ?", status). + Count(&Notification{}) + return +} + +func setNotificationStatusRead(e Engine, userID, issueID int64) error { + notification, err := getIssueNotification(e, userID, issueID) + // ignore if not exists + if err != nil { + return nil + } + + notification.Status = NotificationStatusRead + + _, err = e.Id(notification.ID).Update(notification) + return err +} |