From 5acd6f0b23061c7912ec92cb69ba21f50c72f9be Mon Sep 17 00:00:00 2001 From: Patrick <135162612+plavoie-BC@users.noreply.github.com> Date: Thu, 26 Jun 2025 08:04:21 -0700 Subject: [PATCH 01/14] AB#29051 - Add CC and BCC support to email notification system --- .../EmailNotificationService.cs | 49 +++++++++++++++---- .../IEmailNotificationService.cs | 8 +-- .../Events/EmailNotificationEvent.cs | 6 ++- .../Events/EmailNotificationHandler.cs | 26 +++++++--- .../Emails/CreateEmailDto.cs | 5 ++ .../Emails/EmailAppService.cs | 4 ++ .../Components/EmailsWidget/Default.cshtml | 10 ++++ .../Shared/Components/EmailsWidget/Default.js | 12 ++++- .../EmailsWidget/EmailsWidgetViewComponent.cs | 2 +- .../EmailsWidget/EmailsWidgetViewModel.cs | 8 +++ 10 files changed, 104 insertions(+), 26 deletions(-) diff --git a/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Application/EmailNotificaions/EmailNotificationService.cs b/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Application/EmailNotificaions/EmailNotificationService.cs index e7687324f..80854e579 100644 --- a/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Application/EmailNotificaions/EmailNotificationService.cs +++ b/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Application/EmailNotificaions/EmailNotificationService.cs @@ -98,14 +98,14 @@ public async Task DeleteEmail(Guid id) await _emailLogsRepository.DeleteAsync(id); } - public async Task UpdateEmailLog(Guid emailId, string emailTo, string body, string subject, Guid applicationId, string? emailFrom, string? status,string? emailTemplateName) + public async Task UpdateEmailLog(Guid emailId, string emailTo, string body, string subject, Guid applicationId, string? emailFrom, string? status, string? emailTemplateName, string? emailCC = null, string? emailBCC = null) { if (string.IsNullOrEmpty(emailTo)) { return null; } - var emailObject = await GetEmailObjectAsync(emailTo, body, subject, emailFrom, "html", emailTemplateName); + var emailObject = await GetEmailObjectAsync(emailTo, body, subject, emailFrom, "html", emailTemplateName, emailCC, emailBCC); EmailLog emailLog = await _emailLogsRepository.GetAsync(emailId); emailLog = UpdateMappedEmailLog(emailLog, emailObject); emailLog.ApplicationId = applicationId; @@ -117,19 +117,19 @@ public async Task DeleteEmail(Guid id) return loggedEmail; } - public async Task InitializeEmailLog(string emailTo, string body, string subject, Guid applicationId, string? emailFrom, string? emailTemplateName) + public async Task InitializeEmailLog(string emailTo, string body, string subject, Guid applicationId, string? emailFrom, string? emailTemplateName, string? emailCC = null, string? emailBCC = null) { - return await InitializeEmailLog(emailTo, body, subject, applicationId, emailFrom, EmailStatus.Initialized, emailTemplateName); + return await InitializeEmailLog(emailTo, body, subject, applicationId, emailFrom, EmailStatus.Initialized, emailTemplateName, emailCC, emailBCC); } [RemoteService(false)] - public async Task InitializeEmailLog(string emailTo, string body, string subject, Guid applicationId, string? emailFrom, string? status, string? emailTemplateName) + public async Task InitializeEmailLog(string emailTo, string body, string subject, Guid applicationId, string? emailFrom, string? status, string? emailTemplateName, string? emailCC = null, string? emailBCC = null) { if (string.IsNullOrEmpty(emailTo)) { return null; } - var emailObject = await GetEmailObjectAsync(emailTo, body, subject, emailFrom, "html", emailTemplateName); + var emailObject = await GetEmailObjectAsync(emailTo, body, subject, emailFrom, "html", emailTemplateName, emailCC, emailBCC); EmailLog emailLog = new EmailLog(); emailLog = UpdateMappedEmailLog(emailLog, emailObject); emailLog.ApplicationId = applicationId; @@ -234,9 +234,12 @@ public async Task SendCommentNotification(EmailCommentDto i /// The body of the email /// Subject Message /// From Email Address - /// Type of body email: html or text + /// Type of body email: html or text + /// Template name for the email + /// CC email addresses + /// BCC email addresses /// HttpResponseMessage indicating the result of the operation - public async Task SendEmailNotification(string emailTo, string body, string subject, string? emailFrom, string? emailBodyType, string? emailTemplateName) + public async Task SendEmailNotification(string emailTo, string body, string subject, string? emailFrom, string? emailBodyType, string? emailTemplateName, string? emailCC = null, string? emailBCC = null) { try { @@ -250,7 +253,7 @@ public async Task SendEmailNotification(string emailTo, str } // Send the email using the CHES client service - var emailObject = await GetEmailObjectAsync(emailTo, body, subject, emailFrom, emailBodyType, emailTemplateName); + var emailObject = await GetEmailObjectAsync(emailTo, body, subject, emailFrom, emailBodyType, emailTemplateName, emailCC, emailBCC); var response = await _chesClientService.SendAsync(emailObject); // Assuming SendAsync returns a HttpResponseMessage or equivalent: @@ -328,7 +331,7 @@ public async Task SendEmailToQueue(EmailLog emailLog) await _emailQueueService.SendToEmailEventQueueAsync(emailNotificationEvent); } - protected virtual async Task GetEmailObjectAsync(string emailTo, string body, string subject, string? emailFrom, string? emailBodyType, string? emailTemplateName) + protected virtual async Task GetEmailObjectAsync(string emailTo, string body, string subject, string? emailFrom, string? emailBodyType, string? emailTemplateName, string? emailCC = null, string? emailBCC = null) { List toList = new(); string[] emails = emailTo.Split([',', ';'], StringSplitOptions.RemoveEmptyEntries); @@ -338,12 +341,36 @@ protected virtual async Task GetEmailObjectAsync(string emailTo, string toList.Add(email.Trim()); } + List? ccList = null; + if (!string.IsNullOrWhiteSpace(emailCC)) + { + ccList = new List(); + string[] ccEmails = emailCC.Split([',', ';'], StringSplitOptions.RemoveEmptyEntries); + foreach (string email in ccEmails) + { + ccList.Add(email.Trim()); + } + } + + List? bccList = null; + if (!string.IsNullOrWhiteSpace(emailBCC)) + { + bccList = new List(); + string[] bccEmails = emailBCC.Split([',', ';'], StringSplitOptions.RemoveEmptyEntries); + foreach (string email in bccEmails) + { + bccList.Add(email.Trim()); + } + } + var defaultFromAddress = await SettingProvider.GetOrNullAsync(NotificationsSettings.Mailing.DefaultFromAddress); var emailObject = new { body, bodyType = emailBodyType ?? "text", + cc = ccList, + bcc = bccList, encoding = "utf-8", from = emailFrom ?? defaultFromAddress ?? "NoReply@gov.bc.ca", priority = "normal", @@ -362,6 +389,8 @@ protected virtual EmailLog UpdateMappedEmailLog(EmailLog emailLog, dynamic email emailLog.BodyType = emailDynamicObject.bodyType; emailLog.FromAddress = emailDynamicObject.from; emailLog.ToAddress = String.Join(",", emailDynamicObject.to); + emailLog.CC = emailDynamicObject.cc != null ? String.Join(",", emailDynamicObject.cc) : ""; + emailLog.BCC = emailDynamicObject.bcc != null ? String.Join(",", emailDynamicObject.bcc) : ""; emailLog.TemplateName = emailDynamicObject.templateName; return emailLog; } diff --git a/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Application/EmailNotificaions/IEmailNotificationService.cs b/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Application/EmailNotificaions/IEmailNotificationService.cs index a4304c06c..9813b44f7 100644 --- a/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Application/EmailNotificaions/IEmailNotificationService.cs +++ b/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Application/EmailNotificaions/IEmailNotificationService.cs @@ -10,12 +10,12 @@ namespace Unity.Notifications.EmailNotifications { public interface IEmailNotificationService : IApplicationService { - Task UpdateEmailLog(Guid emailId, string emailTo, string body, string subject, Guid applicationId, string? emailFrom, string? status, string? emailTemplateName); - Task InitializeEmailLog(string emailTo, string body, string subject, Guid applicationId, string? emailFrom, string? status, string? emailTemplateName); - Task InitializeEmailLog(string emailTo, string body, string subject, Guid applicationId, string? emailFrom, string? emailTemplateName); + Task UpdateEmailLog(Guid emailId, string emailTo, string body, string subject, Guid applicationId, string? emailFrom, string? status, string? emailTemplateName, string? emailCC = null, string? emailBCC = null); + Task InitializeEmailLog(string emailTo, string body, string subject, Guid applicationId, string? emailFrom, string? status, string? emailTemplateName, string? emailCC = null, string? emailBCC = null); + Task InitializeEmailLog(string emailTo, string body, string subject, Guid applicationId, string? emailFrom, string? emailTemplateName, string? emailCC = null, string? emailBCC = null); Task GetEmailLogById(Guid id); Task SendCommentNotification(EmailCommentDto input); - Task SendEmailNotification(string emailTo, string body, string subject, string? emailFrom, string? emailBodyType, string? emailTemplateName); + Task SendEmailNotification(string emailTo, string body, string subject, string? emailFrom, string? emailBodyType, string? emailTemplateName, string? emailCC = null, string? emailBCC = null); Task SendEmailToQueue(EmailLog emailLog); string GetApprovalBody(); string GetDeclineBody(); diff --git a/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Application/Events/EmailNotificationEvent.cs b/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Application/Events/EmailNotificationEvent.cs index a502e50a8..ff1ade61c 100644 --- a/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Application/Events/EmailNotificationEvent.cs +++ b/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Application/Events/EmailNotificationEvent.cs @@ -16,8 +16,10 @@ public class EmailNotificationEvent public string Subject { get; set; } = string.Empty; public string? EmailFrom { get; set; } = string.Empty; public string EmailAddress { get; set; } = string.Empty; - public List EmailAddressList { get; set; } = new List(); - + public List EmailAddressList { get; set; } = []; + public string? EmailCC { get; set; } = string.Empty; + public string? EmailBCC { get; set; } = string.Empty; + public IEnumerable Cc { get; set; } = []; [JsonConverter(typeof(JsonStringEnumConverter))] public EmailAction Action { get; set; } public string? EmailTemplateName { get; set; } = string.Empty; diff --git a/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Application/Events/EmailNotificationHandler.cs b/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Application/Events/EmailNotificationHandler.cs index f924ad56a..63afa3542 100644 --- a/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Application/Events/EmailNotificationHandler.cs +++ b/applications/Unity.GrantManager/modules/Unity.Notifications/src/Unity.Notifications.Application/Events/EmailNotificationHandler.cs @@ -25,7 +25,7 @@ public async Task HandleEventAsync(EmailNotificationEvent eventData) } } - private async Task InitializeAndSendEmailToQueue(string emailTo, string body, string subject, Guid applicationId, string? emailFrom, string? emailTemplateName) + private async Task InitializeAndSendEmailToQueue(string emailTo, string body, string subject, Guid applicationId, string? emailFrom, string? emailTemplateName, string? emailCC = null, string? emailBCC = null) { EmailLog emailLog = await InitializeEmail( emailTo, @@ -34,12 +34,14 @@ private async Task InitializeAndSendEmailToQueue(string emailTo, string body, st applicationId, emailFrom, EmailStatus.Initialized, - emailTemplateName); + emailTemplateName, + emailCC, + emailBCC); await emailNotificationService.SendEmailToQueue(emailLog); } - private async Task InitializeEmail(string emailTo, string body, string subject, Guid applicationId, string? emailFrom, string status, string? emailTemplateName) + private async Task InitializeEmail(string emailTo, string body, string subject, Guid applicationId, string? emailFrom, string status, string? emailTemplateName, string? emailCC = null, string? emailBCC = null) { EmailLog emailLog = await emailNotificationService.InitializeEmailLog( emailTo, @@ -48,7 +50,9 @@ private async Task InitializeEmail(string emailTo, string body, string applicationId, emailFrom, status, - emailTemplateName) ?? throw new UserFriendlyException("Unable to Initialize Email Log"); + emailTemplateName, + emailCC, + emailBCC) ?? throw new UserFriendlyException("Unable to Initialize Email Log"); return emailLog; } @@ -87,7 +91,7 @@ private async Task HandleSendCustomEmail(EmailNotificationEvent eventData) string emailToAddress = String.Join(",", eventData.EmailAddressList); if (eventData.Id == Guid.Empty) { - await InitializeAndSendEmailToQueue(emailToAddress, eventData.Body, eventData.Subject, eventData.ApplicationId, eventData.EmailFrom,eventData.EmailTemplateName); + await InitializeAndSendEmailToQueue(emailToAddress, eventData.Body, eventData.Subject, eventData.ApplicationId, eventData.EmailFrom, eventData.EmailTemplateName, eventData.EmailCC, eventData.EmailBCC); } else { @@ -99,7 +103,9 @@ private async Task HandleSendCustomEmail(EmailNotificationEvent eventData) eventData.ApplicationId, eventData.EmailFrom, EmailStatus.Initialized, - eventData.EmailTemplateName); + eventData.EmailTemplateName, + eventData.EmailCC, + eventData.EmailBCC); if (emailLog != null) { @@ -129,7 +135,9 @@ await emailNotificationService.UpdateEmailLog( eventData.ApplicationId, eventData.EmailFrom, EmailStatus.Draft, - eventData.EmailTemplateName); + eventData.EmailTemplateName, + eventData.EmailCC, + eventData.EmailBCC); } else { @@ -140,7 +148,9 @@ await InitializeEmail( eventData.ApplicationId, eventData.EmailFrom, EmailStatus.Draft, - eventData.EmailTemplateName); + eventData.EmailTemplateName, + eventData.EmailCC, + eventData.EmailBCC); } } diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/Emails/CreateEmailDto.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/Emails/CreateEmailDto.cs index f6ddffba4..8433ffe51 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/Emails/CreateEmailDto.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/Emails/CreateEmailDto.cs @@ -18,6 +18,11 @@ public class CreateEmailDto [Required] public string EmailBody { get; set; } = string.Empty; + + public string? EmailCC { get; set; } + + public string? EmailBCC { get; set; } + public Guid ApplicationId { get; set; } public Guid OwnerId { get; set; } public Guid EmailId { get; set; } = Guid.Empty; diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Emails/EmailAppService.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Emails/EmailAppService.cs index db050d151..ab8cb7189 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Emails/EmailAppService.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Emails/EmailAppService.cs @@ -36,6 +36,8 @@ private static EmailNotificationEvent GetEmailNotificationEvent(CreateEmailDto d List toList = []; string[] emails = dto.EmailTo.Split([',', ';'], StringSplitOptions.RemoveEmptyEntries); + dto.Bcc = dto.EmailBCC.Split(";").Select(e => e?.Trim()).Concat(email.Bcc?.Any() ?? false ? email.Bcc : []).NotNullOrWhiteSpace(); + foreach (string email in emails) { toList.Add(email.Trim()); @@ -49,6 +51,8 @@ private static EmailNotificationEvent GetEmailNotificationEvent(CreateEmailDto d EmailAddress = dto.EmailTo, EmailAddressList = toList, EmailFrom = dto.EmailFrom, + EmailCC = dto.EmailCC, + EmailBCC = dto.EmailBCC, Subject = dto.EmailSubject, Body = dto.EmailBody, EmailTemplateName = dto.EmailTemplateName diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/EmailsWidget/Default.cshtml b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/EmailsWidget/Default.cshtml index 832884db3..665c4d83e 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/EmailsWidget/Default.cshtml +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/EmailsWidget/Default.cshtml @@ -90,6 +90,16 @@