diff options
Diffstat (limited to 'models/notification.go')
-rw-r--r-- | models/notification.go | 218 |
1 files changed, 203 insertions, 15 deletions
diff --git a/models/notification.go b/models/notification.go index 5c03b49257..8e9bca0dc6 100644 --- a/models/notification.go +++ b/models/notification.go @@ -6,8 +6,14 @@ package models import ( "fmt" + "path" + "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" + + "xorm.io/builder" + "xorm.io/xorm" ) type ( @@ -47,17 +53,67 @@ type Notification struct { IssueID int64 `xorm:"INDEX NOT NULL"` CommitID string `xorm:"INDEX"` CommentID int64 - Comment *Comment `xorm:"-"` UpdatedBy int64 `xorm:"INDEX NOT NULL"` Issue *Issue `xorm:"-"` Repository *Repository `xorm:"-"` + Comment *Comment `xorm:"-"` + User *User `xorm:"-"` CreatedUnix timeutil.TimeStamp `xorm:"created INDEX NOT NULL"` UpdatedUnix timeutil.TimeStamp `xorm:"updated INDEX NOT NULL"` } +// FindNotificationOptions represent the filters for notifications. If an ID is 0 it will be ignored. +type FindNotificationOptions struct { + UserID int64 + RepoID int64 + IssueID int64 + Status NotificationStatus + UpdatedAfterUnix int64 + UpdatedBeforeUnix int64 +} + +// ToCond will convert each condition into a xorm-Cond +func (opts *FindNotificationOptions) ToCond() builder.Cond { + cond := builder.NewCond() + if opts.UserID != 0 { + cond = cond.And(builder.Eq{"notification.user_id": opts.UserID}) + } + if opts.RepoID != 0 { + cond = cond.And(builder.Eq{"notification.repo_id": opts.RepoID}) + } + if opts.IssueID != 0 { + cond = cond.And(builder.Eq{"notification.issue_id": opts.IssueID}) + } + if opts.Status != 0 { + cond = cond.And(builder.Eq{"notification.status": opts.Status}) + } + if opts.UpdatedAfterUnix != 0 { + cond = cond.And(builder.Gte{"notification.updated_unix": opts.UpdatedAfterUnix}) + } + if opts.UpdatedBeforeUnix != 0 { + cond = cond.And(builder.Lte{"notification.updated_unix": opts.UpdatedBeforeUnix}) + } + return cond +} + +// ToSession will convert the given options to a xorm Session by using the conditions from ToCond and joining with issue table if required +func (opts *FindNotificationOptions) ToSession(e Engine) *xorm.Session { + return e.Where(opts.ToCond()) +} + +func getNotifications(e Engine, options FindNotificationOptions) (nl NotificationList, err error) { + err = options.ToSession(e).OrderBy("notification.updated_unix DESC").Find(&nl) + return +} + +// GetNotifications returns all notifications that fit to the given options. +func GetNotifications(opts FindNotificationOptions) (NotificationList, error) { + return getNotifications(x, opts) +} + // CreateOrUpdateIssueNotifications creates an issue notification // for each watcher, or updates it if already exists func CreateOrUpdateIssueNotifications(issueID, commentID int64, notificationAuthorID int64) error { @@ -238,22 +294,124 @@ func notificationsForUser(e Engine, user *User, statuses []NotificationStatus, p return } +// APIFormat converts a Notification to api.NotificationThread +func (n *Notification) APIFormat() *api.NotificationThread { + result := &api.NotificationThread{ + ID: n.ID, + Unread: !(n.Status == NotificationStatusRead || n.Status == NotificationStatusPinned), + Pinned: n.Status == NotificationStatusPinned, + UpdatedAt: n.UpdatedUnix.AsTime(), + URL: n.APIURL(), + } + + //since user only get notifications when he has access to use minimal access mode + if n.Repository != nil { + result.Repository = n.Repository.APIFormat(AccessModeRead) + } + + //handle Subject + switch n.Source { + case NotificationSourceIssue: + result.Subject = &api.NotificationSubject{Type: "Issue"} + if n.Issue != nil { + result.Subject.Title = n.Issue.Title + result.Subject.URL = n.Issue.APIURL() + comment, err := n.Issue.GetLastComment() + if err == nil && comment != nil { + result.Subject.LatestCommentURL = comment.APIURL() + } + } + case NotificationSourcePullRequest: + result.Subject = &api.NotificationSubject{Type: "Pull"} + if n.Issue != nil { + result.Subject.Title = n.Issue.Title + result.Subject.URL = n.Issue.APIURL() + comment, err := n.Issue.GetLastComment() + if err == nil && comment != nil { + result.Subject.LatestCommentURL = comment.APIURL() + } + } + case NotificationSourceCommit: + result.Subject = &api.NotificationSubject{ + Type: "Commit", + Title: n.CommitID, + } + //unused until now + } + + return result +} + +// LoadAttributes load Repo Issue User and Comment if not loaded +func (n *Notification) LoadAttributes() (err error) { + return n.loadAttributes(x) +} + +func (n *Notification) loadAttributes(e Engine) (err error) { + if err = n.loadRepo(e); err != nil { + return + } + if err = n.loadIssue(e); err != nil { + return + } + if err = n.loadUser(e); err != nil { + return + } + if err = n.loadComment(e); err != nil { + return + } + return +} + +func (n *Notification) loadRepo(e Engine) (err error) { + if n.Repository == nil { + n.Repository, err = getRepositoryByID(e, n.RepoID) + if err != nil { + return fmt.Errorf("getRepositoryByID [%d]: %v", n.RepoID, err) + } + } + return nil +} + +func (n *Notification) loadIssue(e Engine) (err error) { + if n.Issue == nil { + n.Issue, err = getIssueByID(e, n.IssueID) + if err != nil { + return fmt.Errorf("getIssueByID [%d]: %v", n.IssueID, err) + } + return n.Issue.loadAttributes(e) + } + return nil +} + +func (n *Notification) loadComment(e Engine) (err error) { + if n.Comment == nil && n.CommentID > 0 { + n.Comment, err = GetCommentByID(n.CommentID) + if err != nil { + return fmt.Errorf("GetCommentByID [%d]: %v", n.CommentID, err) + } + } + return nil +} + +func (n *Notification) loadUser(e Engine) (err error) { + if n.User == nil { + n.User, err = getUserByID(e, n.UserID) + if err != nil { + return fmt.Errorf("getUserByID [%d]: %v", n.UserID, err) + } + } + return nil +} + // 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 + return n.Repository, n.loadRepo(x) } // 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 + return n.Issue, n.loadIssue(x) } // HTMLURL formats a URL-string to the notification @@ -264,9 +422,34 @@ func (n *Notification) HTMLURL() string { return n.Issue.HTMLURL() } +// APIURL formats a URL-string to the notification +func (n *Notification) APIURL() string { + return setting.AppURL + path.Join("api/v1/notifications/threads", fmt.Sprintf("%d", n.ID)) +} + // NotificationList contains a list of notifications type NotificationList []*Notification +// APIFormat converts a NotificationList to api.NotificationThread list +func (nl NotificationList) APIFormat() []*api.NotificationThread { + var result = make([]*api.NotificationThread, 0, len(nl)) + for _, n := range nl { + result = append(result, n.APIFormat()) + } + return result +} + +// LoadAttributes load Repo Issue User and Comment if not loaded +func (nl NotificationList) LoadAttributes() (err error) { + for i := 0; i < len(nl); i++ { + err = nl[i].LoadAttributes() + if err != nil { + return + } + } + return +} + func (nl NotificationList) getPendingRepoIDs() []int64 { var ids = make(map[int64]struct{}, len(nl)) for _, notification := range nl { @@ -486,7 +669,7 @@ func setNotificationStatusReadIfUnread(e Engine, userID, issueID int64) error { // SetNotificationStatus change the notification status func SetNotificationStatus(notificationID int64, user *User, status NotificationStatus) error { - notification, err := getNotificationByID(notificationID) + notification, err := getNotificationByID(x, notificationID) if err != nil { return err } @@ -501,9 +684,14 @@ func SetNotificationStatus(notificationID int64, user *User, status Notification return err } -func getNotificationByID(notificationID int64) (*Notification, error) { +// GetNotificationByID return notification by ID +func GetNotificationByID(notificationID int64) (*Notification, error) { + return getNotificationByID(x, notificationID) +} + +func getNotificationByID(e Engine, notificationID int64) (*Notification, error) { notification := new(Notification) - ok, err := x. + ok, err := e. Where("id = ?", notificationID). Get(notification) @@ -512,7 +700,7 @@ func getNotificationByID(notificationID int64) (*Notification, error) { } if !ok { - return nil, fmt.Errorf("Notification %d does not exists", notificationID) + return nil, ErrNotExist{ID: notificationID} } return notification, nil |