diff --git a/.gitignore b/.gitignore index 83443355..e704f070 100644 --- a/.gitignore +++ b/.gitignore @@ -242,3 +242,4 @@ ModelManifest.xml # FAKE - F# Make .fake/ .vs +/.idea/ diff --git a/src/FluentEmail.Core/Email.cs b/src/FluentEmail.Core/Email.cs index fb20eab0..06c69a6e 100644 --- a/src/FluentEmail.Core/Email.cs +++ b/src/FluentEmail.Core/Email.cs @@ -41,8 +41,10 @@ public Email(ITemplateRenderer renderer, ISender sender) /// /// Email address to send from /// Name to send from - public Email(string emailAddress, string name = "") - : this(DefaultRenderer, DefaultSender, emailAddress, name) { } + public Email(string emailAddress, string name = "") + : this(DefaultRenderer, DefaultSender, emailAddress, name) + { + } /// /// Creates a new Email instance using the given engines and mailing address. @@ -57,6 +59,7 @@ public Email(ITemplateRenderer renderer, ISender sender, string emailAddress, st { FromAddress = new Address() {EmailAddress = emailAddress, Name = name} }; + Renderer = renderer; Sender = sender; } @@ -86,6 +89,7 @@ public static IFluentEmail From(string emailAddress, string name = null) public IFluentEmail SetFrom(string emailAddress, string name = null) { Data.FromAddress = new Address(emailAddress, name ?? ""); + return this; } @@ -102,13 +106,16 @@ public IFluentEmail To(string emailAddress, string name = null) //email address has semi-colon, try split var nameSplit = name?.Split(';') ?? new string [0]; var addressSplit = emailAddress.Split(';'); + for (int i = 0; i < addressSplit.Length; i++) { var currentName = string.Empty; + if ((nameSplit.Length - 1) >= i) { currentName = nameSplit[i]; } + Data.ToAddresses.Add(new Address(addressSplit[i].Trim(), currentName.Trim())); } } @@ -116,6 +123,7 @@ public IFluentEmail To(string emailAddress, string name = null) { Data.ToAddresses.Add(new Address(emailAddress.Trim(), name?.Trim())); } + return this; } @@ -152,6 +160,7 @@ public IFluentEmail To(IList
mailAddresses) { Data.ToAddresses.Add(address); } + return this; } @@ -164,6 +173,7 @@ public IFluentEmail To(IList
mailAddresses) public IFluentEmail CC(string emailAddress, string name = "") { Data.CcAddresses.Add(new Address(emailAddress, name)); + return this; } @@ -178,6 +188,7 @@ public IFluentEmail CC(IList
mailAddresses) { Data.CcAddresses.Add(address); } + return this; } @@ -190,6 +201,7 @@ public IFluentEmail CC(IList
mailAddresses) public IFluentEmail BCC(string emailAddress, string name = "") { Data.BccAddresses.Add(new Address(emailAddress, name)); + return this; } @@ -204,6 +216,7 @@ public IFluentEmail BCC(IList
mailAddresses) { Data.BccAddresses.Add(address); } + return this; } @@ -240,6 +253,7 @@ public IFluentEmail ReplyTo(string address, string name) public IFluentEmail Subject(string subject) { Data.Subject = subject; + return this; } @@ -252,9 +266,10 @@ public IFluentEmail Body(string body, bool isHtml = false) { Data.IsHtml = isHtml; Data.Body = body; + return this; - } - + } + /// /// Adds a Plaintext alternative Body to the Email. Used in conjunction with an HTML email, /// this allows for email readers without html capability, and also helps avoid spam filters. @@ -263,6 +278,7 @@ public IFluentEmail Body(string body, bool isHtml = false) public IFluentEmail PlaintextAlternativeBody(string body) { Data.PlaintextAlternativeBody = body; + return this; } @@ -272,6 +288,7 @@ public IFluentEmail PlaintextAlternativeBody(string body) public IFluentEmail HighPriority() { Data.Priority = Priority.High; + return this; } @@ -281,6 +298,7 @@ public IFluentEmail HighPriority() public IFluentEmail LowPriority() { Data.Priority = Priority.Low; + return this; } @@ -290,6 +308,7 @@ public IFluentEmail LowPriority() public IFluentEmail UsingTemplateEngine(ITemplateRenderer renderer) { Renderer = renderer; + return this; } @@ -329,7 +348,6 @@ public IFluentEmail PlaintextAlternativeUsingTemplateFromEmbedded(string path return this; } - /// /// Adds the template file to the email /// @@ -385,6 +403,7 @@ public IFluentEmail PlaintextAlternativeUsingTemplateFromFile(string filename public IFluentEmail UsingCultureTemplateFromFile(string filename, T model, CultureInfo culture, bool isHtml = true) { var cultureFile = GetCultureFileName(filename, culture); + return UsingTemplateFromFile(cultureFile, model, isHtml); } @@ -398,6 +417,7 @@ public IFluentEmail UsingCultureTemplateFromFile(string filename, T model, Cu public IFluentEmail PlaintextAlternativeUsingCultureTemplateFromFile(string filename, T model, CultureInfo culture) { var cultureFile = GetCultureFileName(filename, culture); + return PlaintextAlternativeUsingTemplateFromFile(cultureFile, model); } @@ -457,18 +477,22 @@ public IFluentEmail Attach(IList attachments) { Data.Attachments.Add(attachment); } + return this; } - public IFluentEmail AttachFromFilename(string filename, string contentType = null, string attachmentName = null) + public IFluentEmail AttachFromFilename(string filename, string contentType = null, string attachmentName = null) { var stream = File.OpenRead(filename); - Attach(new Attachment() - { - Data = stream, - Filename = attachmentName ?? filename, - ContentType = contentType - }); + + Attach( + new Attachment() + { + Data = stream, + Filename = attachmentName ?? filename, + ContentType = contentType + } + ); return this; } @@ -492,6 +516,30 @@ public IFluentEmail Header(string header, string body) return this; } + public IFluentEmail MailGunTemplate(string mailGunTemplate) + { + Data.MailGunTemplate = mailGunTemplate; + + return this; + } + + public IFluentEmail MailGunTemplateVar(string key, string value) + { + Data.MailGunTemplateVars.Add(key, value); + + return this; + } + + public IFluentEmail MailGunTemplateVar(Dictionary vars) + { + foreach (var var in vars) + { + Data.MailGunTemplateVars.Add(var.Key, var.Value); + } + + return this; + } + /// /// Sends email synchronously /// @@ -512,6 +560,7 @@ private static string GetCultureFileName(string fileName, CultureInfo culture) var cultureExtension = string.Format("{0}{1}", culture.Name, extension); var cultureFile = Path.ChangeExtension(fileName, cultureExtension); + if (File.Exists(cultureFile)) return cultureFile; else diff --git a/src/FluentEmail.Core/IFluentEmail.cs b/src/FluentEmail.Core/IFluentEmail.cs index d0dfc65e..2ad14efe 100644 --- a/src/FluentEmail.Core/IFluentEmail.cs +++ b/src/FluentEmail.Core/IFluentEmail.cs @@ -241,5 +241,27 @@ public interface IFluentEmail: IHideObjectMembers /// value of the header /// Instance of the Email class IFluentEmail Header(string header, string body); - } + + /// + /// Define mailgun template to use. Use this option, cause ignore body defined. + /// + /// Name of template to use. + /// Instance of the Email class + IFluentEmail MailGunTemplate(string mailGunTemplate); + + /// + /// Adds mailgun template vars. + /// + /// Name of template var. + /// Value of template var. + /// Instance of the Email class + IFluentEmail MailGunTemplateVar(string key, string value); + + /// + /// Adds mailgun template vars. + /// + /// List of template vars. + /// Instance of the Email class + IFluentEmail MailGunTemplateVar(Dictionary vars); + } } diff --git a/src/FluentEmail.Core/Models/EmailData.cs b/src/FluentEmail.Core/Models/EmailData.cs index f1a4fb8c..42513b74 100644 --- a/src/FluentEmail.Core/Models/EmailData.cs +++ b/src/FluentEmail.Core/Models/EmailData.cs @@ -16,9 +16,13 @@ public class EmailData public Priority Priority { get; set; } public List Tags { get; set; } + public string MailGunTemplate { get; set; } + public bool IsHtml { get; set; } public Dictionary Headers { get; set; } + public Dictionary MailGunTemplateVars { get; set; } + public EmailData() { ToAddresses = new List
(); @@ -28,6 +32,8 @@ public EmailData() Attachments = new List(); Tags = new List(); Headers = new Dictionary(); + MailGunTemplate = string.Empty; + MailGunTemplateVars = new Dictionary(); } } } diff --git a/src/Senders/FluentEmail.Mailgun/MailgunSender.cs b/src/Senders/FluentEmail.Mailgun/MailgunSender.cs index 0c42044e..a96ec0f8 100644 --- a/src/Senders/FluentEmail.Mailgun/MailgunSender.cs +++ b/src/Senders/FluentEmail.Mailgun/MailgunSender.cs @@ -24,18 +24,22 @@ public MailgunSender(string domainName, string apiKey, MailGunRegion mailGunRegi _domainName = domainName; _apiKey = apiKey; string url = string.Empty; - switch(mailGunRegion) + + switch (mailGunRegion) { case MailGunRegion.USA: url = $"https://api.mailgun.net/v3/{_domainName}/"; + break; case MailGunRegion.EU: url = $"https://api.eu.mailgun.net/v3/{_domainName}/"; + break; default: throw new ArgumentException($"'{mailGunRegion}' is not a valid value for {nameof(mailGunRegion)}"); } + _httpClient = new HttpClient() { BaseAddress = new Uri(url) @@ -54,71 +58,97 @@ public async Task SendAsync(IFluentEmail email, CancellationToken? var parameters = new List>(); parameters.Add(new KeyValuePair("from", $"{email.Data.FromAddress.Name} <{email.Data.FromAddress.EmailAddress}>")); - email.Data.ToAddresses.ForEach(x => { - parameters.Add(new KeyValuePair("to", $"{x.Name} <{x.EmailAddress}>")); - }); - email.Data.CcAddresses.ForEach(x => { - parameters.Add(new KeyValuePair("cc", $"{x.Name} <{x.EmailAddress}>")); - }); - email.Data.BccAddresses.ForEach(x => { - parameters.Add(new KeyValuePair("bcc", $"{x.Name} <{x.EmailAddress}>")); - }); - email.Data.ReplyToAddresses.ForEach(x => { - parameters.Add(new KeyValuePair("h:Reply-To", $"{x.Name} <{x.EmailAddress}>")); - }); + email.Data.ToAddresses.ForEach(x => { parameters.Add(new KeyValuePair("to", $"{x.Name} <{x.EmailAddress}>")); }); + email.Data.CcAddresses.ForEach(x => { parameters.Add(new KeyValuePair("cc", $"{x.Name} <{x.EmailAddress}>")); }); + email.Data.BccAddresses.ForEach(x => { parameters.Add(new KeyValuePair("bcc", $"{x.Name} <{x.EmailAddress}>")); }); + email.Data.ReplyToAddresses.ForEach(x => { parameters.Add(new KeyValuePair("h:Reply-To", $"{x.Name} <{x.EmailAddress}>")); }); parameters.Add(new KeyValuePair("subject", email.Data.Subject)); - parameters.Add(new KeyValuePair(email.Data.IsHtml ? "html" : "text", email.Data.Body)); - - if (!string.IsNullOrEmpty(email.Data.PlaintextAlternativeBody)) + if (!string.IsNullOrEmpty(email.Data.MailGunTemplate)) { - parameters.Add(new KeyValuePair("text", email.Data.PlaintextAlternativeBody)); + parameters.Add(new KeyValuePair("template", email.Data.MailGunTemplate)); } - - email.Data.Tags.ForEach(x => + else { - parameters.Add(new KeyValuePair("o:tag", x)); - }); + parameters.Add( + new KeyValuePair( + email.Data.IsHtml ? + "html" : + "text", + email.Data.Body + ) + ); + + if (!string.IsNullOrEmpty(email.Data.PlaintextAlternativeBody)) + { + parameters.Add(new KeyValuePair("text", email.Data.PlaintextAlternativeBody)); + } + } + + email.Data.Tags.ForEach(x => { parameters.Add(new KeyValuePair("o:tag", x)); }); foreach (var emailHeader in email.Data.Headers) { var key = emailHeader.Key; + if (!key.StartsWith("h:")) { key = "h:" + emailHeader.Key; } - parameters.Add(new KeyValuePair(key, emailHeader.Value)); + parameters.Add(new KeyValuePair(key, emailHeader.Value)); } - var files = new List(); - email.Data.Attachments.ForEach(x => + if (email.Data.MailGunTemplateVars.Any()) { - string param; + var builder = new StringBuilder(); + + foreach (var var in email.Data.MailGunTemplateVars) + { + builder.Append($"\"{var.Key}\":\"{var.Value}\""); + builder.Append(","); + } - if (x.IsInline) - param = "inline"; - else - param = "attachment"; + var variables = $"{{{builder.ToString().Trim(',')}}}"; + + parameters.Add(new KeyValuePair("h:X-Mailgun-Variables", variables)); + } - files.Add(new HttpFile() + var files = new List(); + + email.Data.Attachments.ForEach( + x => { - ParameterName = param, - Data = x.Data, - Filename = x.Filename, - ContentType = x.ContentType - }); - }); + string param; + + if (x.IsInline) + param = "inline"; + else + param = "attachment"; + + files.Add( + new HttpFile() + { + ParameterName = param, + Data = x.Data, + Filename = x.Filename, + ContentType = x.ContentType + } + ); + } + ); var response = await _httpClient.PostMultipart("messages", parameters, files).ConfigureAwait(false); var result = new SendResponse {MessageId = response.Data?.Id}; + if (!response.Success) { result.ErrorMessages.AddRange(response.Errors.Select(x => x.ErrorMessage)); + return result; } - + return result; } }