mirror of https://github.com/abpframework/eventhub
22 changed files with 496 additions and 343 deletions
@ -0,0 +1,34 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<OutputType>Exe</OutputType> |
|||
<TargetFramework>net5.0</TargetFramework> |
|||
<RootNamespace>EventHub</RootNamespace> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Autofac" Version="4.2.1" /> |
|||
<PackageReference Include="Volo.Abp.BackgroundWorkers" Version="4.2.1" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="5.0.*" /> |
|||
<PackageReference Include="Serilog.Extensions.Hosting" Version="3.1.0" /> |
|||
<PackageReference Include="Serilog.Sinks.Async" Version="1.4.0" /> |
|||
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" /> |
|||
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<Content Include="appsettings.json"> |
|||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> |
|||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> |
|||
</Content> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\EventHub.Domain\EventHub.Domain.csproj" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
@ -0,0 +1,36 @@ |
|||
using System; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.Hosting; |
|||
using Volo.Abp; |
|||
|
|||
namespace EventHub |
|||
{ |
|||
public class EventHubBackgroundServicesHostedService : IHostedService |
|||
{ |
|||
private readonly IAbpApplicationWithExternalServiceProvider _application; |
|||
private readonly IServiceProvider _serviceProvider; |
|||
|
|||
public EventHubBackgroundServicesHostedService( |
|||
IAbpApplicationWithExternalServiceProvider application, |
|||
IServiceProvider serviceProvider) |
|||
{ |
|||
_application = application; |
|||
_serviceProvider = serviceProvider; |
|||
} |
|||
|
|||
public Task StartAsync(CancellationToken cancellationToken) |
|||
{ |
|||
_application.Initialize(_serviceProvider); |
|||
|
|||
return Task.CompletedTask; |
|||
} |
|||
|
|||
public Task StopAsync(CancellationToken cancellationToken) |
|||
{ |
|||
_application.Shutdown(); |
|||
|
|||
return Task.CompletedTask; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
using EventHub.Events; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Hosting; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Autofac; |
|||
using Volo.Abp.BackgroundWorkers; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace EventHub |
|||
{ |
|||
|
|||
[DependsOn( |
|||
typeof(AbpAutofacModule) |
|||
)] |
|||
public class EventHubBackgroundServicesModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
var configuration = context.Services.GetConfiguration(); |
|||
var hostEnvironment = context.Services.GetSingletonInstance<IHostEnvironment>(); |
|||
|
|||
context.Services.AddHostedService<EventHubBackgroundServicesHostedService>(); |
|||
} |
|||
|
|||
public override void OnApplicationInitialization(ApplicationInitializationContext context) |
|||
{ |
|||
context.AddBackgroundWorker<EventReminderWorker>(); |
|||
context.AddBackgroundWorker<NewEventDetectorWorker>(); |
|||
context.AddBackgroundWorker<EventTimingChangeWorker>(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,82 +1,81 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Linq.Dynamic.Core; |
|||
using System.Threading.Tasks; |
|||
using EventHub.Emailing; |
|||
using EventHub.Events.Registrations; |
|||
using EventHub.Users; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Domain.Repositories; |
|||
using Volo.Abp.Emailing; |
|||
using Volo.Abp.Linq; |
|||
using Volo.Abp.TextTemplating; |
|||
|
|||
namespace EventHub.Events |
|||
{ |
|||
public class EventTimingChangeNotifier : ITransientDependency |
|||
{ |
|||
private readonly IEmailSender _emailSender; |
|||
private readonly ITemplateRenderer _templateRenderer; |
|||
private readonly IRepository<EventRegistration, Guid> _eventRegistrationRepository; |
|||
private readonly IAsyncQueryableExecuter _asyncExecuter; |
|||
private readonly IRepository<AppUser, Guid> _userRepository; |
|||
|
|||
public EventTimingChangeNotifier( |
|||
IEmailSender emailSender, |
|||
ITemplateRenderer templateRenderer, |
|||
IRepository<EventRegistration, Guid> eventRegistrationRepository, |
|||
IAsyncQueryableExecuter asyncExecuter, |
|||
IRepository<AppUser, Guid> userRepository |
|||
) |
|||
{ |
|||
_emailSender = emailSender; |
|||
_templateRenderer = templateRenderer; |
|||
_eventRegistrationRepository = eventRegistrationRepository; |
|||
_asyncExecuter = asyncExecuter; |
|||
_userRepository = userRepository; |
|||
} |
|||
|
|||
public async Task NotifyAsync(Event @event) |
|||
{ |
|||
if (@event is null) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
var eventRegistrationQueryable = await _eventRegistrationRepository.GetQueryableAsync(); |
|||
var query = eventRegistrationQueryable |
|||
.Where(x => x.EventId == @event.Id && !x.IsTimingChangeEmailSent); |
|||
|
|||
var eventRegistrations = await _asyncExecuter.ToListAsync(query); |
|||
|
|||
foreach (var eventRegistration in eventRegistrations) |
|||
{ |
|||
var user = await _userRepository.FindAsync(eventRegistration.UserId); |
|||
|
|||
if (user is null) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
var model = new |
|||
{ |
|||
UserName = user.GetFullNameOrUsername(), |
|||
Title = @event.Title, |
|||
StartTime = @event.StartTime, |
|||
EndTime = @event.EndTime, |
|||
Url = @event.Url |
|||
}; |
|||
|
|||
await _emailSender.QueueAsync( |
|||
user.Email, |
|||
"Event time has been changed!", |
|||
await _templateRenderer.RenderAsync(EmailTemplates.EventTimingChanged, model) |
|||
); |
|||
|
|||
eventRegistration.IsTimingChangeEmailSent = true; |
|||
} |
|||
|
|||
await _eventRegistrationRepository.UpdateManyAsync(eventRegistrations, autoSave: true); |
|||
} |
|||
} |
|||
using System; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using EventHub.Emailing; |
|||
using EventHub.Events.Registrations; |
|||
using EventHub.Users; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Domain.Repositories; |
|||
using Volo.Abp.Emailing; |
|||
using Volo.Abp.Linq; |
|||
using Volo.Abp.TextTemplating; |
|||
|
|||
namespace EventHub.Events |
|||
{ |
|||
public class EventTimingChangeNotifier : ITransientDependency |
|||
{ |
|||
private readonly IEmailSender _emailSender; |
|||
private readonly ITemplateRenderer _templateRenderer; |
|||
private readonly IRepository<EventRegistration, Guid> _eventRegistrationRepository; |
|||
private readonly IAsyncQueryableExecuter _asyncExecuter; |
|||
private readonly IRepository<AppUser, Guid> _userRepository; |
|||
|
|||
public EventTimingChangeNotifier( |
|||
IEmailSender emailSender, |
|||
ITemplateRenderer templateRenderer, |
|||
IRepository<EventRegistration, Guid> eventRegistrationRepository, |
|||
IAsyncQueryableExecuter asyncExecuter, |
|||
IRepository<AppUser, Guid> userRepository |
|||
) |
|||
{ |
|||
_emailSender = emailSender; |
|||
_templateRenderer = templateRenderer; |
|||
_eventRegistrationRepository = eventRegistrationRepository; |
|||
_asyncExecuter = asyncExecuter; |
|||
_userRepository = userRepository; |
|||
} |
|||
|
|||
public async Task NotifyAsync(Event @event) |
|||
{ |
|||
if (@event is null) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
var eventRegistrationQueryable = await _eventRegistrationRepository.GetQueryableAsync(); |
|||
var query = eventRegistrationQueryable |
|||
.Where(x => x.EventId == @event.Id && !x.IsTimingChangeEmailSent); |
|||
|
|||
var eventRegistrations = await _asyncExecuter.ToListAsync(query); |
|||
|
|||
foreach (var eventRegistration in eventRegistrations) |
|||
{ |
|||
var user = await _userRepository.FindAsync(eventRegistration.UserId); |
|||
|
|||
if (user is null) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
var model = new |
|||
{ |
|||
UserName = user.GetFullNameOrUsername(), |
|||
Title = @event.Title, |
|||
StartTime = @event.StartTime, |
|||
EndTime = @event.EndTime, |
|||
Url = @event.Url |
|||
}; |
|||
|
|||
await _emailSender.QueueAsync( |
|||
user.Email, |
|||
"Event time has been changed!", |
|||
await _templateRenderer.RenderAsync(EmailTemplates.EventTimingChanged, model) |
|||
); |
|||
|
|||
eventRegistration.IsTimingChangeEmailSent = true; |
|||
} |
|||
|
|||
await _eventRegistrationRepository.UpdateManyAsync(eventRegistrations, autoSave: true); |
|||
} |
|||
} |
|||
} |
|||
@ -1,51 +1,51 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using EventHub.Events.Registrations; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Logging; |
|||
using Volo.Abp.BackgroundWorkers; |
|||
using Volo.Abp.Domain.Repositories; |
|||
using Volo.Abp.Linq; |
|||
using Volo.Abp.Threading; |
|||
using Volo.Abp.Uow; |
|||
|
|||
namespace EventHub.Events |
|||
{ |
|||
public class EventTimingChangeWorker : AsyncPeriodicBackgroundWorkerBase |
|||
{ |
|||
public EventTimingChangeWorker(AbpAsyncTimer timer, IServiceScopeFactory serviceScopeFactory) : base(timer, serviceScopeFactory) |
|||
{ |
|||
Timer.Period = 60000; //1 minute
|
|||
} |
|||
|
|||
[UnitOfWork(isTransactional: false)] |
|||
protected override async Task DoWorkAsync(PeriodicBackgroundWorkerContext workerContext) |
|||
{ |
|||
var eventTimingChangeNotifier = workerContext.ServiceProvider.GetRequiredService<EventTimingChangeNotifier>(); |
|||
var eventRepository = workerContext.ServiceProvider.GetRequiredService<IRepository<Event, Guid>>(); |
|||
var eventRegistrationRepository = workerContext.ServiceProvider.GetRequiredService<IRepository<EventRegistration, Guid>>(); |
|||
var asyncExecuter = workerContext.ServiceProvider.GetRequiredService<IAsyncQueryableExecuter>(); |
|||
|
|||
var eventRegistrationQueryable = await eventRegistrationRepository.GetQueryableAsync(); |
|||
var query = eventRegistrationQueryable |
|||
.Where(x => !x.IsTimingChangeEmailSent) |
|||
.Select(x => x.EventId) |
|||
.Distinct(); |
|||
|
|||
var eventRegistrations = await asyncExecuter.ToListAsync(query); |
|||
|
|||
foreach (var eventRegistration in eventRegistrations) |
|||
{ |
|||
try |
|||
{ |
|||
await eventTimingChangeNotifier.NotifyAsync(await eventRepository.FindAsync(eventRegistration)); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
Logger.LogError($"An error occurred while sending an email to the attendees of the event after the event time has changed. Error message: {e.Message}"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
using System; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using EventHub.Events.Registrations; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Logging; |
|||
using Volo.Abp.BackgroundWorkers; |
|||
using Volo.Abp.Domain.Repositories; |
|||
using Volo.Abp.Linq; |
|||
using Volo.Abp.Threading; |
|||
using Volo.Abp.Uow; |
|||
|
|||
namespace EventHub.Events |
|||
{ |
|||
public class EventTimingChangeWorker : AsyncPeriodicBackgroundWorkerBase |
|||
{ |
|||
public EventTimingChangeWorker(AbpAsyncTimer timer, IServiceScopeFactory serviceScopeFactory) : base(timer, serviceScopeFactory) |
|||
{ |
|||
Timer.Period = 60000; //1 minute
|
|||
} |
|||
|
|||
[UnitOfWork(isTransactional: false)] |
|||
protected override async Task DoWorkAsync(PeriodicBackgroundWorkerContext workerContext) |
|||
{ |
|||
var eventTimingChangeNotifier = workerContext.ServiceProvider.GetRequiredService<EventTimingChangeNotifier>(); |
|||
var eventRepository = workerContext.ServiceProvider.GetRequiredService<IRepository<Event, Guid>>(); |
|||
var eventRegistrationRepository = workerContext.ServiceProvider.GetRequiredService<IRepository<EventRegistration, Guid>>(); |
|||
var asyncExecuter = workerContext.ServiceProvider.GetRequiredService<IAsyncQueryableExecuter>(); |
|||
|
|||
var eventRegistrationQueryable = await eventRegistrationRepository.GetQueryableAsync(); |
|||
var query = eventRegistrationQueryable |
|||
.Where(x => !x.IsTimingChangeEmailSent) |
|||
.Select(x => x.EventId) |
|||
.Distinct(); |
|||
|
|||
var eventRegistrations = await asyncExecuter.ToListAsync(query); |
|||
|
|||
foreach (var eventRegistration in eventRegistrations) |
|||
{ |
|||
try |
|||
{ |
|||
await eventTimingChangeNotifier.NotifyAsync(await eventRepository.FindAsync(eventRegistration)); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
Logger.LogError($"An error occurred while sending an email to the attendees of the event after the event time has changed. Error message: {e.Message}"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,53 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Hosting; |
|||
using Serilog; |
|||
using Serilog.Events; |
|||
|
|||
namespace EventHub |
|||
{ |
|||
public class Program |
|||
{ |
|||
public static async Task<int> Main(string[] args) |
|||
{ |
|||
Log.Logger = new LoggerConfiguration() |
|||
#if DEBUG
|
|||
.MinimumLevel.Debug() |
|||
#else
|
|||
.MinimumLevel.Information() |
|||
#endif
|
|||
.MinimumLevel.Override("Microsoft", LogEventLevel.Information) |
|||
.Enrich.FromLogContext() |
|||
.WriteTo.Async(c => c.File("Logs/logs.txt")) |
|||
.WriteTo.Async(c => c.Console()) |
|||
.CreateLogger(); |
|||
|
|||
try |
|||
{ |
|||
Log.Information("Starting console host."); |
|||
await CreateHostBuilder(args).RunConsoleAsync(); |
|||
return 0; |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
Log.Fatal(ex, "Host terminated unexpectedly!"); |
|||
return 1; |
|||
} |
|||
finally |
|||
{ |
|||
Log.CloseAndFlush(); |
|||
} |
|||
|
|||
} |
|||
|
|||
internal static IHostBuilder CreateHostBuilder(string[] args) => |
|||
Host.CreateDefaultBuilder(args) |
|||
.UseAutofac() |
|||
.UseSerilog() |
|||
.ConfigureServices((hostContext, services) => |
|||
{ |
|||
services.AddApplication<EventHubBackgroundServicesModule>(); |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
{ |
|||
|
|||
} |
|||
@ -1,157 +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 time has been changed!</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> |
|||
The "{{model.title}}" event time just rescheduled. |
|||
</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> |
|||
|
|||
<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 time has been changed!</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> |
|||
The "{{model.title}}" event time just rescheduled. |
|||
</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> |
|||
Loading…
Reference in new issue