592 lines
23 KiB
C#
592 lines
23 KiB
C#
using Data;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.CodeAnalysis;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.Caching.Memory;
|
|
using Microsoft.VisualStudio.TextTemplating;
|
|
using Microsoft.VisualStudio.Web.CodeGenerators.Mvc.Templates.BlazorIdentity.Pages.Manage;
|
|
using Model;
|
|
using Newtonsoft.Json.Linq;
|
|
|
|
using Services.EmailSend;
|
|
using Services.Implemnetation;
|
|
using Services.Interaces;
|
|
using Web.AIConfiguration;
|
|
using Web.ViewModel.NewsLetterVM;
|
|
using iText.Kernel.Pdf;
|
|
using iText.Kernel.Pdf.Canvas.Parser;
|
|
using System.Text.RegularExpressions;
|
|
|
|
namespace Web.Areas.Admin.Controllers
|
|
{
|
|
|
|
|
|
public class NewslettersController : Controller
|
|
{
|
|
private readonly INewsLetterRepository _repository;
|
|
private readonly SurveyContext _context;
|
|
private readonly IEmailServices _emailServices;
|
|
private readonly IConfiguration _configuration;
|
|
private readonly IMemoryCache _memoryCache;
|
|
|
|
public NewslettersController(INewsLetterRepository repository,SurveyContext context,IEmailServices emailServices,IConfiguration configuration)
|
|
{
|
|
_repository = repository;
|
|
_context = context;
|
|
_emailServices = emailServices;
|
|
_configuration = configuration;
|
|
|
|
}
|
|
public IActionResult Index(int page = 1)
|
|
{
|
|
const int PageSize = 10;
|
|
|
|
var totalSubscribedUsers = _context.Subscriptions.Count(s => s.IsSubscribed);
|
|
ViewBag.TotalSubscribedUsers = totalSubscribedUsers;
|
|
|
|
var totalSubscriptions = _context.Subscriptions.Count();
|
|
var totalPages = (int)Math.Ceiling(totalSubscriptions / (double)PageSize);
|
|
|
|
var newsLetterFromdb = _repository.GetAll()
|
|
.OrderByDescending(x=>x.Id)
|
|
.Skip((page - 1) * PageSize)
|
|
.Take(PageSize)
|
|
.ToList();
|
|
|
|
var viewmodel = new List<NewsLetterViewModel>();
|
|
|
|
foreach (var item in newsLetterFromdb)
|
|
{
|
|
viewmodel.Add(new NewsLetterViewModel
|
|
{
|
|
Id = item.Id,
|
|
Name = item.Name,
|
|
Email = item.Email,
|
|
IsSubscribed = item.IsSubscribed
|
|
});
|
|
}
|
|
|
|
var listViewModel = new PaginationViewModel
|
|
{
|
|
Subscriptions = viewmodel,
|
|
CurrentPage = page,
|
|
TotalPages = totalPages
|
|
};
|
|
|
|
return View(listViewModel);
|
|
}
|
|
|
|
[Authorize(Roles = "Admin")]
|
|
public IActionResult Create()
|
|
{
|
|
var totalSubscribedUsers = _context.Subscriptions.Count(s => s.IsSubscribed);
|
|
|
|
// Pass the total count to the view
|
|
ViewBag.TotalSubscribedUsers = totalSubscribedUsers;
|
|
|
|
return View();
|
|
}
|
|
|
|
|
|
[HttpPost]
|
|
public IActionResult DeleteSelectedSubscription(List<int> selectedIds)
|
|
{
|
|
if (selectedIds != null && selectedIds.Any())
|
|
{
|
|
var subscriptions = _context.Subscriptions.Where(s => selectedIds.Contains(s.Id)).ToList();
|
|
_context.Subscriptions.RemoveRange(subscriptions);
|
|
_context.SaveChanges();
|
|
}
|
|
TempData["Success"] = "Subscriber deleted successfully";
|
|
return RedirectToAction(nameof(Index));
|
|
}
|
|
|
|
|
|
[Authorize(Roles = "Admin")]
|
|
[HttpPost]
|
|
public async Task<IActionResult> Create(SendNewsLetterViewModel viewModel)
|
|
{
|
|
if (ModelState.IsValid)
|
|
{
|
|
try
|
|
{
|
|
// Retrieve all subscribed users
|
|
var subscribedUsers = await _context.Subscriptions.Where(s => s.IsSubscribed).ToListAsync();
|
|
string confirmationPath = _configuration["Email:unsubscribePath"];
|
|
|
|
// Send the newsletter email to each subscribed user
|
|
foreach (var user in subscribedUsers)
|
|
{
|
|
string unsubscribeUrl = $"{Request.Scheme}://{Request.Host}/{confirmationPath}?email={user.Email}";
|
|
|
|
// This HTML version with proper line breaks works for primary inbox
|
|
string emailBody = $@"<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset=""UTF-8"">
|
|
</head>
|
|
<body>
|
|
|
|
<p>Hej {user.Name},</p>
|
|
|
|
<div>
|
|
{viewModel.Body.Replace("\n", "<br><br>")}
|
|
</div>
|
|
|
|
<p>Hvis du har spørgsmål, kan du kontakte os på kontakt@nvkn.dk</p>
|
|
|
|
<p>Med venlig hilsen,<br/>
|
|
Nærværskonsulenterne ApS</p>
|
|
|
|
<hr>
|
|
|
|
<p><small>
|
|
Nærværskonsulenterne ApS<br/>
|
|
Brødemosevej 24A, 3300 Frederiksværk<br/>
|
|
kontakt@nvkn.dk
|
|
</small></p>
|
|
|
|
<p>
|
|
<a href=""{unsubscribeUrl}"">
|
|
<button type=""button"">Afmeld nyhedsbrev</button>
|
|
</a>
|
|
</p>
|
|
|
|
</body>
|
|
</html>";
|
|
|
|
var email = new EmailToSend(user.Email, viewModel.Subject, emailBody);
|
|
var isSent = await _emailServices.SendConfirmationEmailAsync(email);
|
|
|
|
// Create a record for the sent email
|
|
var sentEmail = new SentNewsletterEamil
|
|
{
|
|
RecipientEmail = user.Email,
|
|
Subject = viewModel.Subject,
|
|
Body = emailBody,
|
|
SentDate = DateTime.UtcNow,
|
|
IsSent = isSent
|
|
};
|
|
_context.SentNewsletterEamils.Add(sentEmail);
|
|
|
|
// Add small delay to look more natural
|
|
await Task.Delay(2000); // 2 second delay between sends
|
|
}
|
|
|
|
await _context.SaveChangesAsync();
|
|
TempData["success"] = "Nyhedsbrev sendt successfully.";
|
|
return RedirectToAction(nameof(Index));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
TempData["error"] = "Noget gik galt: " + ex.Message;
|
|
return RedirectToAction(nameof(Index));
|
|
}
|
|
}
|
|
return View(viewModel);
|
|
}
|
|
[HttpGet]
|
|
public IActionResult UploadSubscribers()
|
|
{
|
|
return View();
|
|
}
|
|
|
|
[HttpPost]
|
|
public async Task<IActionResult> UploadSubscribers(PdfUploadViewModel viewModel)
|
|
{
|
|
if (!ModelState.IsValid)
|
|
{
|
|
TempData["error"] = "Invalid model state.";
|
|
return BadRequest("Invalid model state.");
|
|
}
|
|
|
|
if (viewModel.SubscriberFile == null || viewModel.SubscriberFile.Length == 0)
|
|
{
|
|
TempData["error"] = "No file uploaded or file is empty.";
|
|
return BadRequest("No file uploaded or file is empty.");
|
|
}
|
|
|
|
var newSubscribers = new List<Subscription>();
|
|
try
|
|
{
|
|
using (var pdfReader = new PdfReader(viewModel.SubscriberFile.OpenReadStream()))
|
|
using (var pdfDocument = new PdfDocument(pdfReader))
|
|
{
|
|
for (int page = 1; page <= pdfDocument.GetNumberOfPages(); page++)
|
|
{
|
|
var text = PdfTextExtractor.GetTextFromPage(pdfDocument.GetPage(page));
|
|
var matches = Regex.Matches(text, @"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b", RegexOptions.IgnoreCase);
|
|
foreach (Match match in matches)
|
|
{
|
|
var email = match.Value.ToLower();
|
|
var name = email.Split('@')[0].Replace(".", " ").Replace("_", " ");
|
|
if (!newSubscribers.Any(s => s.Email == email))
|
|
{
|
|
newSubscribers.Add(new Subscription { Email = email, Name = name, IsSubscribed = true });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Optional: Check existing emails to avoid duplicates
|
|
var existingEmails = _context.Subscriptions.Select(s => s.Email).ToHashSet();
|
|
newSubscribers = newSubscribers.Where(s => !existingEmails.Contains(s.Email)).ToList();
|
|
|
|
if (newSubscribers.Any())
|
|
{
|
|
_context.Subscriptions.AddRange(newSubscribers);
|
|
await _context.SaveChangesAsync();
|
|
TempData["success"] = $"{newSubscribers.Count} new subscribers added successfully.";
|
|
return Ok($"{newSubscribers.Count} new subscribers added successfully.");
|
|
}
|
|
else
|
|
{
|
|
TempData["info"] = "No new subscribers found in the file.";
|
|
return Ok("No new subscribers found in the file.");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
TempData["error"] = $"Error processing file: {ex.Message}";
|
|
return BadRequest($"Error processing file: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
|
|
|
|
[HttpPost]
|
|
public async Task<IActionResult> MailTracking()
|
|
{
|
|
using (var reader = new StreamReader(Request.Body))
|
|
{
|
|
var requestBody = await reader.ReadToEndAsync();
|
|
Console.WriteLine("Received payload: " + requestBody);
|
|
Request.Body.Position = 0;
|
|
|
|
try
|
|
{
|
|
var events = JArray.Parse(requestBody);
|
|
if (events == null)
|
|
{
|
|
return BadRequest("Parsed data is null");
|
|
}
|
|
|
|
var groupedEvents = events.GroupBy(e => e.Value<string>("email"));
|
|
|
|
foreach (var emailGroup in groupedEvents)
|
|
{
|
|
string email = emailGroup.Key;
|
|
Console.WriteLine($"Processing events for email: {email}");
|
|
|
|
// Retrieve all matching newsletter email records for the current email
|
|
var newsletterEmails = await _context.SentNewsletterEamils
|
|
.Where(n => n.RecipientEmail == email)
|
|
.ToListAsync();
|
|
|
|
if (newsletterEmails == null || !newsletterEmails.Any())
|
|
{
|
|
Console.WriteLine("No newsletter email record found for email: " + email);
|
|
continue;
|
|
}
|
|
|
|
foreach (var newsletterEmail in newsletterEmails)
|
|
{
|
|
// Update the ReceivedActivity property to the current UTC time
|
|
newsletterEmail.ReceivedActivity = DateTime.UtcNow.ToLocalTime();
|
|
string ipAddress = emailGroup.First().Value<string>("ip");
|
|
string geolocation = emailGroup.First().Value<string>("geo");
|
|
|
|
// Update the newsletterEmail with IP address and geolocation
|
|
newsletterEmail.IpAddress = ipAddress;
|
|
newsletterEmail.Geo = geolocation;
|
|
|
|
foreach (var e in emailGroup)
|
|
{
|
|
string eventType = e.Value<string>("event");
|
|
Console.WriteLine($"Processing {eventType} for {email}");
|
|
|
|
switch (eventType)
|
|
{
|
|
case "sent":
|
|
newsletterEmail.IsDelivered = true;
|
|
break;
|
|
case "open":
|
|
newsletterEmail.IsOpened = true;
|
|
break;
|
|
case "click":
|
|
newsletterEmail.IsClicked = true;
|
|
break;
|
|
case "bounce":
|
|
newsletterEmail.IsBounced = true;
|
|
break;
|
|
case "spam":
|
|
newsletterEmail.IsSpam = true;
|
|
break;
|
|
case "unsub":
|
|
newsletterEmail.IsUnsubscribed = true;
|
|
break;
|
|
case "blocked":
|
|
newsletterEmail.IsBlocked = true;
|
|
break;
|
|
default:
|
|
Console.WriteLine($"Unhandled event type: {eventType}");
|
|
break;
|
|
}
|
|
}
|
|
|
|
_context.Entry(newsletterEmail).State = EntityState.Modified;
|
|
}
|
|
}
|
|
Console.WriteLine("Emails got updated ");
|
|
|
|
await _context.SaveChangesAsync();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine("Exception parsing JSON: " + ex.Message);
|
|
return BadRequest("Error parsing JSON: " + ex.Message);
|
|
}
|
|
}
|
|
|
|
return Ok();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//using (var reader = new StreamReader(Request.Body))
|
|
//{
|
|
// var requestBody = await reader.ReadToEndAsync();
|
|
// Console.WriteLine("Received payload: " + requestBody);
|
|
// Request.Body.Position = 0;
|
|
|
|
// try
|
|
// {
|
|
// var events = JArray.Parse(requestBody);
|
|
// if (events == null)
|
|
// {
|
|
// return BadRequest("Parsed data is null");
|
|
// }
|
|
|
|
// foreach (JObject e in events)
|
|
// {
|
|
// string email = e.Value<string>("email");
|
|
// string eventType = e.Value<string>("event");
|
|
// Console.WriteLine($"Processing {eventType} for {email}");
|
|
|
|
// var newsletterEmail = await _context.SentNewsletterEamils
|
|
// .FirstOrDefaultAsync(n => n.RecipientEmail == email);
|
|
|
|
// if (newsletterEmail == null)
|
|
// {
|
|
// Console.WriteLine("No newsletter email record found for email: " + email);
|
|
// continue;
|
|
// }
|
|
|
|
// // Update the ReceivedActivity property to the current UTC time
|
|
// newsletterEmail.ReceivedActivity = DateTime.UtcNow.ToLocalTime();
|
|
|
|
// switch (eventType)
|
|
// {
|
|
// case "sent":
|
|
// newsletterEmail.IsDelivered = true;
|
|
// break;
|
|
// case "open":
|
|
// newsletterEmail.IsOpened = true;
|
|
// break;
|
|
// case "click":
|
|
// newsletterEmail.IsClicked = true;
|
|
// break;
|
|
// case "bounce":
|
|
// newsletterEmail.IsBounced = true;
|
|
// break;
|
|
// case "spam":
|
|
// newsletterEmail.IsSpam = true;
|
|
// break;
|
|
// case "unsub":
|
|
// newsletterEmail.IsUnsubscribed = true;
|
|
// break;
|
|
// case "blocked":
|
|
// newsletterEmail.IsBlocked = true;
|
|
// break;
|
|
// default:
|
|
// Console.WriteLine($"Unhandled event type: {eventType}");
|
|
// break;
|
|
// }
|
|
|
|
// _context.Entry(newsletterEmail).State = EntityState.Modified;
|
|
// }
|
|
// Console.WriteLine("Email got updated ");
|
|
|
|
// await _context.SaveChangesAsync();
|
|
// }
|
|
// catch (Exception ex)
|
|
// {
|
|
// Console.WriteLine("Exception parsing JSON: " + ex.Message);
|
|
// return BadRequest("Error parsing JSON: " + ex.Message);
|
|
// }
|
|
//}
|
|
|
|
//return Ok();
|
|
}
|
|
|
|
|
|
|
|
//public async Task<IActionResult> MailjetWebhook()
|
|
//{
|
|
// using (var reader = new StreamReader(Request.Body))
|
|
// {
|
|
// var requestBody = await reader.ReadToEndAsync();
|
|
// Console.WriteLine("Received payload: " + requestBody);
|
|
// Request.Body.Position = 0;
|
|
|
|
// try
|
|
// {
|
|
// var events = JArray.Parse(requestBody);
|
|
// if (events == null)
|
|
// {
|
|
// return BadRequest("Parsed data is null");
|
|
// }
|
|
|
|
// foreach (JObject e in events)
|
|
// {
|
|
// string email = e.Value<string>("email");
|
|
// string eventType = e.Value<string>("event");
|
|
// Console.WriteLine($"Processing {eventType} for {email}");
|
|
|
|
// var newsletterEmail = await _context.SentNewsletterEamils
|
|
// .FirstOrDefaultAsync(n => n.RecipientEmail == email);
|
|
|
|
// if (newsletterEmail == null)
|
|
// {
|
|
// Console.WriteLine("No newsletter email record found for email: " + email);
|
|
// continue;
|
|
// }
|
|
|
|
// switch (eventType)
|
|
// {
|
|
// case "sent":
|
|
// newsletterEmail.IsDelivered = true;
|
|
// break;
|
|
|
|
// case "open":
|
|
// newsletterEmail.IsOpened = true;
|
|
// break;
|
|
// case "click":
|
|
// newsletterEmail.IsClicked = true;
|
|
// break;
|
|
// case "bounce":
|
|
// newsletterEmail.IsBounced = true;
|
|
// break;
|
|
// case "spam":
|
|
// newsletterEmail.IsSpam = true;
|
|
// break;
|
|
// case "unsub":
|
|
// newsletterEmail.IsUnsubscribed = true;
|
|
// break;
|
|
// case "blocked":
|
|
// newsletterEmail.IsBlocked = true;
|
|
// break;
|
|
// default:
|
|
// Console.WriteLine($"Unhandled event type: {eventType}");
|
|
// break;
|
|
// }
|
|
|
|
// _context.Entry(newsletterEmail).State = EntityState.Modified;
|
|
// }
|
|
// Console.WriteLine("Email got updated ");
|
|
|
|
// await _context.SaveChangesAsync();
|
|
// }
|
|
// catch (Exception ex)
|
|
// {
|
|
// Console.WriteLine("Exception parsing JSON: " + ex.Message);
|
|
// return BadRequest("Error parsing JSON: " + ex.Message);
|
|
// }
|
|
// }
|
|
|
|
// return Ok();
|
|
//}
|
|
|
|
|
|
|
|
public async Task<IActionResult> EmailStats()
|
|
{
|
|
var emails = await _context.SentNewsletterEamils.ToListAsync();
|
|
return View(emails);
|
|
}
|
|
|
|
|
|
|
|
public async Task<IActionResult> GetEmailStatsData()
|
|
{
|
|
var model = await _context.SentNewsletterEamils.ToListAsync();
|
|
return Json(model); // Returns the list of emails as JSON.
|
|
}
|
|
|
|
//public async Task<JsonResult> GetChartData()
|
|
|
|
// var emails = await _context.SentNewsletterEamils.ToListAsync();
|
|
|
|
// var data = new
|
|
// {
|
|
// Sent = emails.Count(e => e.IsSent),
|
|
// Delivered = emails.Count(e => e.IsDelivered),
|
|
// Opened = emails.Count(e => e.IsOpened),
|
|
// Clicked = emails.Count(e => e.IsClicked),
|
|
// Bounced = emails.Count(e => e.IsBounced),
|
|
// Spam = emails.Count(e => e.IsSpam),
|
|
// Blocked = emails.Count(e => e.IsBlocked),
|
|
// Unsubscribed = emails.Count(e => e.IsUnsubscribed)
|
|
// };
|
|
|
|
// return Json(data);
|
|
//}
|
|
|
|
|
|
[HttpPost]
|
|
public IActionResult DeleteSelected(List<int> selectedIds)
|
|
{
|
|
if (selectedIds != null && selectedIds.Count > 0)
|
|
{
|
|
var items = _context.SentNewsletterEamils.Where(x => selectedIds.Contains(x.Id)).ToList();
|
|
_context.SentNewsletterEamils.RemoveRange(items);
|
|
_context.SaveChanges();
|
|
}
|
|
TempData["Success"] = "Email tracking result deleted successfully";
|
|
return RedirectToAction(nameof(EmailStats));
|
|
}
|
|
|
|
[HttpGet]
|
|
public IActionResult Delete(int id)
|
|
{
|
|
var newsLetterFromDb = _repository.GetById(id);
|
|
|
|
var viewmodel = new NewsLetterViewModel
|
|
{
|
|
Id=newsLetterFromDb.Id,
|
|
Email=newsLetterFromDb.Email,
|
|
Name=newsLetterFromDb.Name,
|
|
IsSubscribed=newsLetterFromDb.IsSubscribed
|
|
};
|
|
return View(viewmodel);
|
|
}
|
|
|
|
[HttpPost]
|
|
[ActionName("Delete")]
|
|
public IActionResult DeleteConfirm(int id)
|
|
{
|
|
_repository.Delete(id);
|
|
|
|
_context.SaveChanges();
|
|
TempData["Success"] = "Subscriber deleted successfully";
|
|
return RedirectToAction(nameof(Index));
|
|
}
|
|
|
|
|
|
|
|
}
|
|
}
|