Add per-email sender name for compliance page emails
When an email has a sender name set (the organization name), the mailer composes the From header as "OrgName via Probo" instead of the default global sender name. This gives compliance page recipients clearer context about which organization is contacting them. Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
@@ -31,6 +31,7 @@ type (
|
||||
ID gid.GID `db:"id"`
|
||||
RecipientEmail string `db:"recipient_email"`
|
||||
RecipientName string `db:"recipient_name"`
|
||||
SenderName *string `db:"sender_name"`
|
||||
ReplyTo *mail.Addr `db:"reply_to"`
|
||||
UnsubscribeURL *string `db:"unsubscribe_url"`
|
||||
MailingListUpdateID *gid.GID `db:"mailing_list_update_id"`
|
||||
@@ -51,6 +52,7 @@ type (
|
||||
Emails []*Email
|
||||
|
||||
EmailOptions struct {
|
||||
SenderName *string
|
||||
ReplyTo *mail.Addr
|
||||
UnsubscribeURL *string
|
||||
MailingListUpdateID *gid.GID
|
||||
@@ -91,6 +93,7 @@ func NewEmail(
|
||||
}
|
||||
|
||||
if opts != nil {
|
||||
e.SenderName = opts.SenderName
|
||||
e.ReplyTo = opts.ReplyTo
|
||||
e.UnsubscribeURL = opts.UnsubscribeURL
|
||||
e.MailingListUpdateID = opts.MailingListUpdateID
|
||||
@@ -108,7 +111,9 @@ INSERT INTO emails (
|
||||
id,
|
||||
recipient_email,
|
||||
recipient_name,
|
||||
reply_to, unsubscribe_url,
|
||||
sender_name,
|
||||
reply_to,
|
||||
unsubscribe_url,
|
||||
mailing_list_update_id,
|
||||
subject,
|
||||
text_body,
|
||||
@@ -123,6 +128,7 @@ VALUES (
|
||||
@id,
|
||||
@recipient_email,
|
||||
@recipient_name,
|
||||
@sender_name,
|
||||
@reply_to,
|
||||
@unsubscribe_url,
|
||||
@mailing_list_update_id,
|
||||
@@ -141,6 +147,7 @@ VALUES (
|
||||
"id": e.ID,
|
||||
"recipient_email": e.RecipientEmail,
|
||||
"recipient_name": e.RecipientName,
|
||||
"sender_name": e.SenderName,
|
||||
"reply_to": e.ReplyTo,
|
||||
"unsubscribe_url": e.UnsubscribeURL,
|
||||
"mailing_list_update_id": e.MailingListUpdateID,
|
||||
@@ -172,6 +179,7 @@ func (emails Emails) BulkInsert(
|
||||
e.ID,
|
||||
e.RecipientEmail,
|
||||
e.RecipientName,
|
||||
e.SenderName,
|
||||
e.ReplyTo,
|
||||
e.UnsubscribeURL,
|
||||
e.MailingListUpdateID,
|
||||
@@ -186,7 +194,7 @@ func (emails Emails) BulkInsert(
|
||||
_, err := conn.CopyFrom(
|
||||
ctx,
|
||||
pgx.Identifier{"emails"},
|
||||
[]string{"id", "recipient_email", "recipient_name", "reply_to", "unsubscribe_url", "mailing_list_update_id", "subject", "text_body", "html_body", "created_at", "updated_at"},
|
||||
[]string{"id", "recipient_email", "recipient_name", "sender_name", "reply_to", "unsubscribe_url", "mailing_list_update_id", "subject", "text_body", "html_body", "created_at", "updated_at"},
|
||||
pgx.CopyFromRows(rows),
|
||||
)
|
||||
return err
|
||||
@@ -198,7 +206,7 @@ func (e *Email) LoadNextPendingForUpdateSkipLocked(
|
||||
) error {
|
||||
q := `
|
||||
SELECT
|
||||
id, recipient_email, recipient_name, reply_to, unsubscribe_url, mailing_list_update_id, subject, text_body, html_body,
|
||||
id, recipient_email, recipient_name, sender_name, reply_to, unsubscribe_url, mailing_list_update_id, subject, text_body, html_body,
|
||||
status, processing_started_at, attempt_count, max_attempts,
|
||||
last_attempted_at, last_error, created_at, updated_at, sent_at
|
||||
FROM emails
|
||||
|
||||
1
pkg/coredata/migrations/20260322T120000Z.sql
Normal file
1
pkg/coredata/migrations/20260322T120000Z.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TABLE emails ADD COLUMN sender_name text;
|
||||
@@ -238,8 +238,9 @@ func (w *CompletionCertificateWorker) generateCertificate(
|
||||
scope coredata.Scoper,
|
||||
) (*coredata.Email, coredata.EmailAttachments, error) {
|
||||
var (
|
||||
events = coredata.ElectronicSignatureEvents{}
|
||||
signedFile = coredata.File{}
|
||||
events = coredata.ElectronicSignatureEvents{}
|
||||
signedFile = coredata.File{}
|
||||
organization = coredata.Organization{}
|
||||
)
|
||||
|
||||
if err := w.pg.WithConn(
|
||||
@@ -253,6 +254,10 @@ func (w *CompletionCertificateWorker) generateCertificate(
|
||||
return fmt.Errorf("cannot load signed file: %w", err)
|
||||
}
|
||||
|
||||
if err := organization.LoadByID(ctx, conn, scope, signature.OrganizationID); err != nil {
|
||||
return fmt.Errorf("cannot load organization: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
); err != nil {
|
||||
@@ -322,7 +327,9 @@ func (w *CompletionCertificateWorker) generateCertificate(
|
||||
subject,
|
||||
textBody,
|
||||
htmlBody,
|
||||
nil,
|
||||
&coredata.EmailOptions{
|
||||
SenderName: new(organization.Name),
|
||||
},
|
||||
)
|
||||
|
||||
attachments := coredata.EmailAttachments{
|
||||
|
||||
@@ -583,13 +583,20 @@ func (s AuthService) SendMagicLink(ctx context.Context, req *SendMagicLinkReques
|
||||
return fmt.Errorf("cannot render magic link email: %w", err)
|
||||
}
|
||||
|
||||
var emailOpts *coredata.EmailOptions
|
||||
if req.CompliancePageID != nil {
|
||||
emailOpts = &coredata.EmailOptions{
|
||||
SenderName: new(organization.Name),
|
||||
}
|
||||
}
|
||||
|
||||
magicLinkEmail := coredata.NewEmail(
|
||||
fullName,
|
||||
req.Email,
|
||||
subject,
|
||||
textBody,
|
||||
htmlBody,
|
||||
nil,
|
||||
emailOpts,
|
||||
)
|
||||
|
||||
if err := magicLinkEmail.Insert(ctx, tx); err != nil {
|
||||
|
||||
@@ -202,9 +202,14 @@ func (w *SendingWorker) sendAndCommit(
|
||||
return fmt.Errorf("cannot load email attachments: %w", err)
|
||||
}
|
||||
|
||||
fromName := w.senderName
|
||||
if email.SenderName != nil {
|
||||
fromName = *email.SenderName + " via " + w.senderName
|
||||
}
|
||||
|
||||
mail := enmime.Builder().
|
||||
Subject(email.Subject).
|
||||
From(w.senderName, w.senderEmail).
|
||||
From(fromName, w.senderEmail).
|
||||
To(email.RecipientName, email.RecipientEmail).
|
||||
Text([]byte(email.TextBody))
|
||||
|
||||
|
||||
@@ -708,6 +708,7 @@ func (s *Service) CreateUpdateEmails(
|
||||
textBody,
|
||||
htmlBody,
|
||||
&coredata.EmailOptions{
|
||||
SenderName: new(orgName),
|
||||
ReplyTo: replyTo,
|
||||
UnsubscribeURL: &unsubscribeURL,
|
||||
MailingListUpdateID: &mailingListUpdateID,
|
||||
@@ -752,7 +753,18 @@ func (s *Service) buildConfirmationMail(
|
||||
return nil, fmt.Errorf("cannot render subscription confirmation email: %w", err)
|
||||
}
|
||||
|
||||
return coredata.NewEmail(fullName, email, subject, textBody, htmlBody, &coredata.EmailOptions{ReplyTo: replyTo, UnsubscribeURL: &unsubscribeURL}), nil
|
||||
return coredata.NewEmail(
|
||||
fullName,
|
||||
email,
|
||||
subject,
|
||||
textBody,
|
||||
htmlBody,
|
||||
&coredata.EmailOptions{
|
||||
SenderName: new(orgName),
|
||||
ReplyTo: replyTo,
|
||||
UnsubscribeURL: &unsubscribeURL,
|
||||
},
|
||||
), nil
|
||||
}
|
||||
|
||||
func (s *Service) buildUnsubscriptionMail(
|
||||
@@ -772,7 +784,17 @@ func (s *Service) buildUnsubscriptionMail(
|
||||
return nil, fmt.Errorf("cannot render unsubscription email: %w", err)
|
||||
}
|
||||
|
||||
return coredata.NewEmail(fullName, email, subject, textBody, htmlBody, &coredata.EmailOptions{ReplyTo: replyTo}), nil
|
||||
return coredata.NewEmail(
|
||||
fullName,
|
||||
email,
|
||||
subject,
|
||||
textBody,
|
||||
htmlBody,
|
||||
&coredata.EmailOptions{
|
||||
SenderName: new(orgName),
|
||||
ReplyTo: replyTo,
|
||||
},
|
||||
), nil
|
||||
}
|
||||
|
||||
func (s *Service) buildUnsubscribeURL(mailingListID gid.GID, email mail.Addr) (string, error) {
|
||||
|
||||
@@ -693,7 +693,9 @@ func (s *DocumentService) SendSigningNotifications(
|
||||
subject,
|
||||
textBody,
|
||||
htmlBody,
|
||||
nil,
|
||||
&coredata.EmailOptions{
|
||||
SenderName: new(organization.Name),
|
||||
},
|
||||
)
|
||||
|
||||
if err := email.Insert(ctx, tx); err != nil {
|
||||
|
||||
@@ -366,7 +366,9 @@ func (s TrustCenterAccessService) sendAccessEmail(ctx context.Context, tx pg.Con
|
||||
subject,
|
||||
textBody,
|
||||
htmlBody,
|
||||
nil,
|
||||
&coredata.EmailOptions{
|
||||
SenderName: new(organization.Name),
|
||||
},
|
||||
)
|
||||
|
||||
if err := accessEmail.Insert(ctx, tx); err != nil {
|
||||
|
||||
@@ -450,7 +450,9 @@ func (s *TrustCenterAccessService) sendAccessEmail(ctx context.Context, tx pg.Co
|
||||
subject,
|
||||
textBody,
|
||||
htmlBody,
|
||||
nil,
|
||||
&coredata.EmailOptions{
|
||||
SenderName: new(organization.Name),
|
||||
},
|
||||
)
|
||||
|
||||
if err := accessEmail.Insert(ctx, tx); err != nil {
|
||||
@@ -585,7 +587,9 @@ func (s *TrustCenterAccessService) sendDocumentAccessRejectedEmail(
|
||||
subject,
|
||||
textBody,
|
||||
htmlBody,
|
||||
nil,
|
||||
&coredata.EmailOptions{
|
||||
SenderName: new(organization.Name),
|
||||
},
|
||||
)
|
||||
|
||||
if err := accessEmail.Insert(ctx, tx); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user