1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-01 08:02:49 -05:00

[PM-1095][PM-1104] Update email template (#2746)

* [SG-994] Add import Open Sans font to full template

* [SG-994] Update organization user invite email template to new UI

* [SG-994] update alt text for mobile app download buttons

* [SG-994] Update copy. Add hyperlinks to stores.

* [SG-944] Improve layout responsiveness

* [PM-1095][PM-1104] Add new template for title and contact us. Add new template for user organization invite

* [PM-1095][PM-1104] Remove wrong text from free invite

* [PM-1104][PM-1095] Add bold class. Add margin.

* [PM-1104][PM-1095] Change font type to previously used

* [PM-1104][PM-1095] Remove Open Sans font

* [PM-1104][PM-1095] Improve browsers rendering compatibility

* [PM-1104][PM-1095] Fixed margins

* [PM-1095][PM-1104] Remove unnecessary string sanitise.
This commit is contained in:
André Bispo
2023-03-21 14:44:58 +00:00
committed by GitHub
parent 3d0ca908ff
commit 2e3e96a25c
15 changed files with 232 additions and 123 deletions

View File

@ -15,8 +15,8 @@ public interface IMailService
Task SendTwoFactorEmailAsync(string email, string token);
Task SendNoMasterPasswordHintEmailAsync(string email);
Task SendMasterPasswordHintEmailAsync(string email, string hint);
Task SendOrganizationInviteEmailAsync(string organizationName, OrganizationUser orgUser, ExpiringToken token);
Task BulkSendOrganizationInviteEmailAsync(string organizationName, IEnumerable<(OrganizationUser orgUser, ExpiringToken token)> invites);
Task SendOrganizationInviteEmailAsync(string organizationName, OrganizationUser orgUser, ExpiringToken token, bool isFreeOrg);
Task BulkSendOrganizationInviteEmailAsync(string organizationName, IEnumerable<(OrganizationUser orgUser, ExpiringToken token)> invites, bool isFreeOrg);
Task SendOrganizationMaxSeatLimitReachedEmailAsync(Organization organization, int maxSeatCount, IEnumerable<string> ownerEmails);
Task SendOrganizationAutoscaledEmailAsync(Organization organization, int initialSeatCount, IEnumerable<string> ownerEmails);
Task SendOrganizationAcceptedEmailAsync(Organization organization, string userIdentifier, IEnumerable<string> adminEmails);

View File

@ -191,6 +191,9 @@ public class HandlebarsMailService : IMailService
var message = CreateDefaultMessage($"You Have Been Confirmed To {organizationName}", email);
var model = new OrganizationUserConfirmedViewModel
{
TitleFirst = "You're confirmed as a member of ",
TitleSecondBold = CoreHelpers.SanitizeForEmail(organizationName, false),
TitleThird = "!",
OrganizationName = CoreHelpers.SanitizeForEmail(organizationName, false),
WebVaultUrl = _globalSettings.BaseServiceUri.VaultWithHash,
SiteName = _globalSettings.SiteName
@ -200,21 +203,24 @@ public class HandlebarsMailService : IMailService
await _mailDeliveryService.SendEmailAsync(message);
}
public Task SendOrganizationInviteEmailAsync(string organizationName, OrganizationUser orgUser, ExpiringToken token) =>
BulkSendOrganizationInviteEmailAsync(organizationName, new[] { (orgUser, token) });
public Task SendOrganizationInviteEmailAsync(string organizationName, OrganizationUser orgUser, ExpiringToken token, bool isFreeOrg) =>
BulkSendOrganizationInviteEmailAsync(organizationName, new[] { (orgUser, token) }, isFreeOrg);
public async Task BulkSendOrganizationInviteEmailAsync(string organizationName, IEnumerable<(OrganizationUser orgUser, ExpiringToken token)> invites)
public async Task BulkSendOrganizationInviteEmailAsync(string organizationName, IEnumerable<(OrganizationUser orgUser, ExpiringToken token)> invites, bool isFreeOrg)
{
MailQueueMessage CreateMessage(string email, object model)
{
var message = CreateDefaultMessage($"Join {organizationName}", email);
return new MailQueueMessage(message, "OrganizationUserInvited", model);
}
var freeOrgTitle = "A Bitwarden member invited you to an organization. Join now to start securing your passwords!";
var messageModels = invites.Select(invite => CreateMessage(invite.orgUser.Email,
new OrganizationUserInvitedViewModel
{
OrganizationName = CoreHelpers.SanitizeForEmail(organizationName, false),
TitleFirst = isFreeOrg ? freeOrgTitle : "Join ",
TitleSecondBold = isFreeOrg ? string.Empty : CoreHelpers.SanitizeForEmail(organizationName, false),
TitleThird = isFreeOrg ? string.Empty : " on Bitwarden and start securing your passwords!",
OrganizationName = CoreHelpers.SanitizeForEmail(organizationName, false) + invite.orgUser.Status,
Email = WebUtility.UrlEncode(invite.orgUser.Email),
OrganizationId = invite.orgUser.OrganizationId.ToString(),
OrganizationUserId = invite.orgUser.Id.ToString(),
@ -478,6 +484,10 @@ public class HandlebarsMailService : IMailService
Handlebars.RegisterTemplate("FullHtmlLayout", fullHtmlLayoutSource);
var fullTextLayoutSource = await ReadSourceAsync("Layouts.Full.text");
Handlebars.RegisterTemplate("FullTextLayout", fullTextLayoutSource);
var titleContactUsHtmlLayoutSource = await ReadSourceAsync("Layouts.TitleContactUs.html");
Handlebars.RegisterTemplate("TitleContactUsHtmlLayout", titleContactUsHtmlLayoutSource);
var titleContactUsTextLayoutSource = await ReadSourceAsync("Layouts.TitleContactUs.text");
Handlebars.RegisterTemplate("TitleContactUsTextLayout", titleContactUsTextLayoutSource);
Handlebars.RegisterHelper("date", (writer, context, parameters) =>
{

View File

@ -1193,7 +1193,7 @@ public class OrganizationService : IOrganizationService
_dataProtector.Protect($"OrganizationUserInvite {orgUser.Id} {orgUser.Email} {CoreHelpers.ToEpocMilliseconds(DateTime.UtcNow)}");
await _mailService.BulkSendOrganizationInviteEmailAsync(organization.Name,
orgUsers.Select(o => (o, new ExpiringToken(MakeToken(o), DateTime.UtcNow.AddDays(5)))));
orgUsers.Select(o => (o, new ExpiringToken(MakeToken(o), DateTime.UtcNow.AddDays(5)))), organization.PlanType == PlanType.Free);
}
private async Task SendInviteAsync(OrganizationUser orgUser, Organization organization)
@ -1202,8 +1202,7 @@ public class OrganizationService : IOrganizationService
var nowMillis = CoreHelpers.ToEpocMilliseconds(now);
var token = _dataProtector.Protect(
$"OrganizationUserInvite {orgUser.Id} {orgUser.Email} {nowMillis}");
await _mailService.SendOrganizationInviteEmailAsync(organization.Name, orgUser, new ExpiringToken(token, now.AddDays(5)));
await _mailService.SendOrganizationInviteEmailAsync(organization.Name, orgUser, new ExpiringToken(token, now.AddDays(5)), organization.PlanType == PlanType.Free);
}
public async Task<OrganizationUser> AcceptUserAsync(Guid organizationUserId, User user, string token,

View File

@ -52,12 +52,12 @@ public class NoopMailService : IMailService
return Task.FromResult(0);
}
public Task SendOrganizationInviteEmailAsync(string organizationName, OrganizationUser orgUser, ExpiringToken token)
public Task SendOrganizationInviteEmailAsync(string organizationName, OrganizationUser orgUser, ExpiringToken token, bool isFreeOrg)
{
return Task.FromResult(0);
}
public Task BulkSendOrganizationInviteEmailAsync(string organizationName, IEnumerable<(OrganizationUser orgUser, ExpiringToken token)> invites)
public Task BulkSendOrganizationInviteEmailAsync(string organizationName, IEnumerable<(OrganizationUser orgUser, ExpiringToken token)> invites, bool isFreeOrg)
{
return Task.FromResult(0);
}