[PM-28769] [PM-28768] [PM-28772] Welcome email bug fixes (#6644)

Fix: fix bugs reported by QA for Welcome emails
* test: add test for new plan type in welcome email

* fix: change to headStyle so styling is only included once

* fix: update MJML templates to have correct copy text

* chore: move build artifacts for updated email templates

* fix: add setting for SMTP to SSO project

* fix: update component css styling

* chore: rebuild hbs templates

* fix: using billing extension method to fetch Correct PlanType.
This commit is contained in:
Ike 2025-12-05 11:35:37 -05:00 committed by GitHub
parent 5469d8be0e
commit d5f39eac91
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 131 additions and 106 deletions

View File

@ -25,6 +25,12 @@
"connectionString": "UseDevelopmentStorage=true" "connectionString": "UseDevelopmentStorage=true"
}, },
"developmentDirectory": "../../../dev", "developmentDirectory": "../../../dev",
"pricingUri": "https://billingpricing.qa.bitwarden.pw" "pricingUri": "https://billingpricing.qa.bitwarden.pw",
"mail": {
"smtp": {
"host": "localhost",
"port": 10250
}
}
} }
} }

View File

@ -13,7 +13,11 @@
"mail": { "mail": {
"sendGridApiKey": "SECRET", "sendGridApiKey": "SECRET",
"amazonConfigSetName": "Email", "amazonConfigSetName": "Email",
"replyToEmail": "no-reply@bitwarden.com" "replyToEmail": "no-reply@bitwarden.com",
"smtp": {
"host": "localhost",
"port": 10250
}
}, },
"identityServer": { "identityServer": {
"certificateThumbprint": "SECRET" "certificateThumbprint": "SECRET"

View File

@ -5,6 +5,7 @@ using Bit.Core.Auth.Enums;
using Bit.Core.Auth.Models; using Bit.Core.Auth.Models;
using Bit.Core.Auth.Models.Business.Tokenables; using Bit.Core.Auth.Models.Business.Tokenables;
using Bit.Core.Billing.Enums; using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Extensions;
using Bit.Core.Entities; using Bit.Core.Entities;
using Bit.Core.Exceptions; using Bit.Core.Exceptions;
using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces; using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces;
@ -455,9 +456,7 @@ public class RegisterUserCommand : IRegisterUserCommand
else if (!string.IsNullOrEmpty(organization.DisplayName())) else if (!string.IsNullOrEmpty(organization.DisplayName()))
{ {
// If the organization is Free or Families plan, send families welcome email // If the organization is Free or Families plan, send families welcome email
if (organization.PlanType is PlanType.FamiliesAnnually if (organization.PlanType.GetProductTier() is ProductTierType.Free or ProductTierType.Families)
or PlanType.FamiliesAnnually2019
or PlanType.Free)
{ {
await _mailService.SendFreeOrgOrFamilyOrgUserWelcomeEmailAsync(user, organization.DisplayName()); await _mailService.SendFreeOrgOrFamilyOrgUserWelcomeEmailAsync(user, organization.DisplayName());
} }

View File

@ -53,11 +53,37 @@
<style type="text/css"> <style type="text/css">
@media only screen and (max-width:480px) {
.mj-bw-hero-responsive-img {
display: none !important;
}
}
@media only screen and (max-width:480px) {
.mj-bw-learn-more-footer-responsive-img {
display: none !important;
}
}
@media only screen and (max-width:479px) { @media only screen and (max-width:479px) {
table.mj-full-width-mobile { width: 100% !important; } table.mj-full-width-mobile { width: 100% !important; }
td.mj-full-width-mobile { width: auto !important; } td.mj-full-width-mobile { width: auto !important; }
} }
@media only screen and (max-width:480px) {
.mj-bw-icon-row-text {
padding-left: 5px !important;
line-height: 20px;
}
.mj-bw-icon-row {
padding: 10px 15px;
width: fit-content !important;
}
}
</style> </style>
<style type="text/css"> <style type="text/css">
@ -67,29 +93,8 @@
.border-fix > table > tbody > tr > td { .border-fix > table > tbody > tr > td {
border-radius: 3px; border-radius: 3px;
} }
@media only screen and (max-width: 480px) {
.hide-small-img {
display: none !important;
}
.send-bubble {
padding-left: 20px;
padding-right: 20px;
width: 90% !important;
}
}
@media only screen and (max-width: 480px) {
.mj-bw-icon-row-text {
padding-left: 5px !important;
line-height: 20px;
}
.mj-bw-icon-row {
padding: 10px 15px;
width: fit-content !important;
}
}
</style> </style>
<!-- Responsive icon visibility -->
<!-- Responsive styling for mj-bw-icon-row -->
</head> </head>
<body style="word-spacing:normal;background-color:#e6e9ef;"> <body style="word-spacing:normal;background-color:#e6e9ef;">
@ -156,7 +161,7 @@
</h1> </h1>
<mj-text color="#fff" padding-top="0" padding-bottom="0"> <mj-text color="#fff" padding-top="0" padding-bottom="0">
<h2 style="font-weight: normal; font-size: 16px; line-height: 0px"> <h2 style="font-weight: normal; font-size: 16px; line-height: 0px">
Let's get set up to autofill. Lets get you set up to autofill.
</h2> </h2>
</mj-text></div> </mj-text></div>
@ -176,7 +181,7 @@
<tbody> <tbody>
<tr> <tr>
<td align="center" class="hide-small-img" style="font-size:0px;padding:0px;word-break:break-word;"> <td align="center" class="mj-bw-hero-responsive-img" style="font-size:0px;padding:0px;word-break:break-word;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;"> <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;">
<tbody> <tbody>
@ -256,7 +261,7 @@
<tr> <tr>
<td align="left" style="font-size:0px;padding:10px 15px;word-break:break-word;"> <td align="left" style="font-size:0px;padding:10px 15px;word-break:break-word;">
<div style="font-family:'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:16px;line-height:24px;text-align:left;color:#1B2029;">A <b>{{OrganizationName}}</b> administrator will approve you <div style="font-family:'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:16px;line-height:24px;text-align:left;color:#1B2029;">An administrator from <b>{{OrganizationName}}</b> will approve you
before you can share passwords. While you wait for approval, get before you can share passwords. While you wait for approval, get
started with Bitwarden Password Manager:</div> started with Bitwarden Password Manager:</div>
@ -622,10 +627,10 @@
<td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;"> <td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
<div style="font-family:'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:16px;line-height:24px;text-align:left;color:#1B2029;"><p style="font-size: 18px; line-height: 28px; font-weight: bold;"> <div style="font-family:'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:16px;line-height:24px;text-align:left;color:#1B2029;"><p style="font-size: 18px; line-height: 28px; font-weight: bold;">
Learn more about Bitwarden Learn more about Bitwarden
</p> </p>
Find user guides, product documentation, and videos on the Find user guides, product documentation, and videos on the
<a href="https://bitwarden.com/help/" class="link" style="text-decoration: none; color: #175ddc; font-weight: 600;"> Bitwarden Help Center</a>.</div> <a href="https://bitwarden.com/help/" class="link" style="text-decoration: none; color: #175ddc; font-weight: 600;"> Bitwarden Help Center</a>.</div>
</td> </td>
</tr> </tr>
@ -643,7 +648,7 @@
<tbody> <tbody>
<tr> <tr>
<td align="center" class="hide-small-img" style="font-size:0px;padding:10px 25px;word-break:break-word;"> <td align="center" class="mj-bw-learn-more-footer-responsive-img" style="font-size:0px;padding:10px 25px;word-break:break-word;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;"> <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;">
<tbody> <tbody>

View File

@ -53,11 +53,37 @@
<style type="text/css"> <style type="text/css">
@media only screen and (max-width:480px) {
.mj-bw-hero-responsive-img {
display: none !important;
}
}
@media only screen and (max-width:480px) {
.mj-bw-learn-more-footer-responsive-img {
display: none !important;
}
}
@media only screen and (max-width:479px) { @media only screen and (max-width:479px) {
table.mj-full-width-mobile { width: 100% !important; } table.mj-full-width-mobile { width: 100% !important; }
td.mj-full-width-mobile { width: auto !important; } td.mj-full-width-mobile { width: auto !important; }
} }
@media only screen and (max-width:480px) {
.mj-bw-icon-row-text {
padding-left: 5px !important;
line-height: 20px;
}
.mj-bw-icon-row {
padding: 10px 15px;
width: fit-content !important;
}
}
</style> </style>
<style type="text/css"> <style type="text/css">
@ -67,29 +93,8 @@
.border-fix > table > tbody > tr > td { .border-fix > table > tbody > tr > td {
border-radius: 3px; border-radius: 3px;
} }
@media only screen and (max-width: 480px) {
.hide-small-img {
display: none !important;
}
.send-bubble {
padding-left: 20px;
padding-right: 20px;
width: 90% !important;
}
}
@media only screen and (max-width: 480px) {
.mj-bw-icon-row-text {
padding-left: 5px !important;
line-height: 20px;
}
.mj-bw-icon-row {
padding: 10px 15px;
width: fit-content !important;
}
}
</style> </style>
<!-- Responsive icon visibility -->
<!-- Responsive styling for mj-bw-icon-row -->
</head> </head>
<body style="word-spacing:normal;background-color:#e6e9ef;"> <body style="word-spacing:normal;background-color:#e6e9ef;">
@ -156,7 +161,7 @@
</h1> </h1>
<mj-text color="#fff" padding-top="0" padding-bottom="0"> <mj-text color="#fff" padding-top="0" padding-bottom="0">
<h2 style="font-weight: normal; font-size: 16px; line-height: 0px"> <h2 style="font-weight: normal; font-size: 16px; line-height: 0px">
Let's get set up to autofill. Lets get you set up to autofill.
</h2> </h2>
</mj-text></div> </mj-text></div>
@ -176,7 +181,7 @@
<tbody> <tbody>
<tr> <tr>
<td align="center" class="hide-small-img" style="font-size:0px;padding:0px;word-break:break-word;"> <td align="center" class="mj-bw-hero-responsive-img" style="font-size:0px;padding:0px;word-break:break-word;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;"> <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;">
<tbody> <tbody>
@ -621,10 +626,10 @@
<td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;"> <td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
<div style="font-family:'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:16px;line-height:24px;text-align:left;color:#1B2029;"><p style="font-size: 18px; line-height: 28px; font-weight: bold;"> <div style="font-family:'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:16px;line-height:24px;text-align:left;color:#1B2029;"><p style="font-size: 18px; line-height: 28px; font-weight: bold;">
Learn more about Bitwarden Learn more about Bitwarden
</p> </p>
Find user guides, product documentation, and videos on the Find user guides, product documentation, and videos on the
<a href="https://bitwarden.com/help/" class="link" style="text-decoration: none; color: #175ddc; font-weight: 600;"> Bitwarden Help Center</a>.</div> <a href="https://bitwarden.com/help/" class="link" style="text-decoration: none; color: #175ddc; font-weight: 600;"> Bitwarden Help Center</a>.</div>
</td> </td>
</tr> </tr>
@ -642,7 +647,7 @@
<tbody> <tbody>
<tr> <tr>
<td align="center" class="hide-small-img" style="font-size:0px;padding:10px 25px;word-break:break-word;"> <td align="center" class="mj-bw-learn-more-footer-responsive-img" style="font-size:0px;padding:10px 25px;word-break:break-word;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;"> <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;">
<tbody> <tbody>

View File

@ -53,11 +53,37 @@
<style type="text/css"> <style type="text/css">
@media only screen and (max-width:480px) {
.mj-bw-hero-responsive-img {
display: none !important;
}
}
@media only screen and (max-width:480px) {
.mj-bw-learn-more-footer-responsive-img {
display: none !important;
}
}
@media only screen and (max-width:479px) { @media only screen and (max-width:479px) {
table.mj-full-width-mobile { width: 100% !important; } table.mj-full-width-mobile { width: 100% !important; }
td.mj-full-width-mobile { width: auto !important; } td.mj-full-width-mobile { width: auto !important; }
} }
@media only screen and (max-width:480px) {
.mj-bw-icon-row-text {
padding-left: 5px !important;
line-height: 20px;
}
.mj-bw-icon-row {
padding: 10px 15px;
width: fit-content !important;
}
}
</style> </style>
<style type="text/css"> <style type="text/css">
@ -67,29 +93,8 @@
.border-fix > table > tbody > tr > td { .border-fix > table > tbody > tr > td {
border-radius: 3px; border-radius: 3px;
} }
@media only screen and (max-width: 480px) {
.hide-small-img {
display: none !important;
}
.send-bubble {
padding-left: 20px;
padding-right: 20px;
width: 90% !important;
}
}
@media only screen and (max-width: 480px) {
.mj-bw-icon-row-text {
padding-left: 5px !important;
line-height: 20px;
}
.mj-bw-icon-row {
padding: 10px 15px;
width: fit-content !important;
}
}
</style> </style>
<!-- Responsive icon visibility -->
<!-- Responsive styling for mj-bw-icon-row -->
</head> </head>
<body style="word-spacing:normal;background-color:#e6e9ef;"> <body style="word-spacing:normal;background-color:#e6e9ef;">
@ -156,7 +161,7 @@
</h1> </h1>
<mj-text color="#fff" padding-top="0" padding-bottom="0"> <mj-text color="#fff" padding-top="0" padding-bottom="0">
<h2 style="font-weight: normal; font-size: 16px; line-height: 0px"> <h2 style="font-weight: normal; font-size: 16px; line-height: 0px">
Let's get set up to autofill. Lets get you set up to autofill.
</h2> </h2>
</mj-text></div> </mj-text></div>
@ -176,7 +181,7 @@
<tbody> <tbody>
<tr> <tr>
<td align="center" class="hide-small-img" style="font-size:0px;padding:0px;word-break:break-word;"> <td align="center" class="mj-bw-hero-responsive-img" style="font-size:0px;padding:0px;word-break:break-word;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;"> <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;">
<tbody> <tbody>
@ -256,7 +261,7 @@
<tr> <tr>
<td align="left" style="font-size:0px;padding:10px 15px;word-break:break-word;"> <td align="left" style="font-size:0px;padding:10px 15px;word-break:break-word;">
<div style="font-family:'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:16px;line-height:24px;text-align:left;color:#1B2029;">A <b>{{OrganizationName}}</b> administrator will need to confirm <div style="font-family:'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:16px;line-height:24px;text-align:left;color:#1B2029;">An administrator from <b>{{OrganizationName}}</b> will need to confirm
you before you can share passwords. Get started with Bitwarden you before you can share passwords. Get started with Bitwarden
Password Manager:</div> Password Manager:</div>
@ -622,10 +627,10 @@
<td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;"> <td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
<div style="font-family:'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:16px;line-height:24px;text-align:left;color:#1B2029;"><p style="font-size: 18px; line-height: 28px; font-weight: bold;"> <div style="font-family:'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:16px;line-height:24px;text-align:left;color:#1B2029;"><p style="font-size: 18px; line-height: 28px; font-weight: bold;">
Learn more about Bitwarden Learn more about Bitwarden
</p> </p>
Find user guides, product documentation, and videos on the Find user guides, product documentation, and videos on the
<a href="https://bitwarden.com/help/" class="link" style="text-decoration: none; color: #175ddc; font-weight: 600;"> Bitwarden Help Center</a>.</div> <a href="https://bitwarden.com/help/" class="link" style="text-decoration: none; color: #175ddc; font-weight: 600;"> Bitwarden Help Center</a>.</div>
</td> </td>
</tr> </tr>
@ -643,7 +648,7 @@
<tbody> <tbody>
<tr> <tr>
<td align="center" class="hide-small-img" style="font-size:0px;padding:10px 25px;word-break:break-word;"> <td align="center" class="mj-bw-learn-more-footer-responsive-img" style="font-size:0px;padding:10px 25px;word-break:break-word;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;"> <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;">
<tbody> <tbody>

View File

@ -18,16 +18,16 @@ class MjBwIconRow extends BodyComponent {
static defaultAttributes = {}; static defaultAttributes = {};
componentHeadStyle = (breakpoint) => { headStyle = (breakpoint) => {
return ` return `
@media only screen and (max-width:${breakpoint}): { @media only screen and (max-width:${breakpoint}) {
".mj-bw-icon-row-text": { .mj-bw-icon-row-text {
padding-left: "5px !important", padding-left: 5px !important;
line-height: "20px", line-height: 20px;
}, }
".mj-bw-icon-row": { .mj-bw-icon-row {
padding: "10px 15px", padding: 10px 15px;
width: "fit-content !important", width: fit-content !important;
} }
} }
`; `;

View File

@ -9,7 +9,7 @@
<mj-bw-hero <mj-bw-hero
img-src="https://assets.bitwarden.com/email/v1/account-fill.png" img-src="https://assets.bitwarden.com/email/v1/account-fill.png"
title="Welcome to Bitwarden!" title="Welcome to Bitwarden!"
sub-title="Let's get set up to autofill." sub-title="Lets get you set up to autofill."
/> />
</mj-wrapper> </mj-wrapper>

View File

@ -9,7 +9,7 @@
<mj-bw-hero <mj-bw-hero
img-src="https://assets.bitwarden.com/email/v1/account-fill.png" img-src="https://assets.bitwarden.com/email/v1/account-fill.png"
title="Welcome to Bitwarden!" title="Welcome to Bitwarden!"
sub-title="Let's get set up to autofill." sub-title="Lets get you set up to autofill."
/> />
</mj-wrapper> </mj-wrapper>

View File

@ -9,7 +9,7 @@
<mj-bw-hero <mj-bw-hero
img-src="https://assets.bitwarden.com/email/v1/account-fill.png" img-src="https://assets.bitwarden.com/email/v1/account-fill.png"
title="Welcome to Bitwarden!" title="Welcome to Bitwarden!"
sub-title="Let's get set up to autofill." sub-title="Lets get you set up to autofill."
/> />
</mj-wrapper> </mj-wrapper>

View File

@ -1017,6 +1017,7 @@ public class RegisterUserCommandTests
[Theory] [Theory]
[BitAutoData(PlanType.FamiliesAnnually)] [BitAutoData(PlanType.FamiliesAnnually)]
[BitAutoData(PlanType.FamiliesAnnually2019)] [BitAutoData(PlanType.FamiliesAnnually2019)]
[BitAutoData(PlanType.FamiliesAnnually2025)]
[BitAutoData(PlanType.Free)] [BitAutoData(PlanType.Free)]
public async Task SendWelcomeEmail_FamilyOrg_SendsFamilyWelcomeEmail( public async Task SendWelcomeEmail_FamilyOrg_SendsFamilyWelcomeEmail(
PlanType planType, PlanType planType,