-
Notifications
You must be signed in to change notification settings - Fork 3
gl-dotnet-email changes for Cc, Bcc, ReplyTo, and attachments #33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from 10 commits
2ae944b
6a9a2fc
28b5117
52d1bcb
b241f9a
35d7331
36618ae
08fb23e
d9198ab
2a1cf73
9635f2a
19bbf69
3d2e989
b5dbc89
2e4b9e1
90d4f8d
2493e33
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,19 +22,37 @@ public IActionResult Index() | |
|
||
public async Task<IActionResult> SendEmail() | ||
{ | ||
var user = new User | ||
// Email addresses have Email, DisplayName, and an enum: AddressTarget (To,Cc,Bcc, or ReplyTo) | ||
List<IEmailAddress> recipients = new List<IEmailAddress>() { | ||
new EmailAddress() { Email = "[email protected]", DisplayName = "Samuel", AddressAs = AddressTarget.To }, | ||
new EmailAddress() { Email = "[email protected]", DisplayName = "Bob", AddressAs = AddressTarget.Cc }, | ||
new EmailAddress() { Email = "[email protected]", DisplayName="George Jones", AddressAs= AddressTarget.ReplyTo } | ||
}; | ||
|
||
|
||
// example of how to add a simple attachment. Add images, streams, etc as byte arrays, for example: | ||
|
||
MimeKit.AttachmentCollection attachments = new MimeKit.AttachmentCollection | ||
{ | ||
Email = "[email protected]", | ||
DisplayName = "John Doe" | ||
{ "sample_attachment.txt", System.Text.Encoding.UTF8.GetBytes("This is the content of the file attachment.") } | ||
}; | ||
|
||
// the Reply-To addresses are simply another list of IEmailAddress objects, here, were are ignoring them as null. | ||
// Also, in the From address, the AddressAs property is ignored, and the From address is positional and always treated as the From. | ||
// Likewise, a From enumeration value in the recipients list is ignored, and will be treated as a To address. | ||
await this.emailSender.SendEmailAsync(new EmailAddress() { Email="[email protected]", DisplayName="Me", AddressAs=AddressTarget.From }, "A simple message","This is a test message", recipients, attachments); | ||
|
||
|
||
// Here is a second send example. No attachments, but using templates: | ||
|
||
recipients.Clear(); | ||
recipients.Add(new EmailAddress { Email="[email protected]", DisplayName="George Jones", AddressAs=AddressTarget.To }); | ||
var context = new | ||
{ | ||
ApplicationName = "Email Sender Sample", | ||
User = user | ||
User = recipients | ||
}; | ||
|
||
await this.emailSender.SendTemplatedEmailAsync("Invitation", context, user); | ||
await this.emailSender.SendTemplatedEmailAsync("Invitation", context, recipients ); | ||
|
||
return RedirectToAction("Index"); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,13 @@ | ||
namespace GeekLearning.Email | ||
namespace GeekLearning.Email | ||
{ | ||
public enum AddressTarget { From, To, Cc, Bcc, ReplyTo} | ||
|
||
public interface IEmailAddress | ||
{ | ||
string Email { get; } | ||
|
||
string DisplayName { get; } | ||
|
||
AddressTarget AddressAs { get; } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,18 @@ | ||
namespace GeekLearning.Email | ||
namespace GeekLearning.Email | ||
{ | ||
using System.Collections.Generic; | ||
using System.Threading.Tasks; | ||
|
||
using MimeKit; | ||
|
||
public interface IEmailProvider | ||
{ | ||
Task SendEmailAsync(IEmailAddress from, IEnumerable<IEmailAddress> recipients, string subject, string bodyText, string bodyHtml); | ||
Task SendEmailAsync( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My idea was to be able to define a complete email transaction in one call. You might want to send to two (or more) different addresses, Cc: another, and Bcc: yet another, and also have a ReplyTo: for the message that is different that the From: address. (some hosting services required the From address to match the authentication user name, which might not be the same as the 'sender' of the immediate message). Using an attribute parameter for the email address implies a single target transaction. Slower if the app wants to send to several targets. |
||
IEmailAddress from, | ||
IEnumerable<IEmailAddress> recipients, | ||
string subject, | ||
string bodyText, | ||
string bodyHtml | ||
AttachmentCollection attachments=null); | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,12 +4,12 @@ | |
|
||
public interface IEmailSender | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. there should be additional overloads using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's a thought. But there needs to be a solid way to link an AddressTarget to an email address. And how does it look if we have multiple email addresses in the call? |
||
{ | ||
Task SendEmailAsync(string subject, string message, params IEmailAddress[] to); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should not remove the params commodity. |
||
Task SendEmailAsync(string subject, string message, IEnumerable<IEmailAddress> recipients, AttachmentCollection attachments=null); | ||
|
||
Task SendEmailAsync(IEmailAddress from, string subject, string message, params IEmailAddress[] to); | ||
Task SendEmailAsync(IEmailAddress from, string subject, string message, IEnumerable<IEmailAddress> recipients, AttachmentCollection attachments=null); | ||
|
||
Task SendTemplatedEmailAsync<T>(string templateKey, T context, params IEmailAddress[] to); | ||
Task SendTemplatedEmailAsync<T>(string templateKey, T context, IEnumerable<IEmailAddress> recipients, AttachmentCollection attachments=null); | ||
|
||
Task SendTemplatedEmailAsync<T>(IEmailAddress from, string templateKey, T context, params IEmailAddress[] to); | ||
Task SendTemplatedEmailAsync<T>(IEmailAddress from, string templateKey, T context, IEnumerable<IEmailAddress> recipients, AttachmentCollection attachments=null); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,14 +6,17 @@ public EmailAddress() | |
{ | ||
} | ||
|
||
public EmailAddress(string email, string displayName) | ||
public EmailAddress(string email, string displayName, AddressTarget addressAs=AddressTarget.To) | ||
{ | ||
this.Email = email; | ||
this.DisplayName = displayName; | ||
this.AddressAs = addressAs; | ||
} | ||
|
||
public string Email { get; set; } | ||
|
||
public string DisplayName { get; set; } | ||
|
||
public AddressTarget AddressAs { get; set; } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we don't want that. |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ | |
using System.Linq; | ||
using System.Threading.Tasks; | ||
using Templating; | ||
using System.Text.RegularExpressions; | ||
|
||
public class EmailSender : IEmailSender | ||
{ | ||
|
@@ -43,27 +44,28 @@ public EmailSender( | |
} | ||
} | ||
|
||
public Task SendEmailAsync(string subject, string message, params IEmailAddress[] to) | ||
public Task SendEmailAsync(string subject, string message, IEnumerable<IEmailAddress> to, MimeKit.AttachmentCollection attachments) | ||
{ | ||
return this.SendEmailAsync(options.DefaultSender, subject, message, to); | ||
return this.SendEmailAsync(options.DefaultSender, subject, message, to, attachments); | ||
} | ||
|
||
public Task SendEmailAsync(IEmailAddress from, string subject, string message, params IEmailAddress[] to) | ||
public Task SendEmailAsync(IEmailAddress from, string subject, string message, IEnumerable<IEmailAddress> to, MimeKit.AttachmentCollection attachments) | ||
{ | ||
return DoMockupAndSendEmailAsync( | ||
from, | ||
to, | ||
subject, | ||
message, | ||
string.Format("<html><header></header><body>{0}</body></html>", message)); | ||
string.Format("<html><header></header><body>{0}</body></html>", message), | ||
attachments); | ||
} | ||
|
||
public Task SendTemplatedEmailAsync<T>(string templateKey, T context, params IEmailAddress[] to) | ||
public Task SendTemplatedEmailAsync<T>(string templateKey, T context, IEnumerable<IEmailAddress> to, MimeKit.AttachmentCollection attachments) | ||
{ | ||
return this.SendTemplatedEmailAsync(options.DefaultSender, templateKey, context, to); | ||
return this.SendTemplatedEmailAsync(options.DefaultSender, templateKey, context, to, attachments); | ||
} | ||
|
||
public async Task SendTemplatedEmailAsync<T>(IEmailAddress from, string templateKey, T context, params IEmailAddress[] to) | ||
public async Task SendTemplatedEmailAsync<T>(IEmailAddress from, string templateKey, T context, IEnumerable<IEmailAddress> to, MimeKit.AttachmentCollection attachments) | ||
{ | ||
var subjectTemplate = await this.GetTemplateAsync(templateKey, EmailTemplateType.Subject); | ||
var textTemplate = await this.GetTemplateAsync(templateKey, EmailTemplateType.BodyText); | ||
|
@@ -74,7 +76,8 @@ await this.DoMockupAndSendEmailAsync( | |
to, | ||
subjectTemplate.Apply(context), | ||
textTemplate.Apply(context), | ||
htmlTemplate.Apply(context)); | ||
htmlTemplate.Apply(context), | ||
attachments); | ||
} | ||
|
||
private Task<ITemplate> GetTemplateAsync(string templateKey, EmailTemplateType templateType) | ||
|
@@ -87,7 +90,8 @@ private async Task DoMockupAndSendEmailAsync( | |
IEnumerable<IEmailAddress> recipients, | ||
string subject, | ||
string text, | ||
string html) | ||
string html, | ||
MimeKit.AttachmentCollection attachments) | ||
{ | ||
var finalRecipients = new List<IEmailAddress>(); | ||
var mockedUpRecipients = new List<IEmailAddress>(); | ||
|
@@ -96,12 +100,15 @@ private async Task DoMockupAndSendEmailAsync( | |
{ | ||
foreach (var recipient in recipients) | ||
{ | ||
var emailParts = recipient.Email.Split('@'); | ||
if (emailParts.Length != 2) | ||
|
||
string trimmedEmail = recipient.Email.Trim(); | ||
// not sure if it's the most friendly to validate in the sender method. Should be left to caller code, IMO | ||
if (Regex.IsMatch(trimmedEmail, @"\A(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)\Z", RegexOptions.IgnoreCase).Equals(false)) | ||
|
||
{ | ||
throw new NotSupportedException("Bad recipient email."); | ||
} | ||
|
||
var emailParts = trimmedEmail.Split('@'); | ||
var domain = emailParts[1]; | ||
|
||
if (!this.options.Mockup.Exceptions.Emails.Contains(recipient.Email) | ||
|
@@ -142,7 +149,7 @@ await this.provider.SendEmailAsync( | |
finalRecipients, | ||
subject, | ||
text, | ||
html); | ||
html, attachments); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -55,17 +55,14 @@ public async Task SendNotification1(string storeName) | |
new SendGrid.SendGridEmailProviderType(), | ||
}; | ||
|
||
var emailSender = new Internal.EmailSender( | ||
providerTypes, | ||
options, | ||
this.storeFixture.Services.GetRequiredService<IStorageFactory>(), | ||
this.storeFixture.Services.GetRequiredService<ITemplateLoaderFactory>()); | ||
List<IEmailAddress> address = new List<IEmailAddress>() { | ||
new Internal.EmailAddress(){ | ||
DisplayName = "test user", | ||
Email = "[email protected]", | ||
AddressAs = AddressTarget.To} | ||
}; | ||
|
||
await emailSender.SendTemplatedEmailAsync("Notification1", new { }, new Internal.EmailAddress | ||
{ | ||
DisplayName = "test user", | ||
Email = "[email protected]" | ||
}); | ||
await emailSender.SendTemplatedEmailAsync("Notification1", new { }, address, null); | ||
} | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IEmailAddress
is meant to be a simple interface consumer can implement on their on types (or through a facade) so they can use such objects directly.I think
AddressTarget
should be an argument forIEmailSender
methods overloads, not a part of this interface.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand your idea about IEmailAddress, just defining the conventional "name [email protected]" definition. So, what might be an elegant way to link the email user with their 'AddressTarget' attributes? I think I was guessing your thinking when I made the AddressAs property optional. It can be left out, and will default to a 'To:' address insertion. But to avoid the original params implementation (which gets awkward if you want to define 5-10 or more recipients!), and keep the original simplicity of the IEmailAddress contract, maybe there has to have as inputs some kind of User structure, in which the IEmailAddress is itself a property? or overload the class methods? It would be nice to avoid too much of that.