diff --git a/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseOffer.text.hbs b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseOffer.text.hbs deleted file mode 100644 index a6bd7bb7e2..0000000000 --- a/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseOffer.text.hbs +++ /dev/null @@ -1,5 +0,0 @@ -{{#>BasicTextLayout}} -{{OrganizationName}} has offered to sponsor a family organization for you with Bitwarden. To redeem please click the following link: - -{{Url}} -{{/BasicTextLayout}} \ No newline at end of file diff --git a/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseOffer.html.hbs b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseOfferExistingAccount.html.hbs similarity index 69% rename from src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseOffer.html.hbs rename to src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseOfferExistingAccount.html.hbs index d233381b98..153b6dd5b3 100644 --- a/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseOffer.html.hbs +++ b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseOfferExistingAccount.html.hbs @@ -2,15 +2,20 @@ + + +
- {{OrganizationName}} has offered to sponsor a family organization for you with Bitwarden. + A Bitwarden account, {{SponsorEmail}}, has sponsored a free Families subscription for you! To activate your complimentary subscription, click the link below.
- Redeem + Accept Sponsorship
+ If you do not recognize this account, please ignore this message. +
-{{/FullHtmlLayout}} \ No newline at end of file +{{/FullHtmlLayout}} diff --git a/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseOfferExistingAccount.text.hbs b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseOfferExistingAccount.text.hbs new file mode 100644 index 0000000000..f11b315963 --- /dev/null +++ b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseOfferExistingAccount.text.hbs @@ -0,0 +1,5 @@ +{{#>BasicTextLayout}} +A Bitwarden account, {{SponsorEmail}}, has sponsored a free Families subscription for you! To activate your complimentary subscription, click the link below. + +{{Url}} +{{/BasicTextLayout}} diff --git a/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseOfferNewAccount.html.hbs b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseOfferNewAccount.html.hbs new file mode 100644 index 0000000000..6af1f9d712 --- /dev/null +++ b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseOfferNewAccount.html.hbs @@ -0,0 +1,21 @@ +{{#>FullHtmlLayout}} + + + + + + + + + + +
+ A Bitwarden account, {{SponsorEmail}}, has sponsored a free Families subscription for you! To activate your complimentary subscription, you will need to create an account. Click the link below. +
+ + Accept Sponsorship + +
+ If you do not recognize this account, please ignore this message. +
+{{/FullHtmlLayout}} diff --git a/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseOfferNewAccount.text.hbs b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseOfferNewAccount.text.hbs new file mode 100644 index 0000000000..e8c3dfcb35 --- /dev/null +++ b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseOfferNewAccount.text.hbs @@ -0,0 +1,5 @@ +{{#>BasicTextLayout}} +A Bitwarden account, {{SponsorEmail}}, has sponsored a free Families subscription for you! To activate your complimentary subscription, you will need to create an account. Click the link below. + +{{Url}} +{{/BasicTextLayout}} diff --git a/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseRedeemedToFamilyUser.html.hbs b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseRedeemedToFamilyUser.html.hbs index 346982ab29..c44cc1dbbf 100644 --- a/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseRedeemedToFamilyUser.html.hbs +++ b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseRedeemedToFamilyUser.html.hbs @@ -2,8 +2,8 @@
- You have redeemed a family organization sponsorship from {{OrganizationName}}. + Your Families subscription has been successfully activated. Your subscription is free as long as the sponsoring member continues to have a qualifying Bitwarden subscription.
-{{/FullHtmlLayout}} \ No newline at end of file +{{/FullHtmlLayout}} diff --git a/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseRedeemedToFamilyUser.text.hbs b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseRedeemedToFamilyUser.text.hbs index 994c883586..96da5b17f4 100644 --- a/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseRedeemedToFamilyUser.text.hbs +++ b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseRedeemedToFamilyUser.text.hbs @@ -1,3 +1,3 @@ {{#>BasicTextLayout}} -You have redeemed a family organization sponsorship from {{OrganizationName}}. -{{/BasicTextLayout}} \ No newline at end of file +Your Families subscription has been successfully activated. Your subscription is free as long as the sponsoring member continues to have a qualifying Bitwarden subscription. +{{/BasicTextLayout}} diff --git a/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseRedeemedToOrgUser.html.hbs b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseRedeemedToOrgUser.html.hbs index f2615c3740..06b9d85656 100644 --- a/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseRedeemedToOrgUser.html.hbs +++ b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseRedeemedToOrgUser.html.hbs @@ -2,8 +2,8 @@
- You have redeemed a Families for Enterprise sponsorship from {{OrganizationName}}. + Your Families subscription has been successfully redeemed.
-{{/FullHtmlLayout}} \ No newline at end of file +{{/FullHtmlLayout}} diff --git a/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseRedeemedToOrgUser.text.hbs b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseRedeemedToOrgUser.text.hbs index 8dd8e05ef2..d02063ea3e 100644 --- a/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseRedeemedToOrgUser.text.hbs +++ b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseRedeemedToOrgUser.text.hbs @@ -1,3 +1,3 @@ {{#>BasicTextLayout}} -A user in your organization has redeemed a family invitation. -{{/BasicTextLayout}} \ No newline at end of file +Your Families subscription has been successfully redeemed. +{{/BasicTextLayout}} diff --git a/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseSponsorshipEnding.html.hbs b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseSponsorshipEnding.html.hbs index b5d45565a9..a3a6b6db7a 100644 --- a/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseSponsorshipEnding.html.hbs +++ b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseSponsorshipEnding.html.hbs @@ -2,8 +2,8 @@
- Your Families for Enterprise sponsorship has ended and you will lose premium access at the end of the current billing cycle. + Your Families subscription is no longer sponsored and your subscription will expire in {{DaysLeft}} days. Please make sure you have a valid payment method to ensure there's no disruption to your account. Payment method can be updated under Settings → Subscription under your Families Organization.
-{{/FullHtmlLayout}} \ No newline at end of file +{{/FullHtmlLayout}} diff --git a/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseSponsorshipEnding.text.hbs b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseSponsorshipEnding.text.hbs index a2c98521f8..25202e61d2 100644 --- a/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseSponsorshipEnding.text.hbs +++ b/src/Core/MailTemplates/Handlebars/FamiliesForEnterprise/FamiliesForEnterpriseSponsorshipEnding.text.hbs @@ -1,3 +1,3 @@ {{#>BasicTextLayout}} -Your Families for Enterprise sponsorship has ended and you will lose premium access at the end of the current billing cycle. -{{/BasicTextLayout}} \ No newline at end of file +Your Families subscription is no longer sponsored and your subscription will expire in {{DaysLeft}} days. Please make sure you have a valid payment method to ensure there's no disruption to your account. Payment method can be updated under Settings → Subscription under your Families Organization. +{{/BasicTextLayout}} diff --git a/src/Core/Models/Mail/FamiliesForEnterprise/FamiliesForEnterpriseOfferViewModel.cs b/src/Core/Models/Mail/FamiliesForEnterprise/FamiliesForEnterpriseOfferViewModel.cs index 4c8e41c2a4..19be7e145b 100644 --- a/src/Core/Models/Mail/FamiliesForEnterprise/FamiliesForEnterpriseOfferViewModel.cs +++ b/src/Core/Models/Mail/FamiliesForEnterprise/FamiliesForEnterpriseOfferViewModel.cs @@ -2,6 +2,7 @@ namespace Bit.Core.Models.Mail.FamiliesForEnterprise { public class FamiliesForEnterpriseOfferViewModel : BaseMailModel { + public string SponsorEmail { get; set; } public string SponsorshipToken { get; set; } public string Url => $"{WebVaultUrl}/sponsored/families-for-enterprise?token={SponsorshipToken}"; } diff --git a/src/Core/Models/Mail/FamiliesForEnterprise/FamiliesForEnterpriseRedeemedToOrgUserViewModel.cs b/src/Core/Models/Mail/FamiliesForEnterprise/FamiliesForEnterpriseRedeemedToOrgUserViewModel.cs index 53ca9e38f9..3d1bf33713 100644 --- a/src/Core/Models/Mail/FamiliesForEnterprise/FamiliesForEnterpriseRedeemedToOrgUserViewModel.cs +++ b/src/Core/Models/Mail/FamiliesForEnterprise/FamiliesForEnterpriseRedeemedToOrgUserViewModel.cs @@ -2,6 +2,6 @@ namespace Bit.Core.Models.Mail.FamiliesForEnterprise { public class FamiliesForEnterpriseRedeemedToOrgUserViewModel : BaseMailModel { - public string OrganizationName { get; set; } + } } diff --git a/src/Core/Models/Mail/FamiliesForEnterprise/FamiliesForEnterpriseSponsorshipEndingViewModel.cs b/src/Core/Models/Mail/FamiliesForEnterprise/FamiliesForEnterpriseSponsorshipEndingViewModel.cs index e6568562a1..02cf27d4ed 100644 --- a/src/Core/Models/Mail/FamiliesForEnterprise/FamiliesForEnterpriseSponsorshipEndingViewModel.cs +++ b/src/Core/Models/Mail/FamiliesForEnterprise/FamiliesForEnterpriseSponsorshipEndingViewModel.cs @@ -2,6 +2,6 @@ namespace Bit.Core.Models.Mail.FamiliesForEnterprise { public class FamiliesForEnterpriseSponsorshipEndingViewModel : BaseMailModel { - + public int DaysLeft { get; set; } } } diff --git a/src/Core/Services/IMailService.cs b/src/Core/Services/IMailService.cs index 25004f6c4a..88b716f499 100644 --- a/src/Core/Services/IMailService.cs +++ b/src/Core/Services/IMailService.cs @@ -49,12 +49,11 @@ namespace Bit.Core.Services Task SendProviderConfirmedEmailAsync(string providerName, string email); Task SendProviderUserRemoved(string providerName, string email); Task SendUpdatedTempPasswordEmailAsync(string email, string userName); - // TODO: Change signature to hold data needed for email - Task SendFamiliesForEnterpriseOfferEmailAsync(string email, string organizationName, string token); - Task SendFamiliesForEnterpriseRedeemedEmailsAsync(string familyUserEmail, string sponsorEmail, string sponsorOrgName); + Task SendFamiliesForEnterpriseOfferEmailAsync(string email, string sponsorEmail, bool existingAccount, string token); + Task SendFamiliesForEnterpriseRedeemedEmailsAsync(string familyUserEmail, string sponsorEmail); Task SendFamiliesForEnterpriseReconfirmationRequiredEmailAsync(string email); Task SendFamiliesForEnterpriseSponsorshipRevertingEmailAsync(string email, string familyOrgName); - Task SendFamiliesForEnterpriseSponsorshipEndingEmailAsync(string email); + Task SendFamiliesForEnterpriseSponsorshipEndingEmailAsync(string email, DateTime sponsorshipEndDate); Task SendOTPEmailAsync(string email, string token); } } diff --git a/src/Core/Services/Implementations/HandlebarsMailService.cs b/src/Core/Services/Implementations/HandlebarsMailService.cs index 06c9a1670a..39a2786570 100644 --- a/src/Core/Services/Implementations/HandlebarsMailService.cs +++ b/src/Core/Services/Implementations/HandlebarsMailService.cs @@ -757,35 +757,39 @@ namespace Bit.Core.Services await _mailDeliveryService.SendEmailAsync(message); } - public async Task SendFamiliesForEnterpriseOfferEmailAsync(string email, string organizationName, string token) + public async Task SendFamiliesForEnterpriseOfferEmailAsync(string email, string sponsorEmail, bool existingAccount, string token) { - var message = CreateDefaultMessage("Free Bitwarden Family Plan Offer", email); + var message = CreateDefaultMessage("Finish Activation - Your Free Families Subscription", email); var model = new FamiliesForEnterpriseOfferViewModel { + SponsorEmail = sponsorEmail, WebVaultUrl = _globalSettings.BaseServiceUri.VaultWithHash, SiteName = _globalSettings.SiteName, SponsorshipToken = token, }; - await AddMessageContentAsync(message, "FamiliesForEnterprise.FamiliesForEnterpriseOffer", model); + await AddMessageContentAsync(message, existingAccount + ? "FamiliesForEnterprise.FamiliesForEnterpriseOfferExistingAccount" + : "FamiliesForEnterprise.FamiliesForEnterpriseOfferNewAccount", model); + message.Category = "FamiliesForEnterpriseOffer"; await _mailDeliveryService.SendEmailAsync(message); } - public async Task SendFamiliesForEnterpriseRedeemedEmailsAsync(string familyUserEmail, string sponsorEmail, string sponsorOrgName) + public async Task SendFamiliesForEnterpriseRedeemedEmailsAsync(string familyUserEmail, string sponsorEmail) { // Email family user await SendFamiliesForEnterpriseInviteRedeemedToFamilyUserEmailAsync(familyUserEmail); // Email enterprise org user - await SendFamiliesForEnterpriseInviteRedeemedToOrgUserEmailAsync(sponsorEmail, sponsorOrgName); + await SendFamiliesForEnterpriseInviteRedeemedToOrgUserEmailAsync(sponsorEmail); } private async Task SendFamiliesForEnterpriseInviteRedeemedToFamilyUserEmailAsync(string email) { // TODO: Complete emails - var message = CreateDefaultMessage("You Have Redeemed A Family Organization Sponsorship", email); + var message = CreateDefaultMessage("Success! Families Subscription Activated", email); var model = new FamiliesForEnterpriseRedeemedToFamilyUserViewModel { @@ -795,13 +799,13 @@ namespace Bit.Core.Services await _mailDeliveryService.SendEmailAsync(message); } - private async Task SendFamiliesForEnterpriseInviteRedeemedToOrgUserEmailAsync(string email, string organizationName) + private async Task SendFamiliesForEnterpriseInviteRedeemedToOrgUserEmailAsync(string email) { // TODO: Complete emails - var message = CreateDefaultMessage("A User Has Redeemeed Your Sponsorship", email); + var message = CreateDefaultMessage("Success! Families Subscription Activated", email); var model = new FamiliesForEnterpriseRedeemedToOrgUserViewModel { - OrganizationName = CoreHelpers.SanitizeForEmail(organizationName, false), + }; await AddMessageContentAsync(message, "FamiliesForEnterprise.FamiliesForEnterpriseRedeemedToOrgUser", model); message.Category = "FamilyForEnterpriseRedeemedToOrgUser"; @@ -814,7 +818,7 @@ namespace Bit.Core.Services var message = CreateDefaultMessage("Your Sponsorship Requires Reconfirmation", email); var model = new FamiliesForEnterpriseReconfirmationRequiredViewModel { - + }; await AddMessageContentAsync(message, "FamiliesForEnterprise.FamiliesForEnterpriseReconfirmationRequired", model); message.Category = "FamiliesForEnterpriseReconfirmationRequired"; @@ -823,7 +827,6 @@ namespace Bit.Core.Services public async Task SendFamiliesForEnterpriseSponsorshipRevertingEmailAsync(string email, string familyOrgName) { - // TODO: Complete emails var message = CreateDefaultMessage($"{familyOrgName} Organization Sponsorship Is No Longer Valid", email); var model = new FamiliesForEnterpriseSponsorshipRevertingViewModel { @@ -834,13 +837,14 @@ namespace Bit.Core.Services await _mailDeliveryService.SendEmailAsync(message); } - public async Task SendFamiliesForEnterpriseSponsorshipEndingEmailAsync(string email) + public async Task SendFamiliesForEnterpriseSponsorshipEndingEmailAsync(string email, DateTime sponsorshipEndDate) { - // TODO: Complete emails - var message = CreateDefaultMessage("A Family Organization Sponsorship Is Ending", email); + var endsInTime = DateTime.UtcNow - sponsorshipEndDate; + + var message = CreateDefaultMessage("Action Required: Renew Families Subscription", email); var model = new FamiliesForEnterpriseSponsorshipEndingViewModel { - + DaysLeft = (int)endsInTime.TotalDays, }; await AddMessageContentAsync(message, "FamiliesForEnterprise.FamiliesForEnterpriseSponsorshipEnding", model); message.Category = "FamiliesForEnterpriseSponsorshipEnding"; diff --git a/src/Core/Services/Implementations/OrganizationSponsorshipService.cs b/src/Core/Services/Implementations/OrganizationSponsorshipService.cs index b3ddc7a400..7c2f83772d 100644 --- a/src/Core/Services/Implementations/OrganizationSponsorshipService.cs +++ b/src/Core/Services/Implementations/OrganizationSponsorshipService.cs @@ -15,6 +15,7 @@ namespace Bit.Core.Services private readonly IOrganizationSponsorshipRepository _organizationSponsorshipRepository; private readonly IOrganizationRepository _organizationRepository; + private readonly IUserRepository _userRepository; private readonly IPaymentService _paymentService; private readonly IMailService _mailService; @@ -22,12 +23,14 @@ namespace Bit.Core.Services public OrganizationSponsorshipService(IOrganizationSponsorshipRepository organizationSponsorshipRepository, IOrganizationRepository organizationRepository, + IUserRepository userRepository, IPaymentService paymentService, IMailService mailService, IDataProtectionProvider dataProtectionProvider) { _organizationSponsorshipRepository = organizationSponsorshipRepository; _organizationRepository = organizationRepository; + _userRepository = userRepository; _paymentService = paymentService; _mailService = mailService; _dataProtector = dataProtectionProvider.CreateProtector("OrganizationSponsorshipServiceDataProtector"); @@ -105,8 +108,11 @@ namespace Bit.Core.Services public async Task SendSponsorshipOfferAsync(Organization sponsoringOrg, OrganizationSponsorship sponsorship) { + var user = await _userRepository.GetByEmailAsync(sponsorship.OfferedToEmail); + var isExistingAccount = user != null; + await _mailService.SendFamiliesForEnterpriseOfferEmailAsync(sponsorship.OfferedToEmail, sponsoringOrg.Name, - RedemptionToken(sponsorship.Id, sponsorship.PlanSponsorshipType.Value)); + isExistingAccount, RedemptionToken(sponsorship.Id, sponsorship.PlanSponsorshipType.Value)); } public async Task SetUpSponsorshipAsync(OrganizationSponsorship sponsorship, Organization sponsoredOrganization) diff --git a/src/Core/Services/NoopImplementations/NoopMailService.cs b/src/Core/Services/NoopImplementations/NoopMailService.cs index a778ce2d99..c5445fefa6 100644 --- a/src/Core/Services/NoopImplementations/NoopMailService.cs +++ b/src/Core/Services/NoopImplementations/NoopMailService.cs @@ -201,12 +201,12 @@ namespace Bit.Core.Services return Task.FromResult(0); } - public Task SendFamiliesForEnterpriseOfferEmailAsync(string email, string organizationName, string token) + public Task SendFamiliesForEnterpriseOfferEmailAsync(string email, string sponsorEmail, bool existingAccount, string token) { return Task.FromResult(0); } - public Task SendFamiliesForEnterpriseRedeemedEmailsAsync(string familyUserEmail, string sponsorEmail, string sponsorOrgName) + public Task SendFamiliesForEnterpriseRedeemedEmailsAsync(string familyUserEmail, string sponsorEmail) { return Task.FromResult(0); } @@ -221,7 +221,7 @@ namespace Bit.Core.Services return Task.FromResult(0); } - public Task SendFamiliesForEnterpriseSponsorshipEndingEmailAsync(string email) + public Task SendFamiliesForEnterpriseSponsorshipEndingEmailAsync(string email, DateTime sponsorshipEndDate) { return Task.FromResult(0); } diff --git a/test/Core.Test/Services/HandlebarsMailServiceTests.cs b/test/Core.Test/Services/HandlebarsMailServiceTests.cs index 64b1da1930..bed0517730 100644 --- a/test/Core.Test/Services/HandlebarsMailServiceTests.cs +++ b/test/Core.Test/Services/HandlebarsMailServiceTests.cs @@ -35,7 +35,7 @@ namespace Bit.Core.Test.Services ); } - [Fact(Skip = "Only for local development")] + [Fact] public async Task SendAllEmails() { // This test is only opt in and is more for development purposes. @@ -112,6 +112,9 @@ namespace Bit.Core.Test.Services ProviderId = Guid.NewGuid(), Id = Guid.NewGuid(), }}, + { ("familyUserEmail", typeof(string)), "test@bitwarden.com" }, + { ("sponsorEmail", typeof(string)), "test@bitwarden.com" }, + { ("familyOrgName", typeof(string)), "Test Org Name" }, }; var globalSettings = new GlobalSettings