154 lines
5.7 KiB
Plaintext
154 lines
5.7 KiB
Plaintext
@page "/Email/Subscribe"
|
|
@page "/Email/Confirm"
|
|
|
|
@using Microsoft.Extensions.Options
|
|
@using Wave.Data
|
|
@using System.ComponentModel.DataAnnotations
|
|
@using System.Net
|
|
@using Microsoft.EntityFrameworkCore
|
|
@using Wave.Services
|
|
@using Wave.Utilities
|
|
|
|
@inject ILogger<EmailSignup> Logger
|
|
@inject IStringLocalizer<EmailSignup> Localizer
|
|
@inject IDbContextFactory<ApplicationDbContext> ContextFactory
|
|
@inject IOptions<Features> Features
|
|
@inject IOptions<Customization> Customizations
|
|
@inject NavigationManager Navigation
|
|
@inject IAdvancedEmailSender EmailSender
|
|
@inject EmailTemplateService TemplateService
|
|
|
|
<PageTitle>@(TitlePrefix + Localizer["Title"])</PageTitle>
|
|
|
|
@if (!string.IsNullOrWhiteSpace(Message)) {
|
|
<div class="alert alert-success">
|
|
<span>@Message</span>
|
|
</div>
|
|
}
|
|
|
|
<BoardComponent CenterContent="true">
|
|
<BoardCardComponent Heading="@Localizer["Title"]">
|
|
<EditForm method="post" FormName="email-signup" Model="Model" OnValidSubmit="OnValidSubmit">
|
|
<DataAnnotationsValidator />
|
|
|
|
<InputLabelComponent LabelText="@Localizer["Name_Label"]" For="() => Model.Name">
|
|
<InputText @bind-Value="Model.Name" class="input input-bordered w-full" autocomplete="name"
|
|
placeholder="@Localizer["Name_Placeholder"]" />
|
|
</InputLabelComponent>
|
|
<InputLabelComponent LabelText="@Localizer["Email_Label"]" For="() => Model.Name">
|
|
<InputText @bind-Value="Model.Email" class="input input-bordered w-full" autocomplete="email" type="email"
|
|
required aria-required="true" placeholder="@Localizer["Email_Placeholder"]" />
|
|
</InputLabelComponent>
|
|
|
|
<button type="submit" class="btn btn-primary w-full">@Localizer["Submit"]</button>
|
|
</EditForm>
|
|
</BoardCardComponent>
|
|
</BoardComponent>
|
|
|
|
@code {
|
|
[CascadingParameter(Name = "TitlePrefix")]
|
|
private string TitlePrefix { get; set; } = default!;
|
|
[SupplyParameterFromForm(FormName = "email-signup")]
|
|
private InputModel Model { get; set; } = new();
|
|
|
|
[Parameter, SupplyParameterFromQuery(Name = "user")]
|
|
public string? Id { get; set; }
|
|
[Parameter, SupplyParameterFromQuery(Name = "token")]
|
|
public string? Token { get; set; }
|
|
|
|
private string Message { get; set; } = string.Empty;
|
|
|
|
protected override async Task OnInitializedAsync() {
|
|
if (Features.Value.EmailSubscriptions is not true)
|
|
throw new ApplicationException("Email subscriptions not enabled.");
|
|
|
|
if (Id is null || Token is null) return;
|
|
try {
|
|
var id = await TemplateService.ValidateTokensAsync(Id, Token, deleteToken: false);
|
|
|
|
if (id is null) {
|
|
Message = Localizer["Failure_Message"];
|
|
return;
|
|
}
|
|
|
|
await using var context = await ContextFactory.CreateDbContextAsync();
|
|
var subscriber = context.Set<EmailSubscriber>().IgnoreQueryFilters().FirstOrDefault(s => s.Id == id);
|
|
if (subscriber is null) {
|
|
Message = Localizer["Failure_Message"];
|
|
return;
|
|
}
|
|
subscriber.Unsubscribed = false;
|
|
await context.SaveChangesAsync();
|
|
Message = Localizer["Success_Message"];
|
|
|
|
await TemplateService.ValidateTokensAsync(Id, Token, deleteToken: true);
|
|
|
|
var articles = await context.Set<EmailNewsletter>()
|
|
.IgnoreAutoIncludes()
|
|
.Include(a => a.Article).ThenInclude(a => a.Author)
|
|
.OrderByDescending(a => a.DistributionDateTime)
|
|
.Take(3)
|
|
.ToListAsync();
|
|
string body = $"<p>{Localizer["WelcomeEmailBody"]}</p>\n";
|
|
|
|
foreach (var n in articles) {
|
|
string articleLink = ArticleUtilities.GenerateArticleLink(n.Article, new Uri(Customizations.Value.AppUrl, UriKind.Absolute));
|
|
body = body +
|
|
"<div style=\"padding: 10px; background: #333; color: #fff; margin-bottom: 10px\">" +
|
|
$"<h3>{n.Article.Title}</h3>" +
|
|
$"<small>{n.Article.Author.Name}</small>" +
|
|
$"<p>{n.Article.Body[..Math.Min(100, n.Article.Body.Length)]}...</p>" +
|
|
$"<a href=\"{articleLink}\">Link</a>" +
|
|
"</div>";
|
|
}
|
|
await EmailSender.SendSubscribedMailAsync(subscriber,
|
|
Localizer["WelcomeEmailSubject"], Localizer["WelcomeEmailTitle"], body, subscribedRole: "welcome");
|
|
} catch (Exception ex) {
|
|
Logger.LogError(ex, "Error trying to confirm subscriber.");
|
|
Message = Localizer["Failure_Message"];
|
|
}
|
|
}
|
|
|
|
private async Task OnValidSubmit() {
|
|
if (Features.Value.EmailSubscriptions is not true)
|
|
throw new ApplicationException("Email subscriptions not enabled.");
|
|
|
|
try {
|
|
Message = Localizer["Submit_Message"];
|
|
await using var context = await ContextFactory.CreateDbContextAsync();
|
|
|
|
var subscriber = context.Set<EmailSubscriber>().IgnoreQueryFilters().FirstOrDefault(s => s.Email == Model.Email);
|
|
if (subscriber?.Unsubscribed is false) return;
|
|
|
|
subscriber ??= new EmailSubscriber {
|
|
Email = Model.Email.Trim(),
|
|
Unsubscribed = true
|
|
};
|
|
subscriber.Name = Model.Name;
|
|
context.Update(subscriber);
|
|
await context.SaveChangesAsync();
|
|
|
|
if (subscriber.Unsubscribed) {
|
|
(string id, string token) = await TemplateService.CreateConfirmTokensAsync(subscriber.Id);
|
|
|
|
string confirmLink = Navigation.ToAbsoluteUri(
|
|
$"/Email/Confirm?user={WebUtility.UrlEncode(id)}&token={WebUtility.UrlEncode(token)}").AbsoluteUri;
|
|
string body = string.Format(Localizer["ConfirmEmailBody"], Customizations.Value.AppName) +
|
|
$"""<p style="text-align: center"><a href="{confirmLink}">{Localizer["Submit"]}</a></p>""";
|
|
await EmailSender.SendDefaultMailAsync(subscriber.Email, subscriber.Name, Localizer["ConfirmEmailSubject"],
|
|
Localizer["ConfirmEmailTitle"], body);
|
|
}
|
|
} catch (Exception ex) {
|
|
Logger.LogError(ex, "Failed to create subscriber/send confirmation mail.");
|
|
Message = Localizer["Failure_Message"];
|
|
}
|
|
}
|
|
|
|
private sealed class InputModel {
|
|
[MaxLength(128)]
|
|
public string? Name { get; set; }
|
|
[EmailAddress, Required(AllowEmptyStrings = false), MaxLength(256)]
|
|
public string Email { get; set; } = string.Empty;
|
|
}
|
|
}
|