mirror of https://github.com/go-gitea/gitea.git
parent
b0ee340969
commit
e7cf62f2f7
|
@ -25,9 +25,8 @@ func MailParticipantsComment(ctx context.Context, c *issues_model.Comment, opTyp
|
||||||
if c.Type == issues_model.CommentTypePullRequestPush {
|
if c.Type == issues_model.CommentTypePullRequestPush {
|
||||||
content = ""
|
content = ""
|
||||||
}
|
}
|
||||||
if err := mailIssueCommentToParticipants(
|
if err := mailIssueCommentToParticipants(ctx,
|
||||||
&mailCommentContext{
|
&mailComment{
|
||||||
Context: ctx,
|
|
||||||
Issue: issue,
|
Issue: issue,
|
||||||
Doer: c.Poster,
|
Doer: c.Poster,
|
||||||
ActionType: opType,
|
ActionType: opType,
|
||||||
|
@ -48,9 +47,8 @@ func MailMentionsComment(ctx context.Context, pr *issues_model.PullRequest, c *i
|
||||||
|
|
||||||
visited := make(container.Set[int64], len(mentions)+1)
|
visited := make(container.Set[int64], len(mentions)+1)
|
||||||
visited.Add(c.Poster.ID)
|
visited.Add(c.Poster.ID)
|
||||||
if err = mailIssueCommentBatch(
|
if err = mailIssueCommentBatch(ctx,
|
||||||
&mailCommentContext{
|
&mailComment{
|
||||||
Context: ctx,
|
|
||||||
Issue: pr.Issue,
|
Issue: pr.Issue,
|
||||||
Doer: c.Poster,
|
Doer: c.Poster,
|
||||||
ActionType: activities_model.ActionCommentPull,
|
ActionType: activities_model.ActionCommentPull,
|
||||||
|
|
|
@ -24,15 +24,15 @@ const MailBatchSize = 100 // batch size used in mailIssueCommentBatch
|
||||||
// This function sends two list of emails:
|
// This function sends two list of emails:
|
||||||
// 1. Repository watchers (except for WIP pull requests) and users who are participated in comments.
|
// 1. Repository watchers (except for WIP pull requests) and users who are participated in comments.
|
||||||
// 2. Users who are not in 1. but get mentioned in current issue/comment.
|
// 2. Users who are not in 1. but get mentioned in current issue/comment.
|
||||||
func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []*user_model.User) error {
|
func mailIssueCommentToParticipants(ctx context.Context, comment *mailComment, mentions []*user_model.User) error {
|
||||||
// Required by the mail composer; make sure to load these before calling the async function
|
// Required by the mail composer; make sure to load these before calling the async function
|
||||||
if err := ctx.Issue.LoadRepo(ctx); err != nil {
|
if err := comment.Issue.LoadRepo(ctx); err != nil {
|
||||||
return fmt.Errorf("LoadRepo: %w", err)
|
return fmt.Errorf("LoadRepo: %w", err)
|
||||||
}
|
}
|
||||||
if err := ctx.Issue.LoadPoster(ctx); err != nil {
|
if err := comment.Issue.LoadPoster(ctx); err != nil {
|
||||||
return fmt.Errorf("LoadPoster: %w", err)
|
return fmt.Errorf("LoadPoster: %w", err)
|
||||||
}
|
}
|
||||||
if err := ctx.Issue.LoadPullRequest(ctx); err != nil {
|
if err := comment.Issue.LoadPullRequest(ctx); err != nil {
|
||||||
return fmt.Errorf("LoadPullRequest: %w", err)
|
return fmt.Errorf("LoadPullRequest: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,35 +40,35 @@ func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []*user_mo
|
||||||
unfiltered := make([]int64, 1, 64)
|
unfiltered := make([]int64, 1, 64)
|
||||||
|
|
||||||
// =========== Original poster ===========
|
// =========== Original poster ===========
|
||||||
unfiltered[0] = ctx.Issue.PosterID
|
unfiltered[0] = comment.Issue.PosterID
|
||||||
|
|
||||||
// =========== Assignees ===========
|
// =========== Assignees ===========
|
||||||
ids, err := issues_model.GetAssigneeIDsByIssue(ctx, ctx.Issue.ID)
|
ids, err := issues_model.GetAssigneeIDsByIssue(ctx, comment.Issue.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("GetAssigneeIDsByIssue(%d): %w", ctx.Issue.ID, err)
|
return fmt.Errorf("GetAssigneeIDsByIssue(%d): %w", comment.Issue.ID, err)
|
||||||
}
|
}
|
||||||
unfiltered = append(unfiltered, ids...)
|
unfiltered = append(unfiltered, ids...)
|
||||||
|
|
||||||
// =========== Participants (i.e. commenters, reviewers) ===========
|
// =========== Participants (i.e. commenters, reviewers) ===========
|
||||||
ids, err = issues_model.GetParticipantsIDsByIssueID(ctx, ctx.Issue.ID)
|
ids, err = issues_model.GetParticipantsIDsByIssueID(ctx, comment.Issue.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("GetParticipantsIDsByIssueID(%d): %w", ctx.Issue.ID, err)
|
return fmt.Errorf("GetParticipantsIDsByIssueID(%d): %w", comment.Issue.ID, err)
|
||||||
}
|
}
|
||||||
unfiltered = append(unfiltered, ids...)
|
unfiltered = append(unfiltered, ids...)
|
||||||
|
|
||||||
// =========== Issue watchers ===========
|
// =========== Issue watchers ===========
|
||||||
ids, err = issues_model.GetIssueWatchersIDs(ctx, ctx.Issue.ID, true)
|
ids, err = issues_model.GetIssueWatchersIDs(ctx, comment.Issue.ID, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("GetIssueWatchersIDs(%d): %w", ctx.Issue.ID, err)
|
return fmt.Errorf("GetIssueWatchersIDs(%d): %w", comment.Issue.ID, err)
|
||||||
}
|
}
|
||||||
unfiltered = append(unfiltered, ids...)
|
unfiltered = append(unfiltered, ids...)
|
||||||
|
|
||||||
// =========== Repo watchers ===========
|
// =========== Repo watchers ===========
|
||||||
// Make repo watchers last, since it's likely the list with the most users
|
// Make repo watchers last, since it's likely the list with the most users
|
||||||
if !(ctx.Issue.IsPull && ctx.Issue.PullRequest.IsWorkInProgress(ctx) && ctx.ActionType != activities_model.ActionCreatePullRequest) {
|
if !(comment.Issue.IsPull && comment.Issue.PullRequest.IsWorkInProgress(ctx) && comment.ActionType != activities_model.ActionCreatePullRequest) {
|
||||||
ids, err = repo_model.GetRepoWatchersIDs(ctx, ctx.Issue.RepoID)
|
ids, err = repo_model.GetRepoWatchersIDs(ctx, comment.Issue.RepoID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("GetRepoWatchersIDs(%d): %w", ctx.Issue.RepoID, err)
|
return fmt.Errorf("GetRepoWatchersIDs(%d): %w", comment.Issue.RepoID, err)
|
||||||
}
|
}
|
||||||
unfiltered = append(ids, unfiltered...)
|
unfiltered = append(ids, unfiltered...)
|
||||||
}
|
}
|
||||||
|
@ -76,19 +76,19 @@ func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []*user_mo
|
||||||
visited := make(container.Set[int64], len(unfiltered)+len(mentions)+1)
|
visited := make(container.Set[int64], len(unfiltered)+len(mentions)+1)
|
||||||
|
|
||||||
// Avoid mailing the doer
|
// Avoid mailing the doer
|
||||||
if ctx.Doer.EmailNotificationsPreference != user_model.EmailNotificationsAndYourOwn && !ctx.ForceDoerNotification {
|
if comment.Doer.EmailNotificationsPreference != user_model.EmailNotificationsAndYourOwn && !comment.ForceDoerNotification {
|
||||||
visited.Add(ctx.Doer.ID)
|
visited.Add(comment.Doer.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// =========== Mentions ===========
|
// =========== Mentions ===========
|
||||||
if err = mailIssueCommentBatch(ctx, mentions, visited, true); err != nil {
|
if err = mailIssueCommentBatch(ctx, comment, mentions, visited, true); err != nil {
|
||||||
return fmt.Errorf("mailIssueCommentBatch() mentions: %w", err)
|
return fmt.Errorf("mailIssueCommentBatch() mentions: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Avoid mailing explicit unwatched
|
// Avoid mailing explicit unwatched
|
||||||
ids, err = issues_model.GetIssueWatchersIDs(ctx, ctx.Issue.ID, false)
|
ids, err = issues_model.GetIssueWatchersIDs(ctx, comment.Issue.ID, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("GetIssueWatchersIDs(%d): %w", ctx.Issue.ID, err)
|
return fmt.Errorf("GetIssueWatchersIDs(%d): %w", comment.Issue.ID, err)
|
||||||
}
|
}
|
||||||
visited.AddMultiple(ids...)
|
visited.AddMultiple(ids...)
|
||||||
|
|
||||||
|
@ -96,16 +96,16 @@ func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []*user_mo
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err = mailIssueCommentBatch(ctx, unfilteredUsers, visited, false); err != nil {
|
if err = mailIssueCommentBatch(ctx, comment, unfilteredUsers, visited, false); err != nil {
|
||||||
return fmt.Errorf("mailIssueCommentBatch(): %w", err)
|
return fmt.Errorf("mailIssueCommentBatch(): %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func mailIssueCommentBatch(ctx *mailCommentContext, users []*user_model.User, visited container.Set[int64], fromMention bool) error {
|
func mailIssueCommentBatch(ctx context.Context, comment *mailComment, users []*user_model.User, visited container.Set[int64], fromMention bool) error {
|
||||||
checkUnit := unit.TypeIssues
|
checkUnit := unit.TypeIssues
|
||||||
if ctx.Issue.IsPull {
|
if comment.Issue.IsPull {
|
||||||
checkUnit = unit.TypePullRequests
|
checkUnit = unit.TypePullRequests
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,7 +129,7 @@ func mailIssueCommentBatch(ctx *mailCommentContext, users []*user_model.User, vi
|
||||||
}
|
}
|
||||||
|
|
||||||
// test if this user is allowed to see the issue/pull
|
// test if this user is allowed to see the issue/pull
|
||||||
if !access_model.CheckRepoUnitUser(ctx, ctx.Issue.Repo, user, checkUnit) {
|
if !access_model.CheckRepoUnitUser(ctx, comment.Issue.Repo, user, checkUnit) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,7 +141,7 @@ func mailIssueCommentBatch(ctx *mailCommentContext, users []*user_model.User, vi
|
||||||
// working backwards from the last (possibly) incomplete batch. If len(receivers) can be 0 this
|
// working backwards from the last (possibly) incomplete batch. If len(receivers) can be 0 this
|
||||||
// starting condition will need to be changed slightly
|
// starting condition will need to be changed slightly
|
||||||
for i := ((len(receivers) - 1) / MailBatchSize) * MailBatchSize; i >= 0; i -= MailBatchSize {
|
for i := ((len(receivers) - 1) / MailBatchSize) * MailBatchSize; i >= 0; i -= MailBatchSize {
|
||||||
msgs, err := composeIssueCommentMessages(ctx, lang, receivers[i:], fromMention, "issue comments")
|
msgs, err := composeIssueCommentMessages(ctx, comment, lang, receivers[i:], fromMention, "issue comments")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -168,9 +168,8 @@ func MailParticipants(ctx context.Context, issue *issues_model.Issue, doer *user
|
||||||
content = ""
|
content = ""
|
||||||
}
|
}
|
||||||
forceDoerNotification := opType == activities_model.ActionAutoMergePullRequest
|
forceDoerNotification := opType == activities_model.ActionAutoMergePullRequest
|
||||||
if err := mailIssueCommentToParticipants(
|
if err := mailIssueCommentToParticipants(ctx,
|
||||||
&mailCommentContext{
|
&mailComment{
|
||||||
Context: ctx,
|
|
||||||
Issue: issue,
|
Issue: issue,
|
||||||
Doer: doer,
|
Doer: doer,
|
||||||
ActionType: opType,
|
ActionType: opType,
|
||||||
|
@ -205,8 +204,7 @@ func SendIssueAssignedMail(ctx context.Context, issue *issues_model.Issue, doer
|
||||||
}
|
}
|
||||||
|
|
||||||
for lang, tos := range langMap {
|
for lang, tos := range langMap {
|
||||||
msgs, err := composeIssueCommentMessages(&mailCommentContext{
|
msgs, err := composeIssueCommentMessages(ctx, &mailComment{
|
||||||
Context: ctx,
|
|
||||||
Issue: issue,
|
Issue: issue,
|
||||||
Doer: doer,
|
Doer: doer,
|
||||||
ActionType: activities_model.ActionType(0),
|
ActionType: activities_model.ActionType(0),
|
||||||
|
|
|
@ -33,8 +33,7 @@ func fallbackMailSubject(issue *issues_model.Issue) string {
|
||||||
return fmt.Sprintf("[%s] %s (#%d)", issue.Repo.FullName(), issue.Title, issue.Index)
|
return fmt.Sprintf("[%s] %s (#%d)", issue.Repo.FullName(), issue.Title, issue.Index)
|
||||||
}
|
}
|
||||||
|
|
||||||
type mailCommentContext struct {
|
type mailComment struct {
|
||||||
context.Context
|
|
||||||
Issue *issues_model.Issue
|
Issue *issues_model.Issue
|
||||||
Doer *user_model.User
|
Doer *user_model.User
|
||||||
ActionType activities_model.ActionType
|
ActionType activities_model.ActionType
|
||||||
|
@ -43,7 +42,7 @@ type mailCommentContext struct {
|
||||||
ForceDoerNotification bool
|
ForceDoerNotification bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipients []*user_model.User, fromMention bool, info string) ([]*sender_service.Message, error) {
|
func composeIssueCommentMessages(ctx context.Context, comment *mailComment, lang string, recipients []*user_model.User, fromMention bool, info string) ([]*sender_service.Message, error) {
|
||||||
var (
|
var (
|
||||||
subject string
|
subject string
|
||||||
link string
|
link string
|
||||||
|
@ -54,27 +53,27 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient
|
||||||
)
|
)
|
||||||
|
|
||||||
commentType := issues_model.CommentTypeComment
|
commentType := issues_model.CommentTypeComment
|
||||||
if ctx.Comment != nil {
|
if comment.Comment != nil {
|
||||||
commentType = ctx.Comment.Type
|
commentType = comment.Comment.Type
|
||||||
link = ctx.Issue.HTMLURL() + "#" + ctx.Comment.HashTag()
|
link = comment.Issue.HTMLURL() + "#" + comment.Comment.HashTag()
|
||||||
} else {
|
} else {
|
||||||
link = ctx.Issue.HTMLURL()
|
link = comment.Issue.HTMLURL()
|
||||||
}
|
}
|
||||||
|
|
||||||
reviewType := issues_model.ReviewTypeComment
|
reviewType := issues_model.ReviewTypeComment
|
||||||
if ctx.Comment != nil && ctx.Comment.Review != nil {
|
if comment.Comment != nil && comment.Comment.Review != nil {
|
||||||
reviewType = ctx.Comment.Review.Type
|
reviewType = comment.Comment.Review.Type
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is the body of the new issue or comment, not the mail body
|
// This is the body of the new issue or comment, not the mail body
|
||||||
rctx := renderhelper.NewRenderContextRepoComment(ctx.Context, ctx.Issue.Repo).WithUseAbsoluteLink(true)
|
rctx := renderhelper.NewRenderContextRepoComment(ctx, comment.Issue.Repo).WithUseAbsoluteLink(true)
|
||||||
body, err := markdown.RenderString(rctx, ctx.Content)
|
body, err := markdown.RenderString(rctx, comment.Content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if setting.MailService.EmbedAttachmentImages {
|
if setting.MailService.EmbedAttachmentImages {
|
||||||
attEmbedder := newMailAttachmentBase64Embedder(ctx.Doer, ctx.Issue.Repo, maxEmailBodySize)
|
attEmbedder := newMailAttachmentBase64Embedder(comment.Doer, comment.Issue.Repo, maxEmailBodySize)
|
||||||
bodyAfterEmbedding, err := attEmbedder.Base64InlineImages(ctx, body)
|
bodyAfterEmbedding, err := attEmbedder.Base64InlineImages(ctx, body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Failed to embed images in mail body: %v", err)
|
log.Error("Failed to embed images in mail body: %v", err)
|
||||||
|
@ -82,16 +81,16 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient
|
||||||
body = bodyAfterEmbedding
|
body = bodyAfterEmbedding
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
actType, actName, tplName := actionToTemplate(ctx.Issue, ctx.ActionType, commentType, reviewType)
|
actType, actName, tplName := actionToTemplate(comment.Issue, comment.ActionType, commentType, reviewType)
|
||||||
|
|
||||||
if actName != "new" {
|
if actName != "new" {
|
||||||
prefix = "Re: "
|
prefix = "Re: "
|
||||||
}
|
}
|
||||||
fallback = prefix + fallbackMailSubject(ctx.Issue)
|
fallback = prefix + fallbackMailSubject(comment.Issue)
|
||||||
|
|
||||||
if ctx.Comment != nil && ctx.Comment.Review != nil {
|
if comment.Comment != nil && comment.Comment.Review != nil {
|
||||||
reviewComments = make([]*issues_model.Comment, 0, 10)
|
reviewComments = make([]*issues_model.Comment, 0, 10)
|
||||||
for _, lines := range ctx.Comment.Review.CodeComments {
|
for _, lines := range comment.Comment.Review.CodeComments {
|
||||||
for _, comments := range lines {
|
for _, comments := range lines {
|
||||||
reviewComments = append(reviewComments, comments...)
|
reviewComments = append(reviewComments, comments...)
|
||||||
}
|
}
|
||||||
|
@ -104,12 +103,12 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient
|
||||||
"FallbackSubject": fallback,
|
"FallbackSubject": fallback,
|
||||||
"Body": body,
|
"Body": body,
|
||||||
"Link": link,
|
"Link": link,
|
||||||
"Issue": ctx.Issue,
|
"Issue": comment.Issue,
|
||||||
"Comment": ctx.Comment,
|
"Comment": comment.Comment,
|
||||||
"IsPull": ctx.Issue.IsPull,
|
"IsPull": comment.Issue.IsPull,
|
||||||
"User": ctx.Issue.Repo.MustOwner(ctx),
|
"User": comment.Issue.Repo.MustOwner(ctx),
|
||||||
"Repo": ctx.Issue.Repo.FullName(),
|
"Repo": comment.Issue.Repo.FullName(),
|
||||||
"Doer": ctx.Doer,
|
"Doer": comment.Doer,
|
||||||
"IsMention": fromMention,
|
"IsMention": fromMention,
|
||||||
"SubjectPrefix": prefix,
|
"SubjectPrefix": prefix,
|
||||||
"ActionType": actType,
|
"ActionType": actType,
|
||||||
|
@ -140,22 +139,22 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure to compose independent messages to avoid leaking user emails
|
// Make sure to compose independent messages to avoid leaking user emails
|
||||||
msgID := generateMessageIDForIssue(ctx.Issue, ctx.Comment, ctx.ActionType)
|
msgID := generateMessageIDForIssue(comment.Issue, comment.Comment, comment.ActionType)
|
||||||
reference := generateMessageIDForIssue(ctx.Issue, nil, activities_model.ActionType(0))
|
reference := generateMessageIDForIssue(comment.Issue, nil, activities_model.ActionType(0))
|
||||||
|
|
||||||
var replyPayload []byte
|
var replyPayload []byte
|
||||||
if ctx.Comment != nil {
|
if comment.Comment != nil {
|
||||||
if ctx.Comment.Type.HasMailReplySupport() {
|
if comment.Comment.Type.HasMailReplySupport() {
|
||||||
replyPayload, err = incoming_payload.CreateReferencePayload(ctx.Comment)
|
replyPayload, err = incoming_payload.CreateReferencePayload(comment.Comment)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
replyPayload, err = incoming_payload.CreateReferencePayload(ctx.Issue)
|
replyPayload, err = incoming_payload.CreateReferencePayload(comment.Issue)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
unsubscribePayload, err := incoming_payload.CreateReferencePayload(ctx.Issue)
|
unsubscribePayload, err := incoming_payload.CreateReferencePayload(comment.Issue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -164,7 +163,7 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient
|
||||||
for _, recipient := range recipients {
|
for _, recipient := range recipients {
|
||||||
msg := sender_service.NewMessageFrom(
|
msg := sender_service.NewMessageFrom(
|
||||||
recipient.Email,
|
recipient.Email,
|
||||||
fromDisplayName(ctx.Doer),
|
fromDisplayName(comment.Doer),
|
||||||
setting.MailService.FromEmail,
|
setting.MailService.FromEmail,
|
||||||
subject,
|
subject,
|
||||||
mailBody.String(),
|
mailBody.String(),
|
||||||
|
@ -175,7 +174,7 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient
|
||||||
msg.SetHeader("In-Reply-To", reference)
|
msg.SetHeader("In-Reply-To", reference)
|
||||||
|
|
||||||
references := []string{reference}
|
references := []string{reference}
|
||||||
listUnsubscribe := []string{"<" + ctx.Issue.HTMLURL() + ">"}
|
listUnsubscribe := []string{"<" + comment.Issue.HTMLURL() + ">"}
|
||||||
|
|
||||||
if setting.IncomingEmail.Enabled {
|
if setting.IncomingEmail.Enabled {
|
||||||
if replyPayload != nil {
|
if replyPayload != nil {
|
||||||
|
@ -203,7 +202,7 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient
|
||||||
msg.SetHeader("References", references...)
|
msg.SetHeader("References", references...)
|
||||||
msg.SetHeader("List-Unsubscribe", listUnsubscribe...)
|
msg.SetHeader("List-Unsubscribe", listUnsubscribe...)
|
||||||
|
|
||||||
for key, value := range generateAdditionalHeaders(ctx, actType, recipient) {
|
for key, value := range generateAdditionalHeaders(comment, actType, recipient) {
|
||||||
msg.SetHeader(key, value)
|
msg.SetHeader(key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,7 +302,7 @@ func generateMessageIDForIssue(issue *issues_model.Issue, comment *issues_model.
|
||||||
return fmt.Sprintf("<%s/%s/%d%s@%s>", issue.Repo.FullName(), path, issue.Index, extra, setting.Domain)
|
return fmt.Sprintf("<%s/%s/%d%s@%s>", issue.Repo.FullName(), path, issue.Index, extra, setting.Domain)
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateAdditionalHeaders(ctx *mailCommentContext, reason string, recipient *user_model.User) map[string]string {
|
func generateAdditionalHeaders(ctx *mailComment, reason string, recipient *user_model.User) map[string]string {
|
||||||
repo := ctx.Issue.Repo
|
repo := ctx.Issue.Repo
|
||||||
|
|
||||||
return map[string]string{
|
return map[string]string{
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/markup"
|
"code.gitea.io/gitea/modules/markup"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/storage"
|
"code.gitea.io/gitea/modules/storage"
|
||||||
|
"code.gitea.io/gitea/modules/test"
|
||||||
"code.gitea.io/gitea/services/attachment"
|
"code.gitea.io/gitea/services/attachment"
|
||||||
sender_service "code.gitea.io/gitea/services/mailer/sender"
|
sender_service "code.gitea.io/gitea/services/mailer/sender"
|
||||||
|
|
||||||
|
@ -110,9 +111,8 @@ func TestComposeIssueComment(t *testing.T) {
|
||||||
bodyTemplates = template.Must(template.New("issue/comment").Parse(bodyTpl))
|
bodyTemplates = template.Must(template.New("issue/comment").Parse(bodyTpl))
|
||||||
|
|
||||||
recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}, {Name: "Test2", Email: "test2@gitea.com"}}
|
recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}, {Name: "Test2", Email: "test2@gitea.com"}}
|
||||||
msgs, err := composeIssueCommentMessages(&mailCommentContext{
|
msgs, err := composeIssueCommentMessages(t.Context(), &mailComment{
|
||||||
Context: t.Context(),
|
Issue: issue, Doer: doer, ActionType: activities_model.ActionCommentIssue,
|
||||||
Issue: issue, Doer: doer, ActionType: activities_model.ActionCommentIssue,
|
|
||||||
Content: fmt.Sprintf("test @%s %s#%d body", doer.Name, issue.Repo.FullName(), issue.Index),
|
Content: fmt.Sprintf("test @%s %s#%d body", doer.Name, issue.Repo.FullName(), issue.Index),
|
||||||
Comment: comment,
|
Comment: comment,
|
||||||
}, "en-US", recipients, false, "issue comment")
|
}, "en-US", recipients, false, "issue comment")
|
||||||
|
@ -150,6 +150,22 @@ func TestComposeIssueComment(t *testing.T) {
|
||||||
assert.Contains(t, string(b), fmt.Sprintf(`href="%s"`, issue.HTMLURL()))
|
assert.Contains(t, string(b), fmt.Sprintf(`href="%s"`, issue.HTMLURL()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMailMentionsComment(t *testing.T) {
|
||||||
|
doer, _, issue, comment := prepareMailerTest(t)
|
||||||
|
comment.Poster = doer
|
||||||
|
subjectTemplates = texttmpl.Must(texttmpl.New("issue/comment").Parse(subjectTpl))
|
||||||
|
bodyTemplates = template.Must(template.New("issue/comment").Parse(bodyTpl))
|
||||||
|
mails := 0
|
||||||
|
|
||||||
|
defer test.MockVariableValue(&SendAsync, func(msgs ...*sender_service.Message) {
|
||||||
|
mails = len(msgs)
|
||||||
|
})()
|
||||||
|
|
||||||
|
err := MailParticipantsComment(t.Context(), comment, activities_model.ActionCommentIssue, issue, []*user_model.User{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, 3, mails)
|
||||||
|
}
|
||||||
|
|
||||||
func TestComposeIssueMessage(t *testing.T) {
|
func TestComposeIssueMessage(t *testing.T) {
|
||||||
doer, _, issue, _ := prepareMailerTest(t)
|
doer, _, issue, _ := prepareMailerTest(t)
|
||||||
|
|
||||||
|
@ -157,9 +173,8 @@ func TestComposeIssueMessage(t *testing.T) {
|
||||||
bodyTemplates = template.Must(template.New("issue/new").Parse(bodyTpl))
|
bodyTemplates = template.Must(template.New("issue/new").Parse(bodyTpl))
|
||||||
|
|
||||||
recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}, {Name: "Test2", Email: "test2@gitea.com"}}
|
recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}, {Name: "Test2", Email: "test2@gitea.com"}}
|
||||||
msgs, err := composeIssueCommentMessages(&mailCommentContext{
|
msgs, err := composeIssueCommentMessages(t.Context(), &mailComment{
|
||||||
Context: t.Context(),
|
Issue: issue, Doer: doer, ActionType: activities_model.ActionCreateIssue,
|
||||||
Issue: issue, Doer: doer, ActionType: activities_model.ActionCreateIssue,
|
|
||||||
Content: "test body",
|
Content: "test body",
|
||||||
}, "en-US", recipients, false, "issue create")
|
}, "en-US", recipients, false, "issue create")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
@ -204,32 +219,28 @@ func TestTemplateSelection(t *testing.T) {
|
||||||
assert.Contains(t, wholemsg, expBody)
|
assert.Contains(t, wholemsg, expBody)
|
||||||
}
|
}
|
||||||
|
|
||||||
msg := testComposeIssueCommentMessage(t, &mailCommentContext{
|
msg := testComposeIssueCommentMessage(t, &mailComment{
|
||||||
Context: t.Context(),
|
Issue: issue, Doer: doer, ActionType: activities_model.ActionCreateIssue,
|
||||||
Issue: issue, Doer: doer, ActionType: activities_model.ActionCreateIssue,
|
|
||||||
Content: "test body",
|
Content: "test body",
|
||||||
}, recipients, false, "TestTemplateSelection")
|
}, recipients, false, "TestTemplateSelection")
|
||||||
expect(t, msg, "issue/new/subject", "issue/new/body")
|
expect(t, msg, "issue/new/subject", "issue/new/body")
|
||||||
|
|
||||||
msg = testComposeIssueCommentMessage(t, &mailCommentContext{
|
msg = testComposeIssueCommentMessage(t, &mailComment{
|
||||||
Context: t.Context(),
|
Issue: issue, Doer: doer, ActionType: activities_model.ActionCommentIssue,
|
||||||
Issue: issue, Doer: doer, ActionType: activities_model.ActionCommentIssue,
|
|
||||||
Content: "test body", Comment: comment,
|
Content: "test body", Comment: comment,
|
||||||
}, recipients, false, "TestTemplateSelection")
|
}, recipients, false, "TestTemplateSelection")
|
||||||
expect(t, msg, "issue/default/subject", "issue/default/body")
|
expect(t, msg, "issue/default/subject", "issue/default/body")
|
||||||
|
|
||||||
pull := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2, Repo: repo, Poster: doer})
|
pull := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2, Repo: repo, Poster: doer})
|
||||||
comment = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 4, Issue: pull})
|
comment = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 4, Issue: pull})
|
||||||
msg = testComposeIssueCommentMessage(t, &mailCommentContext{
|
msg = testComposeIssueCommentMessage(t, &mailComment{
|
||||||
Context: t.Context(),
|
Issue: pull, Doer: doer, ActionType: activities_model.ActionCommentPull,
|
||||||
Issue: pull, Doer: doer, ActionType: activities_model.ActionCommentPull,
|
|
||||||
Content: "test body", Comment: comment,
|
Content: "test body", Comment: comment,
|
||||||
}, recipients, false, "TestTemplateSelection")
|
}, recipients, false, "TestTemplateSelection")
|
||||||
expect(t, msg, "pull/comment/subject", "pull/comment/body")
|
expect(t, msg, "pull/comment/subject", "pull/comment/body")
|
||||||
|
|
||||||
msg = testComposeIssueCommentMessage(t, &mailCommentContext{
|
msg = testComposeIssueCommentMessage(t, &mailComment{
|
||||||
Context: t.Context(),
|
Issue: issue, Doer: doer, ActionType: activities_model.ActionCloseIssue,
|
||||||
Issue: issue, Doer: doer, ActionType: activities_model.ActionCloseIssue,
|
|
||||||
Content: "test body", Comment: comment,
|
Content: "test body", Comment: comment,
|
||||||
}, recipients, false, "TestTemplateSelection")
|
}, recipients, false, "TestTemplateSelection")
|
||||||
expect(t, msg, "Re: [user2/repo1] issue1 (#1)", "issue/close/body")
|
expect(t, msg, "Re: [user2/repo1] issue1 (#1)", "issue/close/body")
|
||||||
|
@ -246,9 +257,8 @@ func TestTemplateServices(t *testing.T) {
|
||||||
bodyTemplates = template.Must(template.New("issue/default").Parse(tplBody))
|
bodyTemplates = template.Must(template.New("issue/default").Parse(tplBody))
|
||||||
|
|
||||||
recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}}
|
recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}}
|
||||||
msg := testComposeIssueCommentMessage(t, &mailCommentContext{
|
msg := testComposeIssueCommentMessage(t, &mailComment{
|
||||||
Context: t.Context(),
|
Issue: issue, Doer: doer, ActionType: actionType,
|
||||||
Issue: issue, Doer: doer, ActionType: actionType,
|
|
||||||
Content: "test body", Comment: comment,
|
Content: "test body", Comment: comment,
|
||||||
}, recipients, fromMention, "TestTemplateServices")
|
}, recipients, fromMention, "TestTemplateServices")
|
||||||
|
|
||||||
|
@ -280,8 +290,8 @@ func TestTemplateServices(t *testing.T) {
|
||||||
"//Re: //")
|
"//Re: //")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testComposeIssueCommentMessage(t *testing.T, ctx *mailCommentContext, recipients []*user_model.User, fromMention bool, info string) *sender_service.Message {
|
func testComposeIssueCommentMessage(t *testing.T, ctx *mailComment, recipients []*user_model.User, fromMention bool, info string) *sender_service.Message {
|
||||||
msgs, err := composeIssueCommentMessages(ctx, "en-US", recipients, fromMention, info)
|
msgs, err := composeIssueCommentMessages(t.Context(), ctx, "en-US", recipients, fromMention, info)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, msgs, 1)
|
assert.Len(t, msgs, 1)
|
||||||
return msgs[0]
|
return msgs[0]
|
||||||
|
@ -290,10 +300,10 @@ func testComposeIssueCommentMessage(t *testing.T, ctx *mailCommentContext, recip
|
||||||
func TestGenerateAdditionalHeaders(t *testing.T) {
|
func TestGenerateAdditionalHeaders(t *testing.T) {
|
||||||
doer, _, issue, _ := prepareMailerTest(t)
|
doer, _, issue, _ := prepareMailerTest(t)
|
||||||
|
|
||||||
ctx := &mailCommentContext{Context: t.Context(), Issue: issue, Doer: doer}
|
comment := &mailComment{Issue: issue, Doer: doer}
|
||||||
recipient := &user_model.User{Name: "test", Email: "test@gitea.com"}
|
recipient := &user_model.User{Name: "test", Email: "test@gitea.com"}
|
||||||
|
|
||||||
headers := generateAdditionalHeaders(ctx, "dummy-reason", recipient)
|
headers := generateAdditionalHeaders(comment, "dummy-reason", recipient)
|
||||||
|
|
||||||
expected := map[string]string{
|
expected := map[string]string{
|
||||||
"List-ID": "user2/repo1 <repo1.user2.localhost>",
|
"List-ID": "user2/repo1 <repo1.user2.localhost>",
|
||||||
|
@ -480,7 +490,7 @@ func TestFromDisplayName(t *testing.T) {
|
||||||
|
|
||||||
func TestEmbedBase64Images(t *testing.T) {
|
func TestEmbedBase64Images(t *testing.T) {
|
||||||
user, repo, issue, att1, att2 := prepareMailerBase64Test(t)
|
user, repo, issue, att1, att2 := prepareMailerBase64Test(t)
|
||||||
ctx := &mailCommentContext{Context: t.Context(), Issue: issue, Doer: user}
|
// comment := &mailComment{Issue: issue, Doer: user}
|
||||||
|
|
||||||
imgExternalURL := "https://via.placeholder.com/10"
|
imgExternalURL := "https://via.placeholder.com/10"
|
||||||
imgExternalImg := fmt.Sprintf(`<img src="%s"/>`, imgExternalURL)
|
imgExternalImg := fmt.Sprintf(`<img src="%s"/>`, imgExternalURL)
|
||||||
|
@ -509,8 +519,7 @@ func TestEmbedBase64Images(t *testing.T) {
|
||||||
require.NoError(t, issues_model.UpdateIssueCols(t.Context(), issue, "content"))
|
require.NoError(t, issues_model.UpdateIssueCols(t.Context(), issue, "content"))
|
||||||
|
|
||||||
recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}}
|
recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}}
|
||||||
msgs, err := composeIssueCommentMessages(&mailCommentContext{
|
msgs, err := composeIssueCommentMessages(t.Context(), &mailComment{
|
||||||
Context: t.Context(),
|
|
||||||
Issue: issue,
|
Issue: issue,
|
||||||
Doer: user,
|
Doer: user,
|
||||||
ActionType: activities_model.ActionCreateIssue,
|
ActionType: activities_model.ActionCreateIssue,
|
||||||
|
@ -526,7 +535,7 @@ func TestEmbedBase64Images(t *testing.T) {
|
||||||
mailBody := "<html><head></head><body><p>Test1</p>" + imgExternalImg + "<p>Test2</p>" + att1Img + "<p>Test3</p></body></html>"
|
mailBody := "<html><head></head><body><p>Test1</p>" + imgExternalImg + "<p>Test2</p>" + att1Img + "<p>Test3</p></body></html>"
|
||||||
expectedMailBody := "<html><head></head><body><p>Test1</p>" + imgExternalImg + "<p>Test2</p>" + att1ImgBase64 + "<p>Test3</p></body></html>"
|
expectedMailBody := "<html><head></head><body><p>Test1</p>" + imgExternalImg + "<p>Test2</p>" + att1ImgBase64 + "<p>Test3</p></body></html>"
|
||||||
b64embedder := newMailAttachmentBase64Embedder(user, repo, 1024)
|
b64embedder := newMailAttachmentBase64Embedder(user, repo, 1024)
|
||||||
resultMailBody, err := b64embedder.Base64InlineImages(ctx, template.HTML(mailBody))
|
resultMailBody, err := b64embedder.Base64InlineImages(t.Context(), template.HTML(mailBody))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, expectedMailBody, string(resultMailBody))
|
assert.Equal(t, expectedMailBody, string(resultMailBody))
|
||||||
})
|
})
|
||||||
|
@ -534,13 +543,13 @@ func TestEmbedBase64Images(t *testing.T) {
|
||||||
t.Run("LimitedEmailBodySize", func(t *testing.T) {
|
t.Run("LimitedEmailBodySize", func(t *testing.T) {
|
||||||
mailBody := fmt.Sprintf("<html><head></head><body>%s%s</body></html>", att1Img, att2Img)
|
mailBody := fmt.Sprintf("<html><head></head><body>%s%s</body></html>", att1Img, att2Img)
|
||||||
b64embedder := newMailAttachmentBase64Embedder(user, repo, 1024)
|
b64embedder := newMailAttachmentBase64Embedder(user, repo, 1024)
|
||||||
resultMailBody, err := b64embedder.Base64InlineImages(ctx, template.HTML(mailBody))
|
resultMailBody, err := b64embedder.Base64InlineImages(t.Context(), template.HTML(mailBody))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
expected := fmt.Sprintf("<html><head></head><body>%s%s</body></html>", att1ImgBase64, att2Img)
|
expected := fmt.Sprintf("<html><head></head><body>%s%s</body></html>", att1ImgBase64, att2Img)
|
||||||
assert.Equal(t, expected, string(resultMailBody))
|
assert.Equal(t, expected, string(resultMailBody))
|
||||||
|
|
||||||
b64embedder = newMailAttachmentBase64Embedder(user, repo, 4096)
|
b64embedder = newMailAttachmentBase64Embedder(user, repo, 4096)
|
||||||
resultMailBody, err = b64embedder.Base64InlineImages(ctx, template.HTML(mailBody))
|
resultMailBody, err = b64embedder.Base64InlineImages(t.Context(), template.HTML(mailBody))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
expected = fmt.Sprintf("<html><head></head><body>%s%s</body></html>", att1ImgBase64, att2ImgBase64)
|
expected = fmt.Sprintf("<html><head></head><body>%s%s</body></html>", att1ImgBase64, att2ImgBase64)
|
||||||
assert.Equal(t, expected, string(resultMailBody))
|
assert.Equal(t, expected, string(resultMailBody))
|
||||||
|
|
Loading…
Reference in New Issue