From 285eb3ce931ee870f44fb25b86b85ce6092909b8 Mon Sep 17 00:00:00 2001 From: Qaisyousuf Date: Tue, 5 Aug 2025 14:22:48 +0200 Subject: [PATCH] Update email services and improve email template in online survey project Replaced the email service implementation and enhanced the email template design. The main email address for sending online surveys is now survey@asurvey.dk. --- Services/EmailSend/EmailToSend.cs | 16 +- Services/Implemnetation/EmailServices.cs | 55 ++- .../Controllers/QuestionnaireController.cs | 367 +++++++++++++++--- Web/Areas/Admin/Views/Footer/Edit.cshtml | 2 +- Web/Extesions/ServicesExtesions.cs | 4 - Web/Views/Shared/_Layout.cshtml | 1 + Web/Web.csproj | 2 +- Web/appsettings.json | 6 +- Web/wwwroot/Images/LogoForEmail.png | Bin 0 -> 14096 bytes Web/wwwroot/css/site.css | 178 +++++++++ 10 files changed, 535 insertions(+), 96 deletions(-) create mode 100644 Web/wwwroot/Images/LogoForEmail.png diff --git a/Services/EmailSend/EmailToSend.cs b/Services/EmailSend/EmailToSend.cs index 67050e3..8371606 100644 --- a/Services/EmailSend/EmailToSend.cs +++ b/Services/EmailSend/EmailToSend.cs @@ -8,15 +8,19 @@ namespace Services.EmailSend { public class EmailToSend { - public EmailToSend(string to, string subject, string body) + public string To { get; set; } + public string Subject { get; set; } + public string HtmlBody { get; set; } + public Dictionary Headers { get; set; } + + public EmailToSend(string to, string subject, string htmlBody) { To = to; Subject = subject; - Body = body; + HtmlBody = htmlBody; + Headers = new Dictionary(); // optional unless needed } - - public string To { get; set; } - public string Subject { get; set; } - public string Body { get; set; } } + + } diff --git a/Services/Implemnetation/EmailServices.cs b/Services/Implemnetation/EmailServices.cs index 0e1fcc9..7d06c7e 100644 --- a/Services/Implemnetation/EmailServices.cs +++ b/Services/Implemnetation/EmailServices.cs @@ -17,37 +17,50 @@ namespace Services.Implemnetation { _configuration = configuration; } + public async Task SendConfirmationEmailAsync(EmailToSend emailSend) { + var apiKey = _configuration["MailJet:ApiKey"]; + var secretKey = _configuration["MailJet:SecretKey"]; + var fromEmail = _configuration["Email:From"]; + var fromName = _configuration["Email:ApplicationName"]; + var client = new MailjetClient(apiKey, secretKey); - - MailjetClient client = new MailjetClient(_configuration["MailJet:ApiKey"], _configuration["MailJet:SecretKey"]); - - var email = new TransactionalEmailBuilder() - .WithFrom(new SendContact(_configuration["Email:From"], _configuration["Email:ApplicationName"])) - .WithSubject(emailSend.Subject) - .WithHtmlPart(emailSend.Body) - .WithTo(new SendContact(emailSend.To)) - .Build(); - - - var response = await client.SendTransactionalEmailAsync(email); - - if (response.Messages != null) + var message = new JObject { - if (response.Messages[0].Status == "success") + ["From"] = new JObject { - return true; + ["Email"] = fromEmail, + ["Name"] = fromName + }, + ["To"] = new JArray + { + new JObject + { + ["Email"] = emailSend.To, + ["Name"] = emailSend.To.Split('@')[0] } + }, + ["Subject"] = emailSend.Subject, + ["HTMLPart"] = emailSend.HtmlBody + }; + + // ✨ Add headers if any + if (emailSend.Headers != null && emailSend.Headers.Any()) + { + message["Headers"] = JObject.FromObject(emailSend.Headers); } + var request = new MailjetRequest + { + Resource = SendV31.Resource + } + .Property(Send.Messages, new JArray { message }); - return false; - - + var response = await client.PostAsync(request); + return response.IsSuccessStatusCode; } - - } + } diff --git a/Web/Areas/Admin/Controllers/QuestionnaireController.cs b/Web/Areas/Admin/Controllers/QuestionnaireController.cs index bfc5589..6a35625 100644 --- a/Web/Areas/Admin/Controllers/QuestionnaireController.cs +++ b/Web/Areas/Admin/Controllers/QuestionnaireController.cs @@ -464,80 +464,327 @@ namespace Web.Areas.Admin.Controllers } + [HttpPost] [HttpPost] public async Task SendQuestionnaire(SendQuestionnaireViewModel viewModel) { - if (ModelState.IsValid) + if (!ModelState.IsValid) + return View(viewModel); + + var questionnairePath = _configuration["Email:Questionnaire"]; + var subject = _questionnaire.GetQuesById(viewModel.Id)?.Title ?? "Survey Invitation"; + + var currentDateTime = viewModel.ExpirationDateTime ?? DateTime.Now; + string token = Guid.NewGuid().ToString(); + string tokenWithExpiry = $"{token}|{currentDateTime:yyyy-MM-ddTHH:mm:ssZ}"; + + var emailList = viewModel.Emails.Split(',', StringSplitOptions.RemoveEmptyEntries) + .Select(e => e.Trim()) + .ToList(); + + bool allEmailsSent = true; + + foreach (var email in emailList) { - var questionnairePath = _configuration["Email:Questionnaire"]; - DateTime currentDateTime = viewModel.ExpirationDateTime.HasValue ? viewModel.ExpirationDateTime.Value : DateTime.Now; - DateTime expiryDateTime = currentDateTime; // This line might need adjustment if you are actually setting an expiry. - string token = Guid.NewGuid().ToString(); - string tokenWithExpiry = $"{token}|{expiryDateTime.ToString("yyyy-MM-ddTHH:mm:ssZ")}"; - var emailList = viewModel.Emails.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries) - .Select(email => email.Trim()) - .ToList(); - var question = _questionnaire.GetQuesById(viewModel.Id); - var subject = question.Title; + string userName = FormatUserNameFromEmail(email); + string userEmailEncoded = HttpUtility.UrlEncode(email); + string completeUrl = $"{Request.Scheme}://{Request.Host}/{questionnairePath}/{viewModel.QuestionnaireId}?t={tokenWithExpiry}&E={userEmailEncoded}"; - bool allEmailsSent = true; - foreach (var email in emailList) + string emailBody = GenerateEmailBody(userName, subject, completeUrl, currentDateTime); + + var emailSend = new EmailToSend(email, subject, emailBody) { - var userName = email.Substring(0, email.IndexOf('@')); // This assumes the email is valid and contains an '@' - userName = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(userName.Replace(".", " ")); // Optional: Improve formatting, replace dots and capitalize names - var userEmailEncoded = HttpUtility.UrlEncode(email); - var completeUrl = $"{Request.Scheme}://{Request.Host}/{questionnairePath}/{viewModel.QuestionnaireId}?t={tokenWithExpiry}&E={userEmailEncoded}"; + Headers = new Dictionary + { + { "X-Priority", "1" }, + { "Importance", "High" } + } + }; - string emailBody = $@" - - - - - -
-

Hey {userName},

-
{subject}
-

Thank you for participating in our survey. Your feedback is valuable to us.

-

Please click the button below to start the survey:

-

The survey will expire: {expiryDateTime.ToLongDateString()} Time: {expiryDateTime.ToShortTimeString()}

-
-

Søren Eggert Lundsteen Olsen
- Seosoft ApS
-


- Hovedgaden 3 Jordrup
- Kolding 6064
- Denmark

-
- - "; + bool emailSent = await _emailServices.SendConfirmationEmailAsync(emailSend); - var emailSend = new EmailToSend(email, subject, emailBody); - bool emailSent = await _emailServices.SendConfirmationEmailAsync(emailSend); - if (!emailSent) - { - allEmailsSent = false; - ModelState.AddModelError(string.Empty, "Failed to send questionnaire via email to: " + email); - } - } - - - if (allEmailsSent) + if (!emailSent) { - TempData["Success"] = "Questionnaire sent successfully to all recipients."; - return RedirectToAction(nameof(Index)); + allEmailsSent = false; + ModelState.AddModelError(string.Empty, $"Failed to send questionnaire to: {email}"); } } - // If model state is not valid, return the view with validation errors + if (allEmailsSent) + { + TempData["Success"] = "Questionnaire sent successfully to all recipients."; + return RedirectToAction(nameof(Index)); + } + return View(viewModel); } + private string FormatUserNameFromEmail(string email) + { + var usernamePart = email.Split('@')[0]; + return CultureInfo.CurrentCulture.TextInfo.ToTitleCase(usernamePart.Replace('.', ' ')); + } + + + private static string GenerateEmailBody(string userName, string subject, string url, DateTime expiry) + { + var danishCulture = new CultureInfo("da-DK"); + string expiryDate = expiry.ToString("dd. MMMM yyyy", danishCulture); + string expiryTime = expiry.ToString("HH:mm", danishCulture); + + return $@" + + + + + + Invitation til undersøgelse + + + + + +"; + } diff --git a/Web/Areas/Admin/Views/Footer/Edit.cshtml b/Web/Areas/Admin/Views/Footer/Edit.cshtml index 072c5a0..2bfbdaa 100644 --- a/Web/Areas/Admin/Views/Footer/Edit.cshtml +++ b/Web/Areas/Admin/Views/Footer/Edit.cshtml @@ -12,7 +12,7 @@
-
Update banner
+
Update Footer
diff --git a/Web/Extesions/ServicesExtesions.cs b/Web/Extesions/ServicesExtesions.cs index 86d81d4..1d48782 100644 --- a/Web/Extesions/ServicesExtesions.cs +++ b/Web/Extesions/ServicesExtesions.cs @@ -1,14 +1,10 @@ using Data; -using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.EntityFrameworkCore; using Model; using OpenAI_API; using Services.Implemnetation; using Services.Interaces; -using System.Net; using Web.AIConfiguration; namespace Web.Extesions diff --git a/Web/Views/Shared/_Layout.cshtml b/Web/Views/Shared/_Layout.cshtml index 03fa353..6411918 100644 --- a/Web/Views/Shared/_Layout.cshtml +++ b/Web/Views/Shared/_Layout.cshtml @@ -8,6 +8,7 @@ + diff --git a/Web/Web.csproj b/Web/Web.csproj index 81009b7..bcb22c3 100644 --- a/Web/Web.csproj +++ b/Web/Web.csproj @@ -13,7 +13,7 @@ - + diff --git a/Web/appsettings.json b/Web/appsettings.json index 755616b..0e77ab4 100644 --- a/Web/appsettings.json +++ b/Web/appsettings.json @@ -17,7 +17,7 @@ // "SurveyVista": "data source=SQL1003.site4now.net; Initial Catalog=db_ab8a17_vistasurvey;User Id=db_ab8a17_vistasurvey_admin,Password=1!QaisYousuf;integrated security=True; TrustServerCertificate=True;" //}, "Email": { - "From": "mr.qais.yousuf@gmail.com", + "From": "survey@asurvey.dk", "ApplicationName": "Online Survey", "ConfirmEmailPath": "Subscription/Confirmation", "unsubscribePath": "Subscription/UnsubscribeConfirmation", @@ -25,8 +25,8 @@ }, "MailJet": { - "ApiKey": "f545eee3a4743464b9d25fb9c5ab3f6c", - "SecretKey": "8df3cf0337a090b1d6301f312ca51413" + "ApiKey": "f06e28f892a81377545360662d8fe250", + "SecretKey": "244883216ed68f83d2d4107bc53c6484" } } diff --git a/Web/wwwroot/Images/LogoForEmail.png b/Web/wwwroot/Images/LogoForEmail.png new file mode 100644 index 0000000000000000000000000000000000000000..2182bf7ca57599aded6e4d6908c111e68ed97833 GIT binary patch literal 14096 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 U!S(rmI7(PaPF=PH`u5}h0dv