Skip to content

Commit

Permalink
Implement UI for email preview text
Browse files Browse the repository at this point in the history
  • Loading branch information
daniel-lerch committed Nov 29, 2023
1 parent ab4df19 commit a407029
Show file tree
Hide file tree
Showing 13 changed files with 219 additions and 409 deletions.
8 changes: 4 additions & 4 deletions src/TravelBlog/Configuration/MailingOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ namespace TravelBlog.Configuration;
public class MailingOptions
{
public bool EnableMailing { get; set; }
[Required] public string? SenderName { get; set; }
[Required, EmailAddress] public string? SenderAddress { get; set; }
[Required] public string? AuthorName { get; set; }
[Required, EmailAddress] public string? AuthorAddress { get; set; }
[Required] public required string SenderName { get; set; }
[Required, EmailAddress] public required string SenderAddress { get; set; }
[Required] public required string AuthorName { get; set; }
[Required, EmailAddress] public required string AuthorAddress { get; set; }

[Required] public string? SmtpUsername { get; set; }
[Required] public string? SmtpPassword { get; set; }
Expand Down
2 changes: 1 addition & 1 deletion src/TravelBlog/Configuration/SiteOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ namespace TravelBlog.Configuration;

public class SiteOptions
{
[Required] public string? BlogName { get; set; }
[Required] public required string BlogName { get; set; }
[Required] public string? AdminPassword { get; set; }
public bool EnableDebugFeatures { get; set; }
}
69 changes: 9 additions & 60 deletions src/TravelBlog/Controllers/BlogPostController.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
using MimeKit;
using TravelBlog.Configuration;
using TravelBlog.Database;
using TravelBlog.Database.Entities;
using TravelBlog.Extensions;
Expand All @@ -24,20 +20,18 @@ namespace TravelBlog.Controllers;
[Route("~/post/{id?}/{action=Index}")]
public class BlogPostController : Controller
{
private readonly IOptions<SiteOptions> siteOptions;
private readonly IOptions<MailingOptions> mailingOptions;
private readonly DatabaseContext database;
private readonly EmailDeliveryService deliveryService;
private readonly MimeMessageCreationService mimeMessageCreation;
private readonly AuthenticationService authentication;
private readonly MarkdownService markdown;

public BlogPostController(IOptions<SiteOptions> siteOptions, IOptions<MailingOptions> mailingOptions, DatabaseContext database,
EmailDeliveryService deliveryService, AuthenticationService authentication, MarkdownService markdown)
public BlogPostController(DatabaseContext database, EmailDeliveryService deliveryService,
MimeMessageCreationService mimeMessageCreation, AuthenticationService authentication, MarkdownService markdown)
{
this.siteOptions = siteOptions;
this.mailingOptions = mailingOptions;
this.database = database;
this.deliveryService = deliveryService;
this.mimeMessageCreation = mimeMessageCreation;
this.authentication = authentication;
this.markdown = markdown;
}
Expand Down Expand Up @@ -142,19 +136,6 @@ public async Task<IActionResult> Draft(string title, string? content, bool liste
return Redirect("~/post/" + post.Id);
}

[HttpPost("~/post/create")]
[Authorize(Roles = Constants.AdminRole)]
public async Task<IActionResult> Create(string title, string? content, bool listed)
{
var post = new BlogPost(id: default, title, content ?? string.Empty, publishTime: DateTime.Now, modifyTime: default, listed);
database.BlogPosts.Add(post);
await database.SaveChangesAsync();

await NotifySubscribers(post);

return Redirect("~/post/" + post.Id);
}

[HttpGet]
[Authorize(Roles = Constants.AdminRole)]
public async Task<IActionResult> Edit(int id)
Expand Down Expand Up @@ -185,7 +166,7 @@ public async Task<IActionResult> Edit(int id, string title, string? content, boo

[HttpPost]
[Authorize(Roles = Constants.AdminRole)]
public async Task<IActionResult> Publish(int id, string title, string? content, bool listed)
public async Task<IActionResult> Publish(int id, string title, string? content, bool listed, string? preview)
{
BlogPost? post = await database.BlogPosts.SingleOrDefaultAsync(p => p.Id == id);
if (post is null)
Expand All @@ -198,54 +179,22 @@ public async Task<IActionResult> Publish(int id, string title, string? content,
post.Listed = listed;
await database.SaveChangesAsync();

await NotifySubscribers(post);
await NotifySubscribers(post, preview ?? string.Empty);

return Redirect("~/post/" + id);
}

private async Task NotifySubscribers(BlogPost post)
private async Task NotifySubscribers(BlogPost post, string preview)
{
List<Subscriber> subscribers = await database.Subscribers
.Where(s => s.MailAddress != null && s.ConfirmationTime != default && s.DeletionTime == default).ToListAsync();

string htmlTemplate = LoadHtmlTemplate();

await deliveryService.Enqueue(subscribers.Select(subscriber =>
{
string postUrl = Url.ContentLink($"~/post/{post.Id}/auth?token={subscriber.Token}");
string unsubscribeUrl = Url.ContentLink("~/unsubscribe?token=" + subscriber.Token);
string message = $"Hey {subscriber.GivenName},\r\n" +
$"es wurde etwas neues auf {siteOptions.Value.BlogName} gepostet:\r\n" +
$"{postUrl}\r\n\r\n" +
$"Du kannst dich von diesem Blog jederzeit hier abmelden: {unsubscribeUrl}";
string htmlBody = htmlTemplate
.Replace("${POST_TITLE}", post.Title)
.Replace("${POST_HTML_PREVIEW}", post.Content)
.Replace("${POST_URL}", postUrl)
.Replace("${BLOG_NAME}", siteOptions.Value.BlogName)
.Replace("${UNSUBCRIBE_URL}", unsubscribeUrl);
MimeMessage mimeMessage = new();
mimeMessage.From.Add(new MailboxAddress(mailingOptions.Value.SenderName, mailingOptions.Value.SenderAddress));
mimeMessage.To.Add(new MailboxAddress(subscriber.GetName(), subscriber.MailAddress));
mimeMessage.ReplyTo.Add(new MailboxAddress(mailingOptions.Value.AuthorName, mailingOptions.Value.AuthorAddress));
mimeMessage.Subject = "Neuer Post";
mimeMessage.Body = new MultipartAlternative
{
new TextPart("plain") { Text = message },
new TextPart("html") { Text = htmlBody }
};
MimeMessage mimeMessage =
mimeMessageCreation.CreatePostNotification(post, subscriber, preview, Url);
return (subscriber.MailAddress!, mimeMessage);
}), post.Id);
}

private static string LoadHtmlTemplate()
{
using Stream? stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("TravelBlog.Resources.post.html")
?? throw new ApplicationException("Could not load resource post.html");
using StreamReader reader = new(stream);
return reader.ReadToEnd();
}
}
1 change: 1 addition & 0 deletions src/TravelBlog/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ public static void Main(string[] args)
builder.Services.AddScoped<EmailDeliveryService>();

builder.Services.AddTransient<SubscriberService>();
builder.Services.AddTransient<MimeMessageCreationService>();

if (!builder.Environment.IsDevelopment())
{
Expand Down
Loading

0 comments on commit a407029

Please sign in to comment.