loading up the forgejo repo on tangled to test page performance
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

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.

authored by

6543 and committed by
Gergely Nagy
004cc6dc 54f2dcff

+86 -3
+4
custom/conf/app.example.ini
··· 1727 1727 ;; 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. 1728 1728 ;ENVELOPE_FROM = 1729 1729 ;; 1730 + ;; 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>`, 1731 + ;; set it to `{{ .DisplayName }} (by {{ .AppName }})`. Available Variables: `.DisplayName`, `.AppName` and `.Domain`. 1732 + ;FROM_DISPLAY_NAME_FORMAT = {{ .DisplayName }} 1733 + ;; 1730 1734 ;; Mailer user name and password, if required by provider. 1731 1735 ;USER = 1732 1736 ;;
+15
modules/setting/mailer.go
··· 8 8 "net" 9 9 "net/mail" 10 10 "strings" 11 + "text/template" 11 12 "time" 12 13 13 14 "code.gitea.io/gitea/modules/log" ··· 46 47 SendmailArgs []string `ini:"-"` 47 48 SendmailTimeout time.Duration `ini:"SENDMAIL_TIMEOUT"` 48 49 SendmailConvertCRLF bool `ini:"SENDMAIL_CONVERT_CRLF"` 50 + 51 + // Customization 52 + FromDisplayNameFormat string `ini:"FROM_DISPLAY_NAME_FORMAT"` 53 + FromDisplayNameFormatTemplate *template.Template `ini:"-"` 49 54 } 50 55 51 56 // MailService the global mailer ··· 232 237 MailService.FromEmail = parsed.Address 233 238 } else { 234 239 log.Error("no mailer.FROM provided, email system may not work.") 240 + } 241 + 242 + MailService.FromDisplayNameFormatTemplate, _ = template.New("mailFrom").Parse("{{ .DisplayName }}") 243 + if MailService.FromDisplayNameFormat != "" { 244 + template, err := template.New("mailFrom").Parse(MailService.FromDisplayNameFormat) 245 + if err != nil { 246 + log.Error("mailer.FROM_DISPLAY_NAME_FORMAT is no valid template: %v", err) 247 + } else { 248 + MailService.FromDisplayNameFormatTemplate = template 249 + } 235 250 } 236 251 237 252 switch MailService.EnvelopeFrom {
+17 -1
services/mailer/mail.go
··· 313 313 for _, recipient := range recipients { 314 314 msg := NewMessageFrom( 315 315 recipient.Email, 316 - ctx.Doer.GetCompleteName(), 316 + fromDisplayName(ctx.Doer), 317 317 setting.MailService.FromEmail, 318 318 subject, 319 319 mailBody.String(), ··· 545 545 } 546 546 return typeName, name, template 547 547 } 548 + 549 + func fromDisplayName(u *user_model.User) string { 550 + if setting.MailService.FromDisplayNameFormatTemplate != nil { 551 + var ctx bytes.Buffer 552 + err := setting.MailService.FromDisplayNameFormatTemplate.Execute(&ctx, map[string]any{ 553 + "DisplayName": u.DisplayName(), 554 + "AppName": setting.AppName, 555 + "Domain": setting.Domain, 556 + }) 557 + if err == nil { 558 + return mime.QEncoding.Encode("utf-8", ctx.String()) 559 + } 560 + log.Error("fromDisplayName: %w", err) 561 + } 562 + return u.GetCompleteName() 563 + }
+1 -1
services/mailer/mail_release.go
··· 85 85 } 86 86 87 87 msgs := make([]*Message, 0, len(tos)) 88 - publisherName := rel.Publisher.DisplayName() 88 + publisherName := fromDisplayName(rel.Publisher) 89 89 msgID := createMessageIDForRelease(rel) 90 90 for _, to := range tos { 91 91 msg := NewMessageFrom(to.EmailTo(), publisherName, setting.MailService.FromEmail, subject, mailBody.String())
+1 -1
services/mailer/mail_repo.go
··· 79 79 } 80 80 81 81 for _, to := range emailTos { 82 - msg := NewMessage(to.EmailTo(), subject, content.String()) 82 + msg := NewMessageFrom(to.EmailTo(), fromDisplayName(doer), setting.MailService.FromEmail, subject, content.String()) 83 83 msg.Info = fmt.Sprintf("UID: %d, repository pending transfer notification", newOwner.ID) 84 84 85 85 SendAsync(msg)
+48
services/mailer/mail_test.go
··· 489 489 }) 490 490 } 491 491 } 492 + 493 + func TestFromDisplayName(t *testing.T) { 494 + template, err := texttmpl.New("mailFrom").Parse("{{ .DisplayName }}") 495 + assert.NoError(t, err) 496 + setting.MailService = &setting.Mailer{FromDisplayNameFormatTemplate: template} 497 + defer func() { setting.MailService = nil }() 498 + 499 + tests := []struct { 500 + userDisplayName string 501 + fromDisplayName string 502 + }{{ 503 + userDisplayName: "test", 504 + fromDisplayName: "test", 505 + }, { 506 + userDisplayName: "Hi Its <Mee>", 507 + fromDisplayName: "Hi Its <Mee>", 508 + }, { 509 + userDisplayName: "Æsir", 510 + fromDisplayName: "=?utf-8?q?=C3=86sir?=", 511 + }, { 512 + userDisplayName: "new😀user", 513 + fromDisplayName: "=?utf-8?q?new=F0=9F=98=80user?=", 514 + }} 515 + 516 + for _, tc := range tests { 517 + t.Run(tc.userDisplayName, func(t *testing.T) { 518 + user := &user_model.User{FullName: tc.userDisplayName, Name: "tmp"} 519 + got := fromDisplayName(user) 520 + assert.EqualValues(t, tc.fromDisplayName, got) 521 + }) 522 + } 523 + 524 + t.Run("template with all available vars", func(t *testing.T) { 525 + template, err = texttmpl.New("mailFrom").Parse("{{ .DisplayName }} (by {{ .AppName }} on [{{ .Domain }}])") 526 + assert.NoError(t, err) 527 + setting.MailService = &setting.Mailer{FromDisplayNameFormatTemplate: template} 528 + oldAppName := setting.AppName 529 + setting.AppName = "Code IT" 530 + oldDomain := setting.Domain 531 + setting.Domain = "code.it" 532 + defer func() { 533 + setting.AppName = oldAppName 534 + setting.Domain = oldDomain 535 + }() 536 + 537 + assert.EqualValues(t, "Mister X (by Code IT on [code.it])", fromDisplayName(&user_model.User{FullName: "Mister X", Name: "tmp"})) 538 + }) 539 + }