mirror of
https://github.com/go-gitea/gitea.git
synced 2025-08-16 11:56:29 +00:00
Add release notification and fix repository transfer/commit notification
This commit is contained in:
parent
40dec17b5c
commit
6b055ddb9f
@ -11,7 +11,6 @@ import (
|
|||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
issues_model "code.gitea.io/gitea/models/issues"
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
"code.gitea.io/gitea/models/organization"
|
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
@ -46,6 +45,8 @@ const (
|
|||||||
NotificationSourceCommit
|
NotificationSourceCommit
|
||||||
// NotificationSourceRepository is a notification for a repository
|
// NotificationSourceRepository is a notification for a repository
|
||||||
NotificationSourceRepository
|
NotificationSourceRepository
|
||||||
|
// NotificationSourceRelease is a notification for a release
|
||||||
|
NotificationSourceRelease
|
||||||
)
|
)
|
||||||
|
|
||||||
// Notification represents a notification
|
// Notification represents a notification
|
||||||
@ -60,6 +61,7 @@ type Notification struct {
|
|||||||
IssueID int64 `xorm:"NOT NULL"`
|
IssueID int64 `xorm:"NOT NULL"`
|
||||||
CommitID string
|
CommitID string
|
||||||
CommentID int64
|
CommentID int64
|
||||||
|
ReleaseID int64
|
||||||
|
|
||||||
UpdatedBy int64 `xorm:"NOT NULL"`
|
UpdatedBy int64 `xorm:"NOT NULL"`
|
||||||
|
|
||||||
@ -67,6 +69,7 @@ type Notification struct {
|
|||||||
Repository *repo_model.Repository `xorm:"-"`
|
Repository *repo_model.Repository `xorm:"-"`
|
||||||
Comment *issues_model.Comment `xorm:"-"`
|
Comment *issues_model.Comment `xorm:"-"`
|
||||||
User *user_model.User `xorm:"-"`
|
User *user_model.User `xorm:"-"`
|
||||||
|
Release *repo_model.Release `xorm:"-"`
|
||||||
|
|
||||||
CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"`
|
CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"`
|
||||||
UpdatedUnix timeutil.TimeStamp `xorm:"updated NOT NULL"`
|
UpdatedUnix timeutil.TimeStamp `xorm:"updated NOT NULL"`
|
||||||
@ -104,6 +107,10 @@ func (n *Notification) TableIndices() []*schemas.Index {
|
|||||||
commitIDIndex.AddColumn("commit_id")
|
commitIDIndex.AddColumn("commit_id")
|
||||||
indices = append(indices, commitIDIndex)
|
indices = append(indices, commitIDIndex)
|
||||||
|
|
||||||
|
releaseIDIndex := schemas.NewIndex("idx_notification_release_id", schemas.IndexType)
|
||||||
|
releaseIDIndex.AddColumn("release_id")
|
||||||
|
indices = append(indices, releaseIDIndex)
|
||||||
|
|
||||||
updatedByIndex := schemas.NewIndex("idx_notification_updated_by", schemas.IndexType)
|
updatedByIndex := schemas.NewIndex("idx_notification_updated_by", schemas.IndexType)
|
||||||
updatedByIndex.AddColumn("updated_by")
|
updatedByIndex.AddColumn("updated_by")
|
||||||
indices = append(indices, updatedByIndex)
|
indices = append(indices, updatedByIndex)
|
||||||
@ -116,36 +123,53 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateRepoTransferNotification creates notification for the user a repository was transferred to
|
// CreateRepoTransferNotification creates notification for the user a repository was transferred to
|
||||||
func CreateRepoTransferNotification(ctx context.Context, doer, newOwner *user_model.User, repo *repo_model.Repository) error {
|
func CreateRepoTransferNotification(ctx context.Context, doerID, repoID, receiverID int64) error {
|
||||||
return db.WithTx(ctx, func(ctx context.Context) error {
|
notify := &Notification{
|
||||||
var notify []*Notification
|
UserID: receiverID,
|
||||||
|
RepoID: repoID,
|
||||||
|
Status: NotificationStatusUnread,
|
||||||
|
UpdatedBy: doerID,
|
||||||
|
Source: NotificationSourceRepository,
|
||||||
|
}
|
||||||
|
return db.Insert(ctx, notify)
|
||||||
|
}
|
||||||
|
|
||||||
if newOwner.IsOrganization() {
|
func CreateCommitNotifications(ctx context.Context, doerID, repoID int64, commitID string, receiverID int64) error {
|
||||||
users, err := organization.GetUsersWhoCanCreateOrgRepo(ctx, newOwner.ID)
|
notification := &Notification{
|
||||||
if err != nil || len(users) == 0 {
|
Source: NotificationSourceCommit,
|
||||||
return err
|
UserID: receiverID,
|
||||||
}
|
RepoID: repoID,
|
||||||
for i := range users {
|
CommitID: commitID,
|
||||||
notify = append(notify, &Notification{
|
Status: NotificationStatusUnread,
|
||||||
UserID: i,
|
UpdatedBy: doerID,
|
||||||
RepoID: repo.ID,
|
}
|
||||||
Status: NotificationStatusUnread,
|
|
||||||
UpdatedBy: doer.ID,
|
|
||||||
Source: NotificationSourceRepository,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
notify = []*Notification{{
|
|
||||||
UserID: newOwner.ID,
|
|
||||||
RepoID: repo.ID,
|
|
||||||
Status: NotificationStatusUnread,
|
|
||||||
UpdatedBy: doer.ID,
|
|
||||||
Source: NotificationSourceRepository,
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
return db.Insert(ctx, notify)
|
return db.Insert(ctx, notification)
|
||||||
})
|
}
|
||||||
|
|
||||||
|
func CreateOrUpdateReleaseNotifications(ctx context.Context, doerID, releaseID, receiverID int64) error {
|
||||||
|
notification := new(Notification)
|
||||||
|
if _, err := db.GetEngine(ctx).
|
||||||
|
Where("user_id = ?", receiverID).
|
||||||
|
And("release_id = ?", releaseID).
|
||||||
|
Get(notification); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if notification.ID > 0 {
|
||||||
|
notification.Status = NotificationStatusUnread
|
||||||
|
notification.UpdatedBy = doerID
|
||||||
|
_, err := db.GetEngine(ctx).ID(notification.ID).Cols("status", "updated_by").Update(notification)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
notification = &Notification{
|
||||||
|
Source: NotificationSourceRelease,
|
||||||
|
UserID: receiverID,
|
||||||
|
Status: NotificationStatusUnread,
|
||||||
|
ReleaseID: releaseID,
|
||||||
|
UpdatedBy: doerID,
|
||||||
|
}
|
||||||
|
return db.Insert(ctx, notification)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createIssueNotification(ctx context.Context, userID int64, issue *issues_model.Issue, commentID, updatedByID int64) error {
|
func createIssueNotification(ctx context.Context, userID int64, issue *issues_model.Issue, commentID, updatedByID int64) error {
|
||||||
@ -213,6 +237,9 @@ func (n *Notification) LoadAttributes(ctx context.Context) (err error) {
|
|||||||
if err = n.loadComment(ctx); err != nil {
|
if err = n.loadComment(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err = n.loadRelease(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,6 +280,16 @@ func (n *Notification) loadComment(ctx context.Context) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *Notification) loadRelease(ctx context.Context) (err error) {
|
||||||
|
if n.Release == nil && n.ReleaseID != 0 {
|
||||||
|
n.Release, err = repo_model.GetReleaseByID(ctx, n.ReleaseID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("GetReleaseByID [%d]: %w", n.ReleaseID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (n *Notification) loadUser(ctx context.Context) (err error) {
|
func (n *Notification) loadUser(ctx context.Context) (err error) {
|
||||||
if n.User == nil {
|
if n.User == nil {
|
||||||
n.User, err = user_model.GetUserByID(ctx, n.UserID)
|
n.User, err = user_model.GetUserByID(ctx, n.UserID)
|
||||||
@ -285,6 +322,8 @@ func (n *Notification) HTMLURL(ctx context.Context) string {
|
|||||||
return n.Repository.HTMLURL() + "/commit/" + url.PathEscape(n.CommitID)
|
return n.Repository.HTMLURL() + "/commit/" + url.PathEscape(n.CommitID)
|
||||||
case NotificationSourceRepository:
|
case NotificationSourceRepository:
|
||||||
return n.Repository.HTMLURL()
|
return n.Repository.HTMLURL()
|
||||||
|
case NotificationSourceRelease:
|
||||||
|
return n.Release.HTMLURL()
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@ -301,6 +340,8 @@ func (n *Notification) Link(ctx context.Context) string {
|
|||||||
return n.Repository.Link() + "/commit/" + url.PathEscape(n.CommitID)
|
return n.Repository.Link() + "/commit/" + url.PathEscape(n.CommitID)
|
||||||
case NotificationSourceRepository:
|
case NotificationSourceRepository:
|
||||||
return n.Repository.Link()
|
return n.Repository.Link()
|
||||||
|
case NotificationSourceRelease:
|
||||||
|
return n.Release.Link()
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@ -373,6 +414,17 @@ func SetRepoReadBy(ctx context.Context, userID, repoID int64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetReleaseReadBy sets issue to be read by given user.
|
||||||
|
func SetReleaseReadBy(ctx context.Context, releaseID, userID int64) error {
|
||||||
|
_, err := db.GetEngine(ctx).Where(builder.Eq{
|
||||||
|
"user_id": userID,
|
||||||
|
"status": NotificationStatusUnread,
|
||||||
|
"source": NotificationSourceRelease,
|
||||||
|
"release_id": releaseID,
|
||||||
|
}).Cols("status").Update(&Notification{Status: NotificationStatusRead})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// SetNotificationStatus change the notification status
|
// SetNotificationStatus change the notification status
|
||||||
func SetNotificationStatus(ctx context.Context, notificationID int64, user *user_model.User, status NotificationStatus) (*Notification, error) {
|
func SetNotificationStatus(ctx context.Context, notificationID int64, user *user_model.User, status NotificationStatus) (*Notification, error) {
|
||||||
notification, err := GetNotificationByID(ctx, notificationID)
|
notification, err := GetNotificationByID(ctx, notificationID)
|
||||||
|
@ -25,6 +25,7 @@ type FindNotificationOptions struct {
|
|||||||
UserID int64
|
UserID int64
|
||||||
RepoID int64
|
RepoID int64
|
||||||
IssueID int64
|
IssueID int64
|
||||||
|
ReleaseID int64
|
||||||
Status []NotificationStatus
|
Status []NotificationStatus
|
||||||
Source []NotificationSource
|
Source []NotificationSource
|
||||||
UpdatedAfterUnix int64
|
UpdatedAfterUnix int64
|
||||||
@ -43,6 +44,9 @@ func (opts FindNotificationOptions) ToConds() builder.Cond {
|
|||||||
if opts.IssueID != 0 {
|
if opts.IssueID != 0 {
|
||||||
cond = cond.And(builder.Eq{"notification.issue_id": opts.IssueID})
|
cond = cond.And(builder.Eq{"notification.issue_id": opts.IssueID})
|
||||||
}
|
}
|
||||||
|
if opts.ReleaseID != 0 {
|
||||||
|
cond = cond.And(builder.Eq{"notification.release_id": opts.ReleaseID})
|
||||||
|
}
|
||||||
if len(opts.Status) > 0 {
|
if len(opts.Status) > 0 {
|
||||||
if len(opts.Status) == 1 {
|
if len(opts.Status) == 1 {
|
||||||
cond = cond.And(builder.Eq{"notification.status": opts.Status[0]})
|
cond = cond.And(builder.Eq{"notification.status": opts.Status[0]})
|
||||||
@ -70,17 +74,9 @@ func (opts FindNotificationOptions) ToOrders() string {
|
|||||||
// for each watcher, or updates it if already exists
|
// for each watcher, or updates it if already exists
|
||||||
// receiverID > 0 just send to receiver, else send to all watcher
|
// receiverID > 0 just send to receiver, else send to all watcher
|
||||||
func CreateOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, notificationAuthorID, receiverID int64) error {
|
func CreateOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, notificationAuthorID, receiverID int64) error {
|
||||||
ctx, committer, err := db.TxContext(ctx)
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
if err != nil {
|
return createOrUpdateIssueNotifications(ctx, issueID, commentID, notificationAuthorID, receiverID)
|
||||||
return err
|
})
|
||||||
}
|
|
||||||
defer committer.Close()
|
|
||||||
|
|
||||||
if err := createOrUpdateIssueNotifications(ctx, issueID, commentID, notificationAuthorID, receiverID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return committer.Commit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, notificationAuthorID, receiverID int64) error {
|
func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, notificationAuthorID, receiverID int64) error {
|
||||||
@ -186,6 +182,9 @@ func (nl NotificationList) LoadAttributes(ctx context.Context) error {
|
|||||||
if _, err := nl.LoadComments(ctx); err != nil {
|
if _, err := nl.LoadComments(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if _, err := nl.LoadReleases(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -458,6 +457,31 @@ func (nl NotificationList) LoadComments(ctx context.Context) ([]int, error) {
|
|||||||
return failures, nil
|
return failures, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (nl NotificationList) LoadReleases(ctx context.Context) ([]int, error) {
|
||||||
|
if len(nl) == 0 {
|
||||||
|
return []int{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
releaseIDs := nl.getPendingCommentIDs()
|
||||||
|
releases := make(map[int64]*repo_model.Release, len(releaseIDs))
|
||||||
|
if err := db.GetEngine(ctx).In("id", releaseIDs).Find(&releases); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
failures := []int{}
|
||||||
|
for i, notification := range nl {
|
||||||
|
if notification.ReleaseID > 0 && notification.Release == nil && releases[notification.ReleaseID] != nil {
|
||||||
|
notification.Release = releases[notification.ReleaseID]
|
||||||
|
if notification.Release == nil {
|
||||||
|
log.Error("Notification[%d]: ReleaseID[%d] failed to load", notification.ID, notification.ReleaseID)
|
||||||
|
failures = append(failures, i)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return failures, nil
|
||||||
|
}
|
||||||
|
|
||||||
// LoadIssuePullRequests loads all issues' pull requests if possible
|
// LoadIssuePullRequests loads all issues' pull requests if possible
|
||||||
func (nl NotificationList) LoadIssuePullRequests(ctx context.Context) error {
|
func (nl NotificationList) LoadIssuePullRequests(ctx context.Context) error {
|
||||||
issues := make(map[int64]*issues_model.Issue, len(nl))
|
issues := make(map[int64]*issues_model.Issue, len(nl))
|
||||||
|
81
models/migrations/v1_25/v321.go
Normal file
81
models/migrations/v1_25/v321.go
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package v1_25 //nolint
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
|
"xorm.io/xorm"
|
||||||
|
"xorm.io/xorm/schemas"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NotificationV321 represents a notification
|
||||||
|
type NotificationV321 struct {
|
||||||
|
ID int64 `xorm:"pk autoincr"`
|
||||||
|
UserID int64 `xorm:"NOT NULL"`
|
||||||
|
RepoID int64 `xorm:"NOT NULL"`
|
||||||
|
|
||||||
|
Status uint8 `xorm:"SMALLINT NOT NULL"`
|
||||||
|
Source uint8 `xorm:"SMALLINT NOT NULL"`
|
||||||
|
|
||||||
|
IssueID int64 `xorm:"NOT NULL"`
|
||||||
|
CommitID string
|
||||||
|
CommentID int64
|
||||||
|
ReleaseID int64
|
||||||
|
|
||||||
|
UpdatedBy int64 `xorm:"NOT NULL"`
|
||||||
|
|
||||||
|
CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"`
|
||||||
|
UpdatedUnix timeutil.TimeStamp `xorm:"updated NOT NULL"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *NotificationV321) TableName() string {
|
||||||
|
return "notification"
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableIndices implements xorm's TableIndices interface
|
||||||
|
func (n *NotificationV321) TableIndices() []*schemas.Index {
|
||||||
|
indices := make([]*schemas.Index, 0, 8)
|
||||||
|
usuuIndex := schemas.NewIndex("u_s_uu", schemas.IndexType)
|
||||||
|
usuuIndex.AddColumn("user_id", "status", "updated_unix")
|
||||||
|
indices = append(indices, usuuIndex)
|
||||||
|
|
||||||
|
// Add the individual indices that were previously defined in struct tags
|
||||||
|
userIDIndex := schemas.NewIndex("idx_notification_user_id", schemas.IndexType)
|
||||||
|
userIDIndex.AddColumn("user_id")
|
||||||
|
indices = append(indices, userIDIndex)
|
||||||
|
|
||||||
|
repoIDIndex := schemas.NewIndex("idx_notification_repo_id", schemas.IndexType)
|
||||||
|
repoIDIndex.AddColumn("repo_id")
|
||||||
|
indices = append(indices, repoIDIndex)
|
||||||
|
|
||||||
|
statusIndex := schemas.NewIndex("idx_notification_status", schemas.IndexType)
|
||||||
|
statusIndex.AddColumn("status")
|
||||||
|
indices = append(indices, statusIndex)
|
||||||
|
|
||||||
|
sourceIndex := schemas.NewIndex("idx_notification_source", schemas.IndexType)
|
||||||
|
sourceIndex.AddColumn("source")
|
||||||
|
indices = append(indices, sourceIndex)
|
||||||
|
|
||||||
|
issueIDIndex := schemas.NewIndex("idx_notification_issue_id", schemas.IndexType)
|
||||||
|
issueIDIndex.AddColumn("issue_id")
|
||||||
|
indices = append(indices, issueIDIndex)
|
||||||
|
|
||||||
|
commitIDIndex := schemas.NewIndex("idx_notification_commit_id", schemas.IndexType)
|
||||||
|
commitIDIndex.AddColumn("commit_id")
|
||||||
|
indices = append(indices, commitIDIndex)
|
||||||
|
|
||||||
|
releaseIDIndex := schemas.NewIndex("idx_notification_release_id", schemas.IndexType)
|
||||||
|
releaseIDIndex.AddColumn("release_id")
|
||||||
|
indices = append(indices, releaseIDIndex)
|
||||||
|
|
||||||
|
updatedByIndex := schemas.NewIndex("idx_notification_updated_by", schemas.IndexType)
|
||||||
|
updatedByIndex.AddColumn("updated_by")
|
||||||
|
indices = append(indices, updatedByIndex)
|
||||||
|
|
||||||
|
return indices
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddReleaseNotification(x *xorm.Engine) error {
|
||||||
|
return x.Sync(new(NotificationV321))
|
||||||
|
}
|
@ -93,6 +93,22 @@ func init() {
|
|||||||
db.RegisterModel(new(Release))
|
db.RegisterModel(new(Release))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Release) LoadPublisher(ctx context.Context) error {
|
||||||
|
if r.Publisher != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
r.Publisher, err = user_model.GetUserByID(ctx, r.PublisherID)
|
||||||
|
if err != nil {
|
||||||
|
if user_model.IsErrUserNotExist(err) {
|
||||||
|
r.Publisher = user_model.NewGhostUser()
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// LoadAttributes load repo and publisher attributes for a release
|
// LoadAttributes load repo and publisher attributes for a release
|
||||||
func (r *Release) LoadAttributes(ctx context.Context) error {
|
func (r *Release) LoadAttributes(ctx context.Context) error {
|
||||||
var err error
|
var err error
|
||||||
@ -102,15 +118,8 @@ func (r *Release) LoadAttributes(ctx context.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if r.Publisher == nil {
|
if err := r.LoadPublisher(ctx); err != nil {
|
||||||
r.Publisher, err = user_model.GetUserByID(ctx, r.PublisherID)
|
return err
|
||||||
if err != nil {
|
|
||||||
if user_model.IsErrUserNotExist(err) {
|
|
||||||
r.Publisher = user_model.NewGhostUser()
|
|
||||||
} else {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return GetReleaseAttachments(ctx, r)
|
return GetReleaseAttachments(ctx, r)
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ package user
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/auth"
|
"code.gitea.io/gitea/models/auth"
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
@ -81,3 +82,20 @@ func GetUsersByIDs(ctx context.Context, ids []int64) (UserList, error) {
|
|||||||
Find(&ous)
|
Find(&ous)
|
||||||
return ous, err
|
return ous, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUsersByUsernames returns all resolved users from a list of user names.
|
||||||
|
func GetUsersByUsernames(ctx context.Context, userNames []string) (UserList, error) {
|
||||||
|
ous := make([]*User, 0, len(userNames))
|
||||||
|
if len(userNames) == 0 {
|
||||||
|
return ous, nil
|
||||||
|
}
|
||||||
|
for i, name := range userNames {
|
||||||
|
userNames[i] = strings.ToLower(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := db.GetEngine(ctx).
|
||||||
|
Where("`type` = ?", UserTypeIndividual).
|
||||||
|
In("lower_name", userNames).
|
||||||
|
Find(&ous)
|
||||||
|
return ous, err
|
||||||
|
}
|
||||||
|
@ -46,4 +46,6 @@ const (
|
|||||||
NotifySubjectCommit NotifySubjectType = "Commit"
|
NotifySubjectCommit NotifySubjectType = "Commit"
|
||||||
// NotifySubjectRepository an repository is subject of an notification
|
// NotifySubjectRepository an repository is subject of an notification
|
||||||
NotifySubjectRepository NotifySubjectType = "Repository"
|
NotifySubjectRepository NotifySubjectType = "Repository"
|
||||||
|
// NotifySubjectRelease an release is subject of an notification
|
||||||
|
NotifySubjectRelease NotifySubjectType = "Release"
|
||||||
)
|
)
|
||||||
|
@ -71,6 +71,8 @@ func subjectToSource(value []string) (result []activities_model.NotificationSour
|
|||||||
result = append(result, activities_model.NotificationSourceCommit)
|
result = append(result, activities_model.NotificationSourceCommit)
|
||||||
case "repository":
|
case "repository":
|
||||||
result = append(result, activities_model.NotificationSourceRepository)
|
result = append(result, activities_model.NotificationSourceRepository)
|
||||||
|
case "release":
|
||||||
|
result = append(result, activities_model.NotificationSourceRelease)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
|
@ -80,7 +80,7 @@ func ListRepoNotifications(ctx *context.APIContext) {
|
|||||||
// collectionFormat: multi
|
// collectionFormat: multi
|
||||||
// items:
|
// items:
|
||||||
// type: string
|
// type: string
|
||||||
// enum: [issue,pull,commit,repository]
|
// enum: [issue,pull,commit,repository,release]
|
||||||
// - name: since
|
// - name: since
|
||||||
// in: query
|
// in: query
|
||||||
// description: Only show notifications updated after the given time. This is a timestamp in RFC 3339 format
|
// description: Only show notifications updated after the given time. This is a timestamp in RFC 3339 format
|
||||||
|
@ -42,7 +42,7 @@ func ListNotifications(ctx *context.APIContext) {
|
|||||||
// collectionFormat: multi
|
// collectionFormat: multi
|
||||||
// items:
|
// items:
|
||||||
// type: string
|
// type: string
|
||||||
// enum: [issue,pull,commit,repository]
|
// enum: [issue,pull,commit,repository,release]
|
||||||
// - name: since
|
// - name: since
|
||||||
// in: query
|
// in: query
|
||||||
// description: Only show notifications updated after the given time. This is a timestamp in RFC 3339 format
|
// description: Only show notifications updated after the given time. This is a timestamp in RFC 3339 format
|
||||||
|
@ -31,6 +31,7 @@ import (
|
|||||||
"code.gitea.io/gitea/services/context/upload"
|
"code.gitea.io/gitea/services/context/upload"
|
||||||
"code.gitea.io/gitea/services/forms"
|
"code.gitea.io/gitea/services/forms"
|
||||||
release_service "code.gitea.io/gitea/services/release"
|
release_service "code.gitea.io/gitea/services/release"
|
||||||
|
activities_model "code.gitea.io/gitea/models/activities"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -298,6 +299,14 @@ func SingleRelease(ctx *context.Context) {
|
|||||||
release.Title = release.TagName
|
release.Title = release.TagName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ctx.IsSigned && !release.IsTag {
|
||||||
|
err = activities_model.SetReleaseReadBy(ctx, release.ID, ctx.Doer.ID)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("SetReleaseReadBy", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ctx.Data["PageIsSingleTag"] = release.IsTag
|
ctx.Data["PageIsSingleTag"] = release.IsTag
|
||||||
ctx.Data["SingleReleaseTagName"] = release.TagName
|
ctx.Data["SingleReleaseTagName"] = release.TagName
|
||||||
if release.IsTag {
|
if release.IsTag {
|
||||||
|
@ -83,6 +83,13 @@ func ToNotificationThread(ctx context.Context, n *activities_model.Notification)
|
|||||||
URL: n.Repository.Link(),
|
URL: n.Repository.Link(),
|
||||||
HTMLURL: n.Repository.HTMLURL(),
|
HTMLURL: n.Repository.HTMLURL(),
|
||||||
}
|
}
|
||||||
|
case activities_model.NotificationSourceRelease:
|
||||||
|
result.Subject = &api.NotificationSubject{
|
||||||
|
Type: api.NotifySubjectRelease,
|
||||||
|
Title: n.Release.Title,
|
||||||
|
URL: n.Release.Link(),
|
||||||
|
HTMLURL: n.Release.HTMLURL(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
@ -9,24 +9,34 @@ import (
|
|||||||
activities_model "code.gitea.io/gitea/models/activities"
|
activities_model "code.gitea.io/gitea/models/activities"
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
issues_model "code.gitea.io/gitea/models/issues"
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
|
"code.gitea.io/gitea/models/organization"
|
||||||
|
access_model "code.gitea.io/gitea/models/perm/access"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
|
"code.gitea.io/gitea/models/unit"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/container"
|
"code.gitea.io/gitea/modules/container"
|
||||||
"code.gitea.io/gitea/modules/graceful"
|
"code.gitea.io/gitea/modules/graceful"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/queue"
|
"code.gitea.io/gitea/modules/queue"
|
||||||
|
"code.gitea.io/gitea/modules/references"
|
||||||
|
"code.gitea.io/gitea/modules/repository"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
notify_service "code.gitea.io/gitea/services/notify"
|
notify_service "code.gitea.io/gitea/services/notify"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
notificationService struct {
|
notificationService struct {
|
||||||
notify_service.NullNotifier
|
notify_service.NullNotifier
|
||||||
issueQueue *queue.WorkerPoolQueue[issueNotificationOpts]
|
queue *queue.WorkerPoolQueue[notificationOpts]
|
||||||
}
|
}
|
||||||
|
|
||||||
issueNotificationOpts struct {
|
notificationOpts struct {
|
||||||
|
Source activities_model.NotificationSource
|
||||||
IssueID int64
|
IssueID int64
|
||||||
CommentID int64
|
CommentID int64
|
||||||
|
CommitID string // commit ID for commit notifications
|
||||||
|
RepoID int64
|
||||||
|
ReleaseID int64
|
||||||
NotificationAuthorID int64
|
NotificationAuthorID int64
|
||||||
ReceiverID int64 // 0 -- ALL Watcher
|
ReceiverID int64 // 0 -- ALL Watcher
|
||||||
}
|
}
|
||||||
@ -43,39 +53,57 @@ var _ notify_service.Notifier = ¬ificationService{}
|
|||||||
// NewNotifier create a new notificationService notifier
|
// NewNotifier create a new notificationService notifier
|
||||||
func NewNotifier() notify_service.Notifier {
|
func NewNotifier() notify_service.Notifier {
|
||||||
ns := ¬ificationService{}
|
ns := ¬ificationService{}
|
||||||
ns.issueQueue = queue.CreateSimpleQueue(graceful.GetManager().ShutdownContext(), "notification-service", handler)
|
ns.queue = queue.CreateSimpleQueue(graceful.GetManager().ShutdownContext(), "notification-service", handler)
|
||||||
if ns.issueQueue == nil {
|
if ns.queue == nil {
|
||||||
log.Fatal("Unable to create notification-service queue")
|
log.Fatal("Unable to create notification-service queue")
|
||||||
}
|
}
|
||||||
return ns
|
return ns
|
||||||
}
|
}
|
||||||
|
|
||||||
func handler(items ...issueNotificationOpts) []issueNotificationOpts {
|
func handler(items ...notificationOpts) []notificationOpts {
|
||||||
for _, opts := range items {
|
for _, opts := range items {
|
||||||
if err := activities_model.CreateOrUpdateIssueNotifications(db.DefaultContext, opts.IssueID, opts.CommentID, opts.NotificationAuthorID, opts.ReceiverID); err != nil {
|
switch opts.Source {
|
||||||
log.Error("Was unable to create issue notification: %v", err)
|
case activities_model.NotificationSourceRepository:
|
||||||
|
if err := activities_model.CreateRepoTransferNotification(db.DefaultContext, opts.NotificationAuthorID, opts.RepoID, opts.ReceiverID); err != nil {
|
||||||
|
log.Error("CreateRepoTransferNotification: %v", err)
|
||||||
|
}
|
||||||
|
case activities_model.NotificationSourceCommit:
|
||||||
|
if err := activities_model.CreateCommitNotifications(db.DefaultContext, opts.RepoID, opts.NotificationAuthorID, opts.CommitID, opts.ReceiverID); err != nil {
|
||||||
|
log.Error("Was unable to create commit notification: %v", err)
|
||||||
|
}
|
||||||
|
case activities_model.NotificationSourceRelease:
|
||||||
|
if err := activities_model.CreateOrUpdateReleaseNotifications(db.DefaultContext, opts.NotificationAuthorID, opts.ReleaseID, opts.ReceiverID); err != nil {
|
||||||
|
log.Error("Was unable to create release notification: %v", err)
|
||||||
|
}
|
||||||
|
case activities_model.NotificationSourceIssue, activities_model.NotificationSourcePullRequest:
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
if err := activities_model.CreateOrUpdateIssueNotifications(db.DefaultContext, opts.IssueID, opts.CommentID, opts.NotificationAuthorID, opts.ReceiverID); err != nil {
|
||||||
|
log.Error("Was unable to create issue notification: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *notificationService) Run() {
|
func (ns *notificationService) Run() {
|
||||||
go graceful.GetManager().RunWithCancel(ns.issueQueue) // TODO: using "go" here doesn't seem right, just leave it as old code
|
go graceful.GetManager().RunWithCancel(ns.queue) // TODO: using "go" here doesn't seem right, just leave it as old code
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *notificationService) CreateIssueComment(ctx context.Context, doer *user_model.User, repo *repo_model.Repository,
|
func (ns *notificationService) CreateIssueComment(ctx context.Context, doer *user_model.User, repo *repo_model.Repository,
|
||||||
issue *issues_model.Issue, comment *issues_model.Comment, mentions []*user_model.User,
|
issue *issues_model.Issue, comment *issues_model.Comment, mentions []*user_model.User,
|
||||||
) {
|
) {
|
||||||
opts := issueNotificationOpts{
|
opts := notificationOpts{
|
||||||
|
Source: util.Iif(issue.IsPull, activities_model.NotificationSourcePullRequest, activities_model.NotificationSourceIssue),
|
||||||
IssueID: issue.ID,
|
IssueID: issue.ID,
|
||||||
NotificationAuthorID: doer.ID,
|
NotificationAuthorID: doer.ID,
|
||||||
}
|
}
|
||||||
if comment != nil {
|
if comment != nil {
|
||||||
opts.CommentID = comment.ID
|
opts.CommentID = comment.ID
|
||||||
}
|
}
|
||||||
_ = ns.issueQueue.Push(opts)
|
_ = ns.queue.Push(opts)
|
||||||
for _, mention := range mentions {
|
for _, mention := range mentions {
|
||||||
opts := issueNotificationOpts{
|
opts := notificationOpts{
|
||||||
IssueID: issue.ID,
|
IssueID: issue.ID,
|
||||||
NotificationAuthorID: doer.ID,
|
NotificationAuthorID: doer.ID,
|
||||||
ReceiverID: mention.ID,
|
ReceiverID: mention.ID,
|
||||||
@ -83,17 +111,18 @@ func (ns *notificationService) CreateIssueComment(ctx context.Context, doer *use
|
|||||||
if comment != nil {
|
if comment != nil {
|
||||||
opts.CommentID = comment.ID
|
opts.CommentID = comment.ID
|
||||||
}
|
}
|
||||||
_ = ns.issueQueue.Push(opts)
|
_ = ns.queue.Push(opts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *notificationService) NewIssue(ctx context.Context, issue *issues_model.Issue, mentions []*user_model.User) {
|
func (ns *notificationService) NewIssue(ctx context.Context, issue *issues_model.Issue, mentions []*user_model.User) {
|
||||||
_ = ns.issueQueue.Push(issueNotificationOpts{
|
_ = ns.queue.Push(notificationOpts{
|
||||||
|
Source: activities_model.NotificationSourceIssue,
|
||||||
IssueID: issue.ID,
|
IssueID: issue.ID,
|
||||||
NotificationAuthorID: issue.Poster.ID,
|
NotificationAuthorID: issue.Poster.ID,
|
||||||
})
|
})
|
||||||
for _, mention := range mentions {
|
for _, mention := range mentions {
|
||||||
_ = ns.issueQueue.Push(issueNotificationOpts{
|
_ = ns.queue.Push(notificationOpts{
|
||||||
IssueID: issue.ID,
|
IssueID: issue.ID,
|
||||||
NotificationAuthorID: issue.Poster.ID,
|
NotificationAuthorID: issue.Poster.ID,
|
||||||
ReceiverID: mention.ID,
|
ReceiverID: mention.ID,
|
||||||
@ -102,7 +131,8 @@ func (ns *notificationService) NewIssue(ctx context.Context, issue *issues_model
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ns *notificationService) IssueChangeStatus(ctx context.Context, doer *user_model.User, commitID string, issue *issues_model.Issue, actionComment *issues_model.Comment, isClosed bool) {
|
func (ns *notificationService) IssueChangeStatus(ctx context.Context, doer *user_model.User, commitID string, issue *issues_model.Issue, actionComment *issues_model.Comment, isClosed bool) {
|
||||||
_ = ns.issueQueue.Push(issueNotificationOpts{
|
_ = ns.queue.Push(notificationOpts{
|
||||||
|
Source: util.Iif(issue.IsPull, activities_model.NotificationSourcePullRequest, activities_model.NotificationSourceIssue),
|
||||||
IssueID: issue.ID,
|
IssueID: issue.ID,
|
||||||
NotificationAuthorID: doer.ID,
|
NotificationAuthorID: doer.ID,
|
||||||
CommentID: actionComment.ID,
|
CommentID: actionComment.ID,
|
||||||
@ -115,7 +145,8 @@ func (ns *notificationService) IssueChangeTitle(ctx context.Context, doer *user_
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if issue.IsPull && issues_model.HasWorkInProgressPrefix(oldTitle) && !issue.PullRequest.IsWorkInProgress(ctx) {
|
if issue.IsPull && issues_model.HasWorkInProgressPrefix(oldTitle) && !issue.PullRequest.IsWorkInProgress(ctx) {
|
||||||
_ = ns.issueQueue.Push(issueNotificationOpts{
|
_ = ns.queue.Push(notificationOpts{
|
||||||
|
Source: util.Iif(issue.IsPull, activities_model.NotificationSourcePullRequest, activities_model.NotificationSourceIssue),
|
||||||
IssueID: issue.ID,
|
IssueID: issue.ID,
|
||||||
NotificationAuthorID: doer.ID,
|
NotificationAuthorID: doer.ID,
|
||||||
})
|
})
|
||||||
@ -123,7 +154,8 @@ func (ns *notificationService) IssueChangeTitle(ctx context.Context, doer *user_
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ns *notificationService) MergePullRequest(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest) {
|
func (ns *notificationService) MergePullRequest(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest) {
|
||||||
_ = ns.issueQueue.Push(issueNotificationOpts{
|
_ = ns.queue.Push(notificationOpts{
|
||||||
|
Source: activities_model.NotificationSourcePullRequest,
|
||||||
IssueID: pr.Issue.ID,
|
IssueID: pr.Issue.ID,
|
||||||
NotificationAuthorID: doer.ID,
|
NotificationAuthorID: doer.ID,
|
||||||
})
|
})
|
||||||
@ -160,7 +192,8 @@ func (ns *notificationService) NewPullRequest(ctx context.Context, pr *issues_mo
|
|||||||
toNotify.Add(mention.ID)
|
toNotify.Add(mention.ID)
|
||||||
}
|
}
|
||||||
for receiverID := range toNotify {
|
for receiverID := range toNotify {
|
||||||
_ = ns.issueQueue.Push(issueNotificationOpts{
|
_ = ns.queue.Push(notificationOpts{
|
||||||
|
Source: activities_model.NotificationSourcePullRequest,
|
||||||
IssueID: pr.Issue.ID,
|
IssueID: pr.Issue.ID,
|
||||||
NotificationAuthorID: pr.Issue.PosterID,
|
NotificationAuthorID: pr.Issue.PosterID,
|
||||||
ReceiverID: receiverID,
|
ReceiverID: receiverID,
|
||||||
@ -169,16 +202,17 @@ func (ns *notificationService) NewPullRequest(ctx context.Context, pr *issues_mo
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ns *notificationService) PullRequestReview(ctx context.Context, pr *issues_model.PullRequest, r *issues_model.Review, c *issues_model.Comment, mentions []*user_model.User) {
|
func (ns *notificationService) PullRequestReview(ctx context.Context, pr *issues_model.PullRequest, r *issues_model.Review, c *issues_model.Comment, mentions []*user_model.User) {
|
||||||
opts := issueNotificationOpts{
|
opts := notificationOpts{
|
||||||
|
Source: activities_model.NotificationSourcePullRequest,
|
||||||
IssueID: pr.Issue.ID,
|
IssueID: pr.Issue.ID,
|
||||||
NotificationAuthorID: r.Reviewer.ID,
|
NotificationAuthorID: r.Reviewer.ID,
|
||||||
}
|
}
|
||||||
if c != nil {
|
if c != nil {
|
||||||
opts.CommentID = c.ID
|
opts.CommentID = c.ID
|
||||||
}
|
}
|
||||||
_ = ns.issueQueue.Push(opts)
|
_ = ns.queue.Push(opts)
|
||||||
for _, mention := range mentions {
|
for _, mention := range mentions {
|
||||||
opts := issueNotificationOpts{
|
opts := notificationOpts{
|
||||||
IssueID: pr.Issue.ID,
|
IssueID: pr.Issue.ID,
|
||||||
NotificationAuthorID: r.Reviewer.ID,
|
NotificationAuthorID: r.Reviewer.ID,
|
||||||
ReceiverID: mention.ID,
|
ReceiverID: mention.ID,
|
||||||
@ -186,13 +220,14 @@ func (ns *notificationService) PullRequestReview(ctx context.Context, pr *issues
|
|||||||
if c != nil {
|
if c != nil {
|
||||||
opts.CommentID = c.ID
|
opts.CommentID = c.ID
|
||||||
}
|
}
|
||||||
_ = ns.issueQueue.Push(opts)
|
_ = ns.queue.Push(opts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *notificationService) PullRequestCodeComment(ctx context.Context, pr *issues_model.PullRequest, c *issues_model.Comment, mentions []*user_model.User) {
|
func (ns *notificationService) PullRequestCodeComment(ctx context.Context, pr *issues_model.PullRequest, c *issues_model.Comment, mentions []*user_model.User) {
|
||||||
for _, mention := range mentions {
|
for _, mention := range mentions {
|
||||||
_ = ns.issueQueue.Push(issueNotificationOpts{
|
_ = ns.queue.Push(notificationOpts{
|
||||||
|
Source: activities_model.NotificationSourcePullRequest,
|
||||||
IssueID: pr.Issue.ID,
|
IssueID: pr.Issue.ID,
|
||||||
NotificationAuthorID: c.Poster.ID,
|
NotificationAuthorID: c.Poster.ID,
|
||||||
CommentID: c.ID,
|
CommentID: c.ID,
|
||||||
@ -202,26 +237,29 @@ func (ns *notificationService) PullRequestCodeComment(ctx context.Context, pr *i
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ns *notificationService) PullRequestPushCommits(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest, comment *issues_model.Comment) {
|
func (ns *notificationService) PullRequestPushCommits(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest, comment *issues_model.Comment) {
|
||||||
opts := issueNotificationOpts{
|
opts := notificationOpts{
|
||||||
|
Source: activities_model.NotificationSourcePullRequest,
|
||||||
IssueID: pr.IssueID,
|
IssueID: pr.IssueID,
|
||||||
NotificationAuthorID: doer.ID,
|
NotificationAuthorID: doer.ID,
|
||||||
CommentID: comment.ID,
|
CommentID: comment.ID,
|
||||||
}
|
}
|
||||||
_ = ns.issueQueue.Push(opts)
|
_ = ns.queue.Push(opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *notificationService) PullReviewDismiss(ctx context.Context, doer *user_model.User, review *issues_model.Review, comment *issues_model.Comment) {
|
func (ns *notificationService) PullReviewDismiss(ctx context.Context, doer *user_model.User, review *issues_model.Review, comment *issues_model.Comment) {
|
||||||
opts := issueNotificationOpts{
|
opts := notificationOpts{
|
||||||
|
Source: activities_model.NotificationSourcePullRequest,
|
||||||
IssueID: review.IssueID,
|
IssueID: review.IssueID,
|
||||||
NotificationAuthorID: doer.ID,
|
NotificationAuthorID: doer.ID,
|
||||||
CommentID: comment.ID,
|
CommentID: comment.ID,
|
||||||
}
|
}
|
||||||
_ = ns.issueQueue.Push(opts)
|
_ = ns.queue.Push(opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *notificationService) IssueChangeAssignee(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, assignee *user_model.User, removed bool, comment *issues_model.Comment) {
|
func (ns *notificationService) IssueChangeAssignee(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, assignee *user_model.User, removed bool, comment *issues_model.Comment) {
|
||||||
if !removed && doer.ID != assignee.ID {
|
if !removed && doer.ID != assignee.ID {
|
||||||
opts := issueNotificationOpts{
|
opts := notificationOpts{
|
||||||
|
Source: activities_model.NotificationSourceIssue,
|
||||||
IssueID: issue.ID,
|
IssueID: issue.ID,
|
||||||
NotificationAuthorID: doer.ID,
|
NotificationAuthorID: doer.ID,
|
||||||
ReceiverID: assignee.ID,
|
ReceiverID: assignee.ID,
|
||||||
@ -231,13 +269,14 @@ func (ns *notificationService) IssueChangeAssignee(ctx context.Context, doer *us
|
|||||||
opts.CommentID = comment.ID
|
opts.CommentID = comment.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = ns.issueQueue.Push(opts)
|
_ = ns.queue.Push(opts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *notificationService) PullRequestReviewRequest(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, reviewer *user_model.User, isRequest bool, comment *issues_model.Comment) {
|
func (ns *notificationService) PullRequestReviewRequest(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, reviewer *user_model.User, isRequest bool, comment *issues_model.Comment) {
|
||||||
if isRequest {
|
if isRequest {
|
||||||
opts := issueNotificationOpts{
|
opts := notificationOpts{
|
||||||
|
Source: activities_model.NotificationSourcePullRequest,
|
||||||
IssueID: issue.ID,
|
IssueID: issue.ID,
|
||||||
NotificationAuthorID: doer.ID,
|
NotificationAuthorID: doer.ID,
|
||||||
ReceiverID: reviewer.ID,
|
ReceiverID: reviewer.ID,
|
||||||
@ -247,15 +286,103 @@ func (ns *notificationService) PullRequestReviewRequest(ctx context.Context, doe
|
|||||||
opts.CommentID = comment.ID
|
opts.CommentID = comment.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = ns.issueQueue.Push(opts)
|
_ = ns.queue.Push(opts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *notificationService) RepoPendingTransfer(ctx context.Context, doer, newOwner *user_model.User, repo *repo_model.Repository) {
|
func (ns *notificationService) RepoPendingTransfer(ctx context.Context, doer, newOwner *user_model.User, repo *repo_model.Repository) {
|
||||||
err := db.WithTx(ctx, func(ctx context.Context) error {
|
opts := notificationOpts{
|
||||||
return activities_model.CreateRepoTransferNotification(ctx, doer, newOwner, repo)
|
Source: activities_model.NotificationSourceRepository,
|
||||||
})
|
RepoID: repo.ID,
|
||||||
if err != nil {
|
NotificationAuthorID: doer.ID,
|
||||||
log.Error("CreateRepoTransferNotification: %v", err)
|
}
|
||||||
|
|
||||||
|
if newOwner.IsOrganization() {
|
||||||
|
users, err := organization.GetUsersWhoCanCreateOrgRepo(ctx, newOwner.ID)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("GetUsersWhoCanCreateOrgRepo: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i := range users {
|
||||||
|
opts.ReceiverID = users[i].ID
|
||||||
|
_ = ns.queue.Push(opts)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
opts.ReceiverID = newOwner.ID
|
||||||
|
_ = ns.queue.Push(opts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ns *notificationService) PushCommits(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
|
||||||
|
if len(commits.Commits) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, commit := range commits.Commits {
|
||||||
|
mentions := references.FindAllMentionsMarkdown(commit.Message)
|
||||||
|
receivers, err := user_model.GetUsersByUsernames(ctx, mentions)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("GetUserIDsByNames: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
notBlocked := make([]*user_model.User, 0, len(mentions))
|
||||||
|
for _, user := range receivers {
|
||||||
|
if !user_model.IsUserBlockedBy(ctx, repo.Owner, user.ID) {
|
||||||
|
notBlocked = append(notBlocked, user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
receivers = notBlocked
|
||||||
|
|
||||||
|
for _, receiver := range receivers {
|
||||||
|
perm, err := access_model.GetUserRepoPermission(ctx, repo, receiver)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("GetUserRepoPermission [%d]: %w", receiver.ID, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !perm.CanRead(unit.TypeCode) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := notificationOpts{
|
||||||
|
Source: activities_model.NotificationSourceCommit,
|
||||||
|
RepoID: repo.ID,
|
||||||
|
CommitID: commit.Sha1,
|
||||||
|
NotificationAuthorID: pusher.ID,
|
||||||
|
ReceiverID: receiver.ID,
|
||||||
|
}
|
||||||
|
if err := ns.queue.Push(opts); err != nil {
|
||||||
|
log.Error("PushCommits: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ns *notificationService) NewRelease(ctx context.Context, rel *repo_model.Release) {
|
||||||
|
_ = rel.LoadPublisher(ctx)
|
||||||
|
ns.UpdateRelease(ctx, rel.Publisher, rel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ns *notificationService) UpdateRelease(ctx context.Context, doer *user_model.User, rel *repo_model.Release) {
|
||||||
|
opts := notificationOpts{
|
||||||
|
Source: activities_model.NotificationSourceRelease,
|
||||||
|
ReleaseID: rel.ID,
|
||||||
|
NotificationAuthorID: rel.PublisherID,
|
||||||
|
}
|
||||||
|
|
||||||
|
repoWatcherIDs, err := repo_model.GetRepoWatchersIDs(ctx, rel.RepoID)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("GetRepoWatchersIDs: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, watcherID := range repoWatcherIDs {
|
||||||
|
if watcherID == doer.ID {
|
||||||
|
// Do not notify the publisher of the release
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
opts.ReceiverID = watcherID
|
||||||
|
_ = ns.queue.Push(opts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user