Merge pull request #10393 from umbraco/v9/feature/email-display-name-in-notifiaction

V9: Add new email Model for notifications
This commit is contained in:
Bjarke Berg
2021-06-17 15:39:14 +02:00
committed by GitHub
15 changed files with 290 additions and 23 deletions

View File

@@ -1,5 +1,5 @@
using System;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Email;
namespace Umbraco.Cms.Core.Events
{

View File

@@ -4,7 +4,7 @@ using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Hosting;
using Umbraco.Cms.Core.Mail;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Email;
using Umbraco.Cms.Core.Services;
using Umbraco.Extensions;

View File

@@ -1,5 +1,5 @@
using System.Threading.Tasks;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Email;
namespace Umbraco.Cms.Core.Mail
{

View File

@@ -1,6 +1,6 @@
using System;
using System.Threading.Tasks;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Email;
namespace Umbraco.Cms.Core.Mail
{

View File

@@ -2,7 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
namespace Umbraco.Cms.Core.Models
namespace Umbraco.Cms.Core.Models.Email
{
public class EmailMessage
{
@@ -33,7 +33,6 @@ namespace Umbraco.Cms.Core.Models
public EmailMessage(string from, string[] to, string[] cc, string[] bcc, string[] replyTo, string subject, string body, bool isBodyHtml, IEnumerable<EmailMessageAttachment> attachments)
{
ArgumentIsNotNullOrEmpty(from, nameof(from));
ArgumentIsNotNullOrEmpty(to, nameof(to));
ArgumentIsNotNullOrEmpty(subject, nameof(subject));
ArgumentIsNotNullOrEmpty(body, nameof(body));

View File

@@ -1,6 +1,6 @@
using System.IO;
namespace Umbraco.Cms.Core.Models
namespace Umbraco.Cms.Core.Models.Email
{
public class EmailMessageAttachment
{

View File

@@ -0,0 +1,18 @@
namespace Umbraco.Cms.Core.Models.Email
{
/// <summary>
/// Represents an email address used for notifications. Contains both the address and its display name.
/// </summary>
public class NotificationEmailAddress
{
public string DisplayName { get; }
public string Address { get; }
public NotificationEmailAddress(string address, string displayName)
{
Address = address;
DisplayName = displayName;
}
}
}

View File

@@ -0,0 +1,54 @@
using System.Collections.Generic;
using System.Linq;
namespace Umbraco.Cms.Core.Models.Email
{
/// <summary>
/// Represents an email when sent with notifications.
/// </summary>
public class NotificationEmailModel
{
public NotificationEmailAddress From { get; }
public IEnumerable<NotificationEmailAddress> To { get; }
public IEnumerable<NotificationEmailAddress> Cc { get; }
public IEnumerable<NotificationEmailAddress> Bcc { get; }
public IEnumerable<NotificationEmailAddress> ReplyTo { get; }
public string Subject { get; }
public string Body { get; }
public bool IsBodyHtml { get; }
public IList<EmailMessageAttachment> Attachments { get; }
public bool HasAttachments => Attachments != null && Attachments.Count > 0;
public NotificationEmailModel(
NotificationEmailAddress from,
IEnumerable<NotificationEmailAddress> to,
IEnumerable<NotificationEmailAddress> cc,
IEnumerable<NotificationEmailAddress> bcc,
IEnumerable<NotificationEmailAddress> replyTo,
string subject,
string body,
IEnumerable<EmailMessageAttachment> attachments,
bool isBodyHtml)
{
From = from;
To = to;
Cc = cc;
Bcc = bcc;
ReplyTo = replyTo;
Subject = subject;
Body = body;
IsBodyHtml = isBodyHtml;
Attachments = attachments?.ToList();
}
}
}

View File

@@ -1,11 +1,11 @@
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Email;
namespace Umbraco.Cms.Core.Notifications
{
public class SendEmailNotification : INotification
{
public SendEmailNotification(EmailMessage message) => Message = message;
public SendEmailNotification(NotificationEmailModel message) => Message = message;
public EmailMessage Message { get; set; }
public NotificationEmailModel Message { get; }
}
}

View File

@@ -7,7 +7,7 @@ using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Mail;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Email;
using Umbraco.Cms.Core.Notifications;
using Umbraco.Cms.Infrastructure.Extensions;
using SmtpClient = MailKit.Net.Smtp.SmtpClient;
@@ -53,7 +53,8 @@ namespace Umbraco.Cms.Infrastructure
{
if (enableNotification)
{
await _eventAggregator.PublishAsync(new SendEmailNotification(message));
await _eventAggregator.PublishAsync(
new SendEmailNotification(message.ToNotificationEmail(_globalSettings.Smtp?.From)));
}
return;
}

View File

@@ -1,7 +1,8 @@
using System;
using System.Collections.Generic;
using MimeKit;
using MimeKit.Text;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Email;
namespace Umbraco.Cms.Infrastructure.Extensions
{
@@ -9,13 +10,9 @@ namespace Umbraco.Cms.Infrastructure.Extensions
{
public static MimeMessage ToMimeMessage(this EmailMessage mailMessage, string configuredFromAddress)
{
var fromEmail = mailMessage.From;
if (string.IsNullOrEmpty(fromEmail))
{
fromEmail = configuredFromAddress;
}
var fromEmail = string.IsNullOrEmpty(mailMessage.From) ? configuredFromAddress : mailMessage.From;
if (!InternetAddress.TryParse(mailMessage.From, out InternetAddress fromAddress))
if (!InternetAddress.TryParse(fromEmail, out InternetAddress fromAddress))
{
throw new ArgumentException($"Email could not be sent. Could not parse from address {fromEmail} as a valid email address.");
}
@@ -78,5 +75,57 @@ namespace Umbraco.Cms.Infrastructure.Extensions
throw new InvalidOperationException($"Email could not be sent. Could not parse a valid recipient address.");
}
}
public static NotificationEmailModel ToNotificationEmail(this EmailMessage emailMessage,
string configuredFromAddress)
{
var fromEmail = string.IsNullOrEmpty(emailMessage.From) ? configuredFromAddress : emailMessage.From;
NotificationEmailAddress from = ToNotificationAddress(fromEmail);
return new NotificationEmailModel(from,
GetNotificationAddresses(emailMessage.To),
GetNotificationAddresses(emailMessage.Cc),
GetNotificationAddresses(emailMessage.Bcc),
GetNotificationAddresses(emailMessage.ReplyTo),
emailMessage.Subject,
emailMessage.Body,
emailMessage.Attachments,
emailMessage.IsBodyHtml);
}
private static NotificationEmailAddress ToNotificationAddress(string address)
{
if (InternetAddress.TryParse(address, out InternetAddress internetAddress))
{
if (internetAddress is MailboxAddress mailboxAddress)
{
return new NotificationEmailAddress(mailboxAddress.Address, internetAddress.Name);
}
}
return null;
}
private static IEnumerable<NotificationEmailAddress> GetNotificationAddresses(IEnumerable<string> addresses)
{
if (addresses is null)
{
return null;
}
var notificationAddresses = new List<NotificationEmailAddress>();
foreach (var address in addresses)
{
NotificationEmailAddress notificationAddress = ToNotificationAddress(address);
if (notificationAddress is not null)
{
notificationAddresses.Add(notificationAddress);
}
}
return notificationAddresses;
}
}
}

View File

@@ -11,6 +11,7 @@ using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.IO;
using Umbraco.Cms.Core.Mail;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Email;
using Umbraco.Cms.Core.Models.Entities;
using Umbraco.Cms.Core.Models.Membership;
using Umbraco.Cms.Core.Persistence.Repositories;

View File

@@ -7,6 +7,7 @@ using System.Linq;
using System.Text;
using NUnit.Framework;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Email;
using Umbraco.Cms.Infrastructure.Extensions;
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Extensions
@@ -24,9 +25,9 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Extensions
const string subject = "Subject";
const string body = "<p>Message</p>";
const bool isBodyHtml = true;
var emailMesasge = new EmailMessage(from, to, subject, body, isBodyHtml);
var emailMessage = new EmailMessage(from, to, subject, body, isBodyHtml);
var result = emailMesasge.ToMimeMessage(ConfiguredSender);
var result = emailMessage.ToMimeMessage(ConfiguredSender);
Assert.AreEqual(1, result.From.Count());
Assert.AreEqual(from, result.From.First().ToString());
@@ -54,9 +55,9 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Extensions
{
new EmailMessageAttachment(attachmentStream, "test.txt"),
};
var emailMesasge = new EmailMessage(from, to, cc, bcc, replyTo, subject, body, isBodyHtml, attachments);
var emailMessage = new EmailMessage(from, to, cc, bcc, replyTo, subject, body, isBodyHtml, attachments);
var result = emailMesasge.ToMimeMessage(ConfiguredSender);
var result = emailMessage.ToMimeMessage(ConfiguredSender);
Assert.AreEqual(1, result.From.Count());
Assert.AreEqual(from, result.From.First().ToString());
@@ -77,5 +78,147 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Extensions
Assert.AreEqual(body, result.TextBody.ToString());
Assert.AreEqual(1, result.Attachments.Count());
}
[Test]
public void Can_Construct_MimeMessage_With_ConfiguredSender()
{
const string to = "to@email.com";
const string subject = "Subject";
const string body = "<p>Message</p>";
const bool isBodyHtml = true;
var emailMessage = new EmailMessage(null, to, subject, body, isBodyHtml);
var result = emailMessage.ToMimeMessage(ConfiguredSender);
Assert.AreEqual(1, result.From.Count());
Assert.AreEqual(ConfiguredSender, result.From.First().ToString());
Assert.AreEqual(1, result.To.Count());
Assert.AreEqual(to, result.To.First().ToString());
Assert.AreEqual(subject, result.Subject);
Assert.IsNull(result.TextBody);
Assert.AreEqual(body, result.HtmlBody.ToString());
}
[Test]
public void Can_Construct_NotificationEmailModel_From_Simple_MailMessage()
{
const string from = "from@email.com";
const string to = "to@email.com";
const string subject = "Subject";
const string body = "<p>Message</p>";
const bool isBodyHtml = true;
var emailMessage = new EmailMessage(from, to, subject, body, isBodyHtml);
NotificationEmailModel result = emailMessage.ToNotificationEmail(ConfiguredSender);
Assert.AreEqual(from, result.From.Address);
Assert.AreEqual("", result.From.DisplayName);
Assert.AreEqual(1, result.To.Count());
Assert.AreEqual(to, result.To.First().Address);
Assert.AreEqual("", result.To.First().DisplayName);
Assert.AreEqual(subject, result.Subject);
Assert.AreEqual(body, result.Body);
Assert.IsTrue(result.IsBodyHtml);
Assert.IsFalse(result.HasAttachments);
}
[Test]
public void Can_Construct_NotificationEmailModel_From_Simple_MailMessage_With_Configured_Sender()
{
const string to = "to@email.com";
const string subject = "Subject";
const string body = "<p>Message</p>";
const bool isBodyHtml = true;
var emailMessage = new EmailMessage(null, to, subject, body, isBodyHtml);
NotificationEmailModel result = emailMessage.ToNotificationEmail(ConfiguredSender);
Assert.AreEqual(ConfiguredSender, result.From.Address);
Assert.AreEqual("", result.From.DisplayName);
Assert.AreEqual(1, result.To.Count());
Assert.AreEqual(to, result.To.First().Address);
Assert.AreEqual("", result.To.First().DisplayName);
Assert.AreEqual(subject, result.Subject);
Assert.AreEqual(body, result.Body);
Assert.IsTrue(result.IsBodyHtml);
Assert.IsFalse(result.HasAttachments);
}
[Test]
public void Can_Construct_NotificationEmailModel_From_Simple_MailMessage_With_DisplayName()
{
const string from = "\"From Email\" <from@from.com>";
const string to = "\"To Email\" <to@to.com>";
const string subject = "Subject";
const string body = "<p>Message</p>";
const bool isBodyHtml = true;
var emailMessage = new EmailMessage(from, to, subject, body, isBodyHtml);
NotificationEmailModel result = emailMessage.ToNotificationEmail(ConfiguredSender);
Assert.AreEqual("from@from.com", result.From.Address);
Assert.AreEqual("From Email", result.From.DisplayName);
Assert.AreEqual(1, result.To.Count());
Assert.AreEqual("to@to.com", result.To.First().Address);
Assert.AreEqual("To Email", result.To.First().DisplayName);
Assert.AreEqual(subject, result.Subject);
Assert.AreEqual(body, result.Body);
Assert.IsTrue(result.IsBodyHtml);
Assert.IsFalse(result.HasAttachments);
}
[Test]
public void Can_Construct_NotificationEmailModel_From_Full_EmailMessage()
{
const string from = "\"From Email\" <from@from.com>";
string[] to = { "to@email.com", "\"Second Email\" <to2@email.com>", "invalid@invalid@invalid" };
string[] cc = { "\"First CC\" <cc@email.com>", "cc2@email.com", "invalid@invalid@invalid" };
string[] bcc = { "bcc@email.com", "bcc2@email.com", "\"Third BCC\" <bcc3@email.com>", "invalid@email@address" };
string[] replyTo = { "replyto@email.com", "invalid@invalid@invalid" };
const string subject = "Subject";
const string body = "Message";
const bool isBodyHtml = false;
using var attachmentStream = new MemoryStream(Encoding.UTF8.GetBytes("test"));
var attachments = new List<EmailMessageAttachment>
{
new EmailMessageAttachment(attachmentStream, "test.txt"),
};
var emailMessage = new EmailMessage(from, to, cc, bcc, replyTo, subject, body, isBodyHtml, attachments);
var result = emailMessage.ToNotificationEmail(ConfiguredSender);
Assert.AreEqual("from@from.com", result.From.Address);
Assert.AreEqual("From Email", result.From.DisplayName);
Assert.AreEqual(2, result.To.Count());
Assert.AreEqual("to@email.com", result.To.First().Address);
Assert.AreEqual("", result.To.First().DisplayName);
Assert.AreEqual("to2@email.com", result.To.Skip(1).First().Address);
Assert.AreEqual("Second Email", result.To.Skip(1).First().DisplayName);
Assert.AreEqual(2, result.Cc.Count());
Assert.AreEqual("cc@email.com", result.Cc.First().Address);
Assert.AreEqual("First CC", result.Cc.First().DisplayName);
Assert.AreEqual("cc2@email.com", result.Cc.Skip(1).First().Address);
Assert.AreEqual("", result.Cc.Skip(1).First().DisplayName);
Assert.AreEqual(3, result.Bcc.Count());
Assert.AreEqual("bcc@email.com", result.Bcc.First().Address);
Assert.AreEqual("", result.Bcc.First().DisplayName);
Assert.AreEqual("bcc2@email.com", result.Bcc.Skip(1).First().Address);
Assert.AreEqual("", result.Bcc.Skip(1).First().DisplayName);
Assert.AreEqual("bcc3@email.com", result.Bcc.Skip(2).First().Address);
Assert.AreEqual("Third BCC", result.Bcc.Skip(2).First().DisplayName);
Assert.AreEqual(1, result.ReplyTo.Count());
Assert.AreEqual("replyto@email.com", result.ReplyTo.First().Address);
Assert.AreEqual("", result.ReplyTo.First().DisplayName);
Assert.AreEqual(subject, result.Subject);
Assert.AreEqual(body, result.Body);
Assert.AreEqual(1, result.Attachments.Count());
}
}
}

View File

@@ -17,6 +17,7 @@ using Umbraco.Cms.Core.Mail;
using Umbraco.Cms.Core.Mapping;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.ContentEditing;
using Umbraco.Cms.Core.Models.Email;
using Umbraco.Cms.Core.Models.Membership;
using Umbraco.Cms.Core.Net;
using Umbraco.Cms.Core.Security;

View File

@@ -26,6 +26,7 @@ using Umbraco.Cms.Core.Mapping;
using Umbraco.Cms.Core.Media;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.ContentEditing;
using Umbraco.Cms.Core.Models.Email;
using Umbraco.Cms.Core.Models.Membership;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;