Browse Source

Send an email 30 minutes before the event starts

pull/18/head
Berkan 6 years ago
parent
commit
41e89855e2
  1. 9
      src/EventHub.Application/Emailing/EmailTemplateDefinitionProvider.cs
  2. 2
      src/EventHub.Application/Emailing/EmailTemplates.cs
  3. 2
      src/EventHub.Application/Emailing/Templates/EventRegistration.tpl
  4. 157
      src/EventHub.Application/Emailing/Templates/EventReminder.tpl
  5. 1
      src/EventHub.Application/EventHub.Application.csproj
  6. 13
      src/EventHub.Application/EventHubApplicationModule.cs
  7. 52
      src/EventHub.Application/Events/EventReminderNotifier.cs
  8. 67
      src/EventHub.Application/Events/EventReminderWorker.cs
  9. 2
      src/EventHub.Domain/Events/Event.cs
  10. 15
      src/EventHub.Domain/Users/AppUser.cs
  11. 2499
      src/EventHub.EntityFrameworkCore.DbMigrations/Migrations/20210203084538_Added_IsRemindingEmailSent_To_Event.Designer.cs
  12. 24
      src/EventHub.EntityFrameworkCore.DbMigrations/Migrations/20210203084538_Added_IsRemindingEmailSent_To_Event.cs
  13. 3
      src/EventHub.EntityFrameworkCore.DbMigrations/Migrations/EventHubMigrationsDbContextModelSnapshot.cs

9
src/EventHub.Application/Emailing/EmailTemplateDefinitionProvider.cs

@ -14,6 +14,15 @@ namespace EventHub.Emailing
isInlineLocalized: true
)
);
context.Add(
new TemplateDefinition(
EmailTemplates.EventReminder
).WithVirtualFilePath(
"/Emailing/Templates/EventReminder.tpl",
isInlineLocalized: true
)
);
}
}
}

2
src/EventHub.Application/Emailing/EmailTemplates.cs

@ -3,5 +3,7 @@ namespace EventHub.Emailing
public static class EmailTemplates
{
public const string EventRegistration = "EventHub.Event.Registration";
public const string EventReminder = "EventHub.Event.Reminder";
}
}

2
src/EventHub.Application/Emailing/Templates/EventRegistration.tpl

@ -79,7 +79,7 @@
Hi,
</p>
<p>
Great news! You attended the {{model.title}} event.
Great news! You attended the "{{model.title}}" event.
</p>
<div>
<table border="0" cellpadding="0" cellspacing="0" style=" border-collapse: separate; vertical-align: top; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">

157
src/EventHub.Application/Emailing/Templates/EventReminder.tpl

@ -0,0 +1,157 @@
<html>
<head>
<meta name="viewport" content="width=device-width">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Eventhub</title>
<style>
@media only screen and (max-width: 620px) {
table[class=mail-body] h1{
font-size: 36px !important;
margin-bottom: 10px !important;
}
table[class=mail-body] .content {
padding: 0 !important;
}
table[class=mail-body] .container {
padding: 0 !important;
width: 100% !important;
}
table[class=mail-body] .main {
border-left-width: 0 !important;
border-radius: 0 !important;
border-right-width: 0 !important;
}
table[class=mail-body] td {
width: 100% !important;
float: left!important;
}
table[class=mail-body] td a.btn {
display: block !important;
text-align: center;
}
table[class=mail-body] td h2 {
margin: 10px 0 !important;
}
table[class=mail-body] td .cont {
padding: 30px !important;
}
}
</style>
</head>
<body>
<table border="0" cellpadding="0" cellspacing="0" class="mail-body" style="background-color: #ffffff;width: 100%; font-size: 16px;line-height: 1.4;font-family: Arial, sans-serif;">
<tbody>
<tr>
<td class="container" width="800" style="display: block;margin: 0 auto !important; max-width: 800px;padding: 10px;width: 800px;">
<div class="content" style="box-sizing: border-box;display: block;margin: 0 auto;max-width: 800px;padding: 0px;">
<div style="display: flex; padding-top: 1.5rem">
<h1 style="justify-content: center;margin-left: auto;margin-right: auto;align-content: center;">EventHub</h1>
</div>
<!-- START CENTERED WHITE CONTAINER -->
<div style="display: flex;">
<h1 style="justify-content: center;margin-left: auto;margin-right: auto;align-content: center;">{{model.title}} event will start soon</h1>
</div>
<table class="main" style="background: #ffffff;border-radius: 3px; width: 100%;">
<!-- START MAIN CONTENT AREA -->
<tbody>
<tr>
<td class="wrapper" width="800">
<table class="main" style="background: #ffffff;border-radius: 3px;width: 100%;">
<tbody>
<tr>
<td class="wrapper" width="800">
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; vertical-align: top;mso-table-lspace: 0pt;mso-table-rspace: 0pt;width: 100%; ">
<tbody>
<tr>
<td style="vertical-align: top;" width="100%">
<div class="cont" style="background: #eeeeee; border-radius: 10px; padding: 40px; margin-top: 20px;">
<table style="border-collapse: separate; vertical-align: top;mso-table-lspace: 0pt;mso-table-rspace: 0pt;width: 100%; ">
<tbody>
<tr>
<td width="100%" style="vertical-align: top; font-size: 16px; font-family: Arial, sans-serif; color: #444; margin-top: 20px; margin-bottom: 20px; line-height: 1.75">
<p>
Hi {{model.user_name}},
</p>
<p>
See you in 30 minutes at the "{{model.title}}" event
</p>
<div>
<table border="0" cellpadding="0" cellspacing="0" style=" border-collapse: separate; vertical-align: top; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">
<tbody>
<tr style="text-align: center">
<td style="vertical-align: top;">
<p style="font-size: 13px; font-family: Arial, sans-serif; color: #777777; margin: 0; margin-top: 30px;">Start Time</p>
<p style="font-size: 21px; font-family: Arial, sans-serif; color: #444444; margin-bottom: 30px; margin-top: 6px;">{{model.start_time}}</p>
</td>
<td style="vertical-align: top;">
<p style="font-size: 13px; font-family: Arial, sans-serif; color: #777777; margin: 0; margin-top: 30px;">End Time</p>
<p style="font-size: 21px; font-family: Arial, sans-serif; color: #444444; margin-bottom: 30px; margin-top: 6px;">{{model.end_time}}</p>
</td>
</tr>
</tbody>
</table>
<p style="margin-bottom: 20px; margin-top: 20px; text-align: center;">
<a href="/events/{{model.url}}" class="btn" target="_blank" style="padding: 15px; background: #e90052; border-radius: 40px; text-align: center; color: white; font-weight: bold; text-decoration: none; font-size: 18px; padding-left: 45px; padding-right: 45px;">Event Details</a>
</p>
</div>
<p style="font-size: 16px; font-family: Arial, sans-serif; color: #444; margin-top: 20px; margin-bottom: 0px;">
Thank you,<br> EventHub
</p>
</td>
</tr>
</tbody>
</table>
</div>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<!-- START FOOTER -->
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; vertical-align: top;mso-table-lspace: 0pt;mso-table-rspace: 0pt;width: 100%;">
<tbody>
<tr>
<td class="content-block" style="vertical-align: top;" width="800">
<div class="cont" style="background: #ffffff;border-top: 2px solid #eee; padding: 35px 40px 5px; margin-top: 0px; text-align: center;">
<a href="https://eventhub.abp.io/" style="font-size: 16px; font-family: Arial, sans-serif; color: #777; font-weight: bold; margin-right: 30px; text-decoration: none;">Home</a>
</div>
</td>
</tr>
<tr>
<td class="content-block powered-by" style="vertical-align: top;" width="100%">
<div class="cont" style="background: #ffffff; border-radius: 10px; padding: 5px 40px; margin-top: 10px; margin-bottom: 50px; text-align: center;">
<a href="mailto:info@eventhub.abp.io" style="font-size: 16px; font-family: Arial, sans-serif; color: #777; font-weight: bold; margin-right: 30px; text-decoration: none;"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAARCAYAAADHeGwwAAABMElEQVQ4je3TvyvFYRQG8M+9LEwWiV0xyeAi2ZTUFQuZDAqTxUCJzY9BFotS/gAySAZFEgYlySzFLosfs169w6XL/V6uzbO973vO85z3OeekstlsGRYxrrTYw1g56jGNO6yivAQyS5hFdRpHGMQauvDwC+IXDGAGnTgJApXYxAJO0YLrH5Dfog3bmMAhatI5AUF1B4/oiIFJcYAMbrCRa3X6E0EvzlEbbZtLILCMHlQESzCS+/hZIKARF7Ef8+jDc564VwxhCq24jL/4gHwCAVXYxyR2o7e3Oe/30cbQuzEcB7/zEX03kkF8BU2RJFS3hVS07wnrGP2GI9HMD6MB/eiOd9Wx6vZCyUmXKhM9Hojn8JO6JInFbG2YrLMi4t/xVZNLhn+BgghNvkLzn7Bz9QbIADMmEt9imAAAAABJRU5ErkJggg==" width="16"> info@eventhub.abp.io</a>
<span style="font-size: 16px; font-family: Arial, sans-serif; color: #777; margin-right: 0px; text-decoration: none;"><strong>EventHub</strong> - Powered by Volosoft</span>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- END FOOTER -->
<!-- END CENTERED WHITE CONTAINER -->
</td>
</tr>
</tbody>
</table>
</body>
</html>

1
src/EventHub.Application/EventHub.Application.csproj

@ -13,6 +13,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.2" />
<PackageReference Include="Volo.Abp.Account.Application" Version="4.2.0-rc.1" />
<PackageReference Include="Volo.Abp.Identity.Application" Version="4.2.0-rc.1" />
<PackageReference Include="Volo.Abp.PermissionManagement.Application" Version="4.2.0-rc.1" />

13
src/EventHub.Application/EventHubApplicationModule.cs

@ -1,5 +1,8 @@
using Volo.Abp.Account;
using EventHub.Events;
using Volo.Abp;
using Volo.Abp.Account;
using Volo.Abp.AutoMapper;
using Volo.Abp.BackgroundWorkers;
using Volo.Abp.Identity;
using Volo.Abp.Modularity;
using Volo.Abp.PermissionManagement;
@ -12,10 +15,16 @@ namespace EventHub
typeof(AbpAccountApplicationModule),
typeof(EventHubApplicationContractsModule),
typeof(AbpIdentityApplicationModule),
typeof(AbpPermissionManagementApplicationModule)
typeof(AbpPermissionManagementApplicationModule),
typeof(AbpBackgroundWorkersModule)
)]
public class EventHubApplicationModule : AbpModule
{
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
context.AddBackgroundWorker<EventReminderWorker>();
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpAutoMapperOptions>(options =>

52
src/EventHub.Application/Events/EventReminderNotifier.cs

@ -0,0 +1,52 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using EventHub.Emailing;
using EventHub.Users;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Emailing;
using Volo.Abp.TextTemplating;
namespace EventHub.Events
{
public class EventReminderNotifier : ITransientDependency
{
private readonly IEmailSender _emailSender;
private readonly ITemplateRenderer _templateRenderer;
public EventReminderNotifier(
IEmailSender emailSender,
ITemplateRenderer templateRenderer)
{
_emailSender = emailSender;
_templateRenderer = templateRenderer;
}
public async Task NotifyAsync(
Event @event,
IEnumerable<AppUser> users)
{
if (users is null || @event is null)
{
return;
}
foreach (var user in users)
{
var model = new
{
UserName = user.GetFullNameOrUsername(),
Title = @event.Title,
StartTime = @event.StartTime,
EndTime = @event.EndTime,
Url = @event.Url
};
await _emailSender.QueueAsync(
user.Email,
"The event has last thirty minutes to start!",
await _templateRenderer.RenderAsync(EmailTemplates.EventReminder, model)
);
}
}
}
}

67
src/EventHub.Application/Events/EventReminderWorker.cs

@ -0,0 +1,67 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using EventHub.Events.Registrations;
using EventHub.Users;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Volo.Abp.BackgroundWorkers;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Threading;
using Volo.Abp.Uow;
namespace EventHub.Events
{
public class EventReminderWorker : AsyncPeriodicBackgroundWorkerBase
{
public EventReminderWorker(AbpAsyncTimer timer, IServiceScopeFactory serviceScopeFactory)
: base(timer, serviceScopeFactory)
{
Timer.Period = 60_000;
}
[UnitOfWork]
protected override async Task DoWorkAsync(PeriodicBackgroundWorkerContext workerContext)
{
var eventReminderNotifier = workerContext.ServiceProvider.GetRequiredService<EventReminderNotifier>();
var eventRepository = workerContext.ServiceProvider.GetRequiredService<IRepository<Event, Guid>>();
var userRepository = workerContext.ServiceProvider.GetRequiredService<IRepository<AppUser, Guid>>();
var eventRegistrationRepository = workerContext.ServiceProvider.GetRequiredService<IRepository<EventRegistration, Guid>>();
var eventQueryable = await eventRepository.GetQueryableAsync();
var userQueryable = await userRepository.GetQueryableAsync();
var eventRegistrationQueryable = await eventRegistrationRepository.GetQueryableAsync();
var thirtyOneMinutesAfter = DateTime.Now.AddMinutes(31);
var twentyNineMinutesAfter = DateTime.Now.AddMinutes(29);
var eventsStartingAfterThirtyMinutes = await eventQueryable.Where(x =>
x.IsRemindingEmailSent == false &&
x.StartTime < thirtyOneMinutesAfter &&
x.StartTime > twentyNineMinutesAfter)
.ToListAsync();
foreach (var @event in eventsStartingAfterThirtyMinutes)
{
var usersToBeNotified = await (from eventRegistration in eventRegistrationQueryable
join user in userQueryable on eventRegistration.UserId equals user.Id
where eventRegistration.EventId == @event.Id
select user).ToListAsync();
@event.IsRemindingEmailSent = true;
try
{
await eventReminderNotifier.NotifyAsync(@event, usersToBeNotified);
}
catch (Exception e)
{
@event.IsRemindingEmailSent = false;
Logger.LogError($"An error occurred while sending reminder mail for {@event.Id} event. Error message: {e.Message}");
}
}
}
}
}

2
src/EventHub.Domain/Events/Event.cs

@ -24,6 +24,8 @@ namespace EventHub.Events
public int? Capacity { get; set; }
public bool IsRemindingEmailSent { get; set; }
private Event()
{

15
src/EventHub.Domain/Users/AppUser.cs

@ -59,5 +59,20 @@ namespace EventHub.Users
{
}
public string GetFullNameOrUsername()
{
if (!string.IsNullOrWhiteSpace(Name) && !string.IsNullOrWhiteSpace(Surname))
{
return Name + " " + Surname;
}
if (!string.IsNullOrWhiteSpace(Name))
{
return Name;
}
return UserName;
}
}
}

2499
src/EventHub.EntityFrameworkCore.DbMigrations/Migrations/20210203084538_Added_IsRemindingEmailSent_To_Event.Designer.cs

File diff suppressed because it is too large

24
src/EventHub.EntityFrameworkCore.DbMigrations/Migrations/20210203084538_Added_IsRemindingEmailSent_To_Event.cs

@ -0,0 +1,24 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace EventHub.Migrations
{
public partial class Added_IsRemindingEmailSent_To_Event : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "IsRemindingEmailSent",
table: "AppEvents",
type: "bit",
nullable: false,
defaultValue: false);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "IsRemindingEmailSent",
table: "AppEvents");
}
}
}

3
src/EventHub.EntityFrameworkCore.DbMigrations/Migrations/EventHubMigrationsDbContextModelSnapshot.cs

@ -73,6 +73,9 @@ namespace EventHub.Migrations
b.Property<bool>("IsOnline")
.HasColumnType("bit");
b.Property<bool>("IsRemindingEmailSent")
.HasColumnType("bit");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");

Loading…
Cancel
Save