Add option to change mail from user display name (#31528)
Make it posible to let mails show e.g.: `Max Musternam (via gitea.kithara.com) <gitea@kithara.com>` Docs: https://gitea.com/gitea/docs/pulls/23 --- *Sponsored by Kithara Software GmbH* (cherry picked from commit 0f533241829d0d48aa16a91e7dc0614fe50bc317) Conflicts: - services/mailer/mail_release.go services/mailer/mail_test.go In both cases, applied the changes manually.
This commit is contained in:
parent
54f2dcff9d
commit
004cc6dc0a
|
@ -1727,6 +1727,10 @@ LEVEL = Info
|
||||||
;; Sometimes it is helpful to use a different address on the envelope. Set this to use ENVELOPE_FROM as the from on the envelope. Set to `<>` to send an empty address.
|
;; Sometimes it is helpful to use a different address on the envelope. Set this to use ENVELOPE_FROM as the from on the envelope. Set to `<>` to send an empty address.
|
||||||
;ENVELOPE_FROM =
|
;ENVELOPE_FROM =
|
||||||
;;
|
;;
|
||||||
|
;; If gitea sends mails on behave of users, it will just use the name also displayed in the WebUI. If you want e.g. `Mister X (by CodeIt) <gitea@codeit.net>`,
|
||||||
|
;; set it to `{{ .DisplayName }} (by {{ .AppName }})`. Available Variables: `.DisplayName`, `.AppName` and `.Domain`.
|
||||||
|
;FROM_DISPLAY_NAME_FORMAT = {{ .DisplayName }}
|
||||||
|
;;
|
||||||
;; Mailer user name and password, if required by provider.
|
;; Mailer user name and password, if required by provider.
|
||||||
;USER =
|
;USER =
|
||||||
;;
|
;;
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"net/mail"
|
"net/mail"
|
||||||
"strings"
|
"strings"
|
||||||
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
@ -46,6 +47,10 @@ type Mailer struct {
|
||||||
SendmailArgs []string `ini:"-"`
|
SendmailArgs []string `ini:"-"`
|
||||||
SendmailTimeout time.Duration `ini:"SENDMAIL_TIMEOUT"`
|
SendmailTimeout time.Duration `ini:"SENDMAIL_TIMEOUT"`
|
||||||
SendmailConvertCRLF bool `ini:"SENDMAIL_CONVERT_CRLF"`
|
SendmailConvertCRLF bool `ini:"SENDMAIL_CONVERT_CRLF"`
|
||||||
|
|
||||||
|
// Customization
|
||||||
|
FromDisplayNameFormat string `ini:"FROM_DISPLAY_NAME_FORMAT"`
|
||||||
|
FromDisplayNameFormatTemplate *template.Template `ini:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// MailService the global mailer
|
// MailService the global mailer
|
||||||
|
@ -234,6 +239,16 @@ func loadMailerFrom(rootCfg ConfigProvider) {
|
||||||
log.Error("no mailer.FROM provided, email system may not work.")
|
log.Error("no mailer.FROM provided, email system may not work.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MailService.FromDisplayNameFormatTemplate, _ = template.New("mailFrom").Parse("{{ .DisplayName }}")
|
||||||
|
if MailService.FromDisplayNameFormat != "" {
|
||||||
|
template, err := template.New("mailFrom").Parse(MailService.FromDisplayNameFormat)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("mailer.FROM_DISPLAY_NAME_FORMAT is no valid template: %v", err)
|
||||||
|
} else {
|
||||||
|
MailService.FromDisplayNameFormatTemplate = template
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch MailService.EnvelopeFrom {
|
switch MailService.EnvelopeFrom {
|
||||||
case "":
|
case "":
|
||||||
MailService.OverrideEnvelopeFrom = false
|
MailService.OverrideEnvelopeFrom = false
|
||||||
|
|
|
@ -313,7 +313,7 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient
|
||||||
for _, recipient := range recipients {
|
for _, recipient := range recipients {
|
||||||
msg := NewMessageFrom(
|
msg := NewMessageFrom(
|
||||||
recipient.Email,
|
recipient.Email,
|
||||||
ctx.Doer.GetCompleteName(),
|
fromDisplayName(ctx.Doer),
|
||||||
setting.MailService.FromEmail,
|
setting.MailService.FromEmail,
|
||||||
subject,
|
subject,
|
||||||
mailBody.String(),
|
mailBody.String(),
|
||||||
|
@ -545,3 +545,19 @@ func actionToTemplate(issue *issues_model.Issue, actionType activities_model.Act
|
||||||
}
|
}
|
||||||
return typeName, name, template
|
return typeName, name, template
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fromDisplayName(u *user_model.User) string {
|
||||||
|
if setting.MailService.FromDisplayNameFormatTemplate != nil {
|
||||||
|
var ctx bytes.Buffer
|
||||||
|
err := setting.MailService.FromDisplayNameFormatTemplate.Execute(&ctx, map[string]any{
|
||||||
|
"DisplayName": u.DisplayName(),
|
||||||
|
"AppName": setting.AppName,
|
||||||
|
"Domain": setting.Domain,
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
return mime.QEncoding.Encode("utf-8", ctx.String())
|
||||||
|
}
|
||||||
|
log.Error("fromDisplayName: %w", err)
|
||||||
|
}
|
||||||
|
return u.GetCompleteName()
|
||||||
|
}
|
||||||
|
|
|
@ -85,7 +85,7 @@ func mailNewRelease(ctx context.Context, lang string, tos []*user_model.User, re
|
||||||
}
|
}
|
||||||
|
|
||||||
msgs := make([]*Message, 0, len(tos))
|
msgs := make([]*Message, 0, len(tos))
|
||||||
publisherName := rel.Publisher.DisplayName()
|
publisherName := fromDisplayName(rel.Publisher)
|
||||||
msgID := createMessageIDForRelease(rel)
|
msgID := createMessageIDForRelease(rel)
|
||||||
for _, to := range tos {
|
for _, to := range tos {
|
||||||
msg := NewMessageFrom(to.EmailTo(), publisherName, setting.MailService.FromEmail, subject, mailBody.String())
|
msg := NewMessageFrom(to.EmailTo(), publisherName, setting.MailService.FromEmail, subject, mailBody.String())
|
||||||
|
|
|
@ -79,7 +79,7 @@ func sendRepoTransferNotifyMailPerLang(lang string, newOwner, doer *user_model.U
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, to := range emailTos {
|
for _, to := range emailTos {
|
||||||
msg := NewMessage(to.EmailTo(), subject, content.String())
|
msg := NewMessageFrom(to.EmailTo(), fromDisplayName(doer), setting.MailService.FromEmail, subject, content.String())
|
||||||
msg.Info = fmt.Sprintf("UID: %d, repository pending transfer notification", newOwner.ID)
|
msg.Info = fmt.Sprintf("UID: %d, repository pending transfer notification", newOwner.ID)
|
||||||
|
|
||||||
SendAsync(msg)
|
SendAsync(msg)
|
||||||
|
|
|
@ -489,3 +489,51 @@ func Test_createReference(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFromDisplayName(t *testing.T) {
|
||||||
|
template, err := texttmpl.New("mailFrom").Parse("{{ .DisplayName }}")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
setting.MailService = &setting.Mailer{FromDisplayNameFormatTemplate: template}
|
||||||
|
defer func() { setting.MailService = nil }()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
userDisplayName string
|
||||||
|
fromDisplayName string
|
||||||
|
}{{
|
||||||
|
userDisplayName: "test",
|
||||||
|
fromDisplayName: "test",
|
||||||
|
}, {
|
||||||
|
userDisplayName: "Hi Its <Mee>",
|
||||||
|
fromDisplayName: "Hi Its <Mee>",
|
||||||
|
}, {
|
||||||
|
userDisplayName: "Æsir",
|
||||||
|
fromDisplayName: "=?utf-8?q?=C3=86sir?=",
|
||||||
|
}, {
|
||||||
|
userDisplayName: "new😀user",
|
||||||
|
fromDisplayName: "=?utf-8?q?new=F0=9F=98=80user?=",
|
||||||
|
}}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.userDisplayName, func(t *testing.T) {
|
||||||
|
user := &user_model.User{FullName: tc.userDisplayName, Name: "tmp"}
|
||||||
|
got := fromDisplayName(user)
|
||||||
|
assert.EqualValues(t, tc.fromDisplayName, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("template with all available vars", func(t *testing.T) {
|
||||||
|
template, err = texttmpl.New("mailFrom").Parse("{{ .DisplayName }} (by {{ .AppName }} on [{{ .Domain }}])")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
setting.MailService = &setting.Mailer{FromDisplayNameFormatTemplate: template}
|
||||||
|
oldAppName := setting.AppName
|
||||||
|
setting.AppName = "Code IT"
|
||||||
|
oldDomain := setting.Domain
|
||||||
|
setting.Domain = "code.it"
|
||||||
|
defer func() {
|
||||||
|
setting.AppName = oldAppName
|
||||||
|
setting.Domain = oldDomain
|
||||||
|
}()
|
||||||
|
|
||||||
|
assert.EqualValues(t, "Mister X (by Code IT on [code.it])", fromDisplayName(&user_model.User{FullName: "Mister X", Name: "tmp"}))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue