diff --git a/src/Volo.Abp.Emailing/Volo.Abp.Emailing.csproj b/src/Volo.Abp.Emailing/Volo.Abp.Emailing.csproj index 3c848d99bf..9aacc1cb1b 100644 --- a/src/Volo.Abp.Emailing/Volo.Abp.Emailing.csproj +++ b/src/Volo.Abp.Emailing/Volo.Abp.Emailing.csproj @@ -15,6 +15,7 @@ + diff --git a/src/Volo.Abp.Emailing/Volo/Abp/Emailing/AbpEmailingModule.cs b/src/Volo.Abp.Emailing/Volo/Abp/Emailing/AbpEmailingModule.cs index 5f7d5a3370..0ddd337d1a 100644 --- a/src/Volo.Abp.Emailing/Volo/Abp/Emailing/AbpEmailingModule.cs +++ b/src/Volo.Abp.Emailing/Volo/Abp/Emailing/AbpEmailingModule.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Modularity; +using Volo.Abp.Settings; namespace Volo.Abp.Emailing { @@ -7,6 +8,11 @@ namespace Volo.Abp.Emailing { public override void ConfigureServices(IServiceCollection services) { + services.Configure(options => + { + options.DefinitionProviders.Add(); + }); + services.AddAssemblyOf(); } } diff --git a/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSenderBase.cs b/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSenderBase.cs new file mode 100644 index 0000000000..59d2b7af72 --- /dev/null +++ b/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSenderBase.cs @@ -0,0 +1,121 @@ +using System; +using System.Net.Mail; +using System.Text; +using System.Threading.Tasks; + +namespace Volo.Abp.Emailing +{ + /// + /// This class can be used as base to implement . + /// + public abstract class EmailSenderBase : IEmailSender + { + public IEmailSenderConfiguration Configuration { get; } + + /// + /// Constructor. + /// + /// Configuration + protected EmailSenderBase(IEmailSenderConfiguration configuration) + { + Configuration = configuration; + } + + public virtual async Task SendAsync(string to, string subject, string body, bool isBodyHtml = true) + { + await SendAsync(new MailMessage + { + To = { to }, + Subject = subject, + Body = body, + IsBodyHtml = isBodyHtml + }); + } + + public virtual void Send(string to, string subject, string body, bool isBodyHtml = true) + { + Send(new MailMessage + { + To = { to }, + Subject = subject, + Body = body, + IsBodyHtml = isBodyHtml + }); + } + + public virtual async Task SendAsync(string from, string to, string subject, string body, bool isBodyHtml = true) + { + await SendAsync(new MailMessage(from, to, subject, body) { IsBodyHtml = isBodyHtml }); + } + + public virtual void Send(string from, string to, string subject, string body, bool isBodyHtml = true) + { + Send(new MailMessage(from, to, subject, body) { IsBodyHtml = isBodyHtml }); + } + + public virtual async Task SendAsync(MailMessage mail, bool normalize = true) + { + if (normalize) + { + NormalizeMail(mail); + } + + await SendEmailAsync(mail); + } + + public virtual void Send(MailMessage mail, bool normalize = true) + { + if (normalize) + { + NormalizeMail(mail); + } + + SendEmail(mail); + } + + /// + /// Should implement this method to send email in derived classes. + /// + /// Mail to be sent + protected abstract Task SendEmailAsync(MailMessage mail); + + /// + /// Should implement this method to send email in derived classes. + /// + /// Mail to be sent + protected abstract void SendEmail(MailMessage mail); + + /// + /// Normalizes given email. + /// Fills if it's not filled before. + /// Sets encodings to UTF8 if they are not set before. + /// + /// Mail to be normalized + protected virtual void NormalizeMail(MailMessage mail) + { + if (mail.From == null || mail.From.Address.IsNullOrEmpty()) + { + mail.From = new MailAddress( + Configuration.DefaultFromAddress, + Configuration.DefaultFromDisplayName, + Encoding.UTF8 + ); + } + + if (mail.HeadersEncoding == null) + { + mail.HeadersEncoding = Encoding.UTF8; + } + + if (mail.SubjectEncoding == null) + { + mail.SubjectEncoding = Encoding.UTF8; + } + + if (mail.BodyEncoding == null) + { + mail.BodyEncoding = Encoding.UTF8; + } + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSenderConfiguration.cs b/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSenderConfiguration.cs new file mode 100644 index 0000000000..3293692cfb --- /dev/null +++ b/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSenderConfiguration.cs @@ -0,0 +1,43 @@ +using System; +using Volo.Abp.Settings; + +namespace Volo.Abp.Emailing +{ + /// + /// Implementation of that reads settings + /// from . + /// + public abstract class EmailSenderConfiguration : IEmailSenderConfiguration + { + public virtual string DefaultFromAddress => GetNotEmptySettingValue(EmailSettingNames.DefaultFromAddress); + + public virtual string DefaultFromDisplayName => SettingManager.GetOrNull(EmailSettingNames.DefaultFromDisplayName); + + protected readonly ISettingManager SettingManager; + + /// + /// Creates a new . + /// + protected EmailSenderConfiguration(ISettingManager settingManager) + { + SettingManager = settingManager; + } + + /// + /// Gets a setting value by checking. Throws if it's null or empty. + /// + /// Name of the setting + /// Value of the setting + protected string GetNotEmptySettingValue(string name) + { + var value = SettingManager.GetOrNull(name); + + if (value.IsNullOrEmpty()) + { + throw new AbpException($"Setting value for '{name}' is null or empty!"); + } + + return value; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSettingNames.cs b/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSettingNames.cs new file mode 100644 index 0000000000..b8be36af0d --- /dev/null +++ b/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSettingNames.cs @@ -0,0 +1,59 @@ +namespace Volo.Abp.Emailing +{ + /// + /// Declares names of the settings defined by . + /// + public static class EmailSettingNames + { + /// + /// Abp.Net.Mail.DefaultFromAddress + /// + public const string DefaultFromAddress = "Abp.Net.Mail.DefaultFromAddress"; + + /// + /// Abp.Net.Mail.DefaultFromDisplayName + /// + public const string DefaultFromDisplayName = "Abp.Net.Mail.DefaultFromDisplayName"; + + /// + /// SMTP related email settings. + /// + public static class Smtp + { + /// + /// Abp.Net.Mail.Smtp.Host + /// + public const string Host = "Abp.Net.Mail.Smtp.Host"; + + /// + /// Abp.Net.Mail.Smtp.Port + /// + public const string Port = "Abp.Net.Mail.Smtp.Port"; + + /// + /// Abp.Net.Mail.Smtp.UserName + /// + public const string UserName = "Abp.Net.Mail.Smtp.UserName"; + + /// + /// Abp.Net.Mail.Smtp.Password + /// + public const string Password = "Abp.Net.Mail.Smtp.Password"; + + /// + /// Abp.Net.Mail.Smtp.Domain + /// + public const string Domain = "Abp.Net.Mail.Smtp.Domain"; + + /// + /// Abp.Net.Mail.Smtp.EnableSsl + /// + public const string EnableSsl = "Abp.Net.Mail.Smtp.EnableSsl"; + + /// + /// Abp.Net.Mail.Smtp.UseDefaultCredentials + /// + public const string UseDefaultCredentials = "Abp.Net.Mail.Smtp.UseDefaultCredentials"; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSettingProvider.cs b/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSettingProvider.cs new file mode 100644 index 0000000000..97be614a03 --- /dev/null +++ b/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSettingProvider.cs @@ -0,0 +1,26 @@ +using Volo.Abp.Settings; + +namespace Volo.Abp.Emailing +{ + /// + /// Defines settings to send emails. + /// for all available configurations. + /// + internal class EmailSettingProvider : SettingDefinitionProvider + { + public override void Define(ISettingDefinitionContext context) + { + context.Add( + new SettingDefinition(EmailSettingNames.Smtp.Host, "127.0.0.1"), + new SettingDefinition(EmailSettingNames.Smtp.Port, "25"), + new SettingDefinition(EmailSettingNames.Smtp.UserName), + new SettingDefinition(EmailSettingNames.Smtp.Password), + new SettingDefinition(EmailSettingNames.Smtp.Domain), + new SettingDefinition(EmailSettingNames.Smtp.EnableSsl, "false"), + new SettingDefinition(EmailSettingNames.Smtp.UseDefaultCredentials, "true"), + new SettingDefinition(EmailSettingNames.DefaultFromAddress), + new SettingDefinition(EmailSettingNames.DefaultFromDisplayName) + ); + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Emailing/Volo/Abp/Emailing/IEmailSender.cs b/src/Volo.Abp.Emailing/Volo/Abp/Emailing/IEmailSender.cs new file mode 100644 index 0000000000..8c4b87428a --- /dev/null +++ b/src/Volo.Abp.Emailing/Volo/Abp/Emailing/IEmailSender.cs @@ -0,0 +1,51 @@ +using System.Net.Mail; +using System.Threading.Tasks; + +namespace Volo.Abp.Emailing +{ + /// + /// This service can be used simply sending emails. + /// + public interface IEmailSender + { + /// + /// Sends an email. + /// + Task SendAsync(string to, string subject, string body, bool isBodyHtml = true); + + /// + /// Sends an email. + /// + void Send(string to, string subject, string body, bool isBodyHtml = true); + + /// + /// Sends an email. + /// + Task SendAsync(string from, string to, string subject, string body, bool isBodyHtml = true); + + /// + /// Sends an email. + /// + void Send(string from, string to, string subject, string body, bool isBodyHtml = true); + + /// + /// Sends an email. + /// + /// Mail to be sent + /// + /// Should normalize email? + /// If true, it sets sender address/name if it's not set before and makes mail encoding UTF-8. + /// + void Send(MailMessage mail, bool normalize = true); + + /// + /// Sends an email. + /// + /// Mail to be sent + /// + /// Should normalize email? + /// If true, it sets sender address/name if it's not set before and makes mail encoding UTF-8. + /// + Task SendAsync(MailMessage mail, bool normalize = true); + } +} diff --git a/src/Volo.Abp.Emailing/Volo/Abp/Emailing/IEmailSenderConfiguration.cs b/src/Volo.Abp.Emailing/Volo/Abp/Emailing/IEmailSenderConfiguration.cs new file mode 100644 index 0000000000..fc997af1ce --- /dev/null +++ b/src/Volo.Abp.Emailing/Volo/Abp/Emailing/IEmailSenderConfiguration.cs @@ -0,0 +1,18 @@ +namespace Volo.Abp.Emailing +{ + /// + /// Defines configurations used while sending emails. + /// + public interface IEmailSenderConfiguration + { + /// + /// Default from address. + /// + string DefaultFromAddress { get; } + + /// + /// Default display name. + /// + string DefaultFromDisplayName { get; } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Emailing/Volo/Abp/Emailing/NullEmailSender.cs b/src/Volo.Abp.Emailing/Volo/Abp/Emailing/NullEmailSender.cs new file mode 100644 index 0000000000..baf9440e3f --- /dev/null +++ b/src/Volo.Abp.Emailing/Volo/Abp/Emailing/NullEmailSender.cs @@ -0,0 +1,49 @@ +using System.Net.Mail; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; + +namespace Volo.Abp.Emailing +{ + /// + /// This class is an implementation of as similar to null pattern. + /// It does not send emails but logs them. + /// + public class NullEmailSender : EmailSenderBase + { + public ILogger Logger { get; set; } + + /// + /// Creates a new object. + /// + /// Configuration + public NullEmailSender(IEmailSenderConfiguration configuration) + : base(configuration) + { + Logger = NullLogger.Instance; + } + + protected override Task SendEmailAsync(MailMessage mail) + { + Logger.LogWarning("USING NullEmailSender!"); + Logger.LogDebug("SendEmailAsync:"); + LogEmail(mail); + return Task.FromResult(0); + } + + protected override void SendEmail(MailMessage mail) + { + Logger.LogWarning("USING NullEmailSender!"); + Logger.LogWarning("SendEmail:"); + LogEmail(mail); + } + + private void LogEmail(MailMessage mail) + { + Logger.LogDebug(mail.To.ToString()); + Logger.LogDebug(mail.CC.ToString()); + Logger.LogDebug(mail.Subject); + Logger.LogDebug(mail.Body); + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/ISmtpEmailSender.cs b/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/ISmtpEmailSender.cs new file mode 100644 index 0000000000..989ae6e372 --- /dev/null +++ b/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/ISmtpEmailSender.cs @@ -0,0 +1,18 @@ +using System.Net.Mail; + +namespace Volo.Abp.Emailing.Smtp +{ + /// + /// Used to send emails over SMTP. + /// + public interface ISmtpEmailSender : IEmailSender + { + /// + /// Creates and configures new object to send emails. + /// + /// + /// An object that is ready to send emails. + /// + SmtpClient BuildClient(); + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/ISmtpEmailSenderConfiguration.cs b/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/ISmtpEmailSenderConfiguration.cs new file mode 100644 index 0000000000..b6abce9249 --- /dev/null +++ b/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/ISmtpEmailSenderConfiguration.cs @@ -0,0 +1,43 @@ +namespace Volo.Abp.Emailing.Smtp +{ + /// + /// Defines configurations to used by SmtpClient object. + /// + public interface ISmtpEmailSenderConfiguration : IEmailSenderConfiguration + { + /// + /// SMTP Host name/IP. + /// + string Host { get; } + + /// + /// SMTP Port. + /// + int Port { get; } + + /// + /// User name to login to SMTP server. + /// + string UserName { get; } + + /// + /// Password to login to SMTP server. + /// + string Password { get; } + + /// + /// Domain name to login to SMTP server. + /// + string Domain { get; } + + /// + /// Is SSL enabled? + /// + bool EnableSsl { get; } + + /// + /// Use default credentials? + /// + bool UseDefaultCredentials { get; } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/SmtpEmailSender.cs b/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/SmtpEmailSender.cs new file mode 100644 index 0000000000..3b45ab13ee --- /dev/null +++ b/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/SmtpEmailSender.cs @@ -0,0 +1,83 @@ +using System; +using System.Net; +using System.Net.Mail; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Emailing.Smtp +{ + /// + /// Used to send emails over SMTP. + /// + public class SmtpEmailSender : EmailSenderBase, ISmtpEmailSender, ITransientDependency + { + private readonly ISmtpEmailSenderConfiguration _configuration; + + /// + /// Creates a new . + /// + /// Configuration + public SmtpEmailSender(ISmtpEmailSenderConfiguration configuration) + : base(configuration) + { + _configuration = configuration; + } + + public SmtpClient BuildClient() + { + var host = _configuration.Host; + var port = _configuration.Port; + + var smtpClient = new SmtpClient(host, port); + try + { + if (_configuration.EnableSsl) + { + smtpClient.EnableSsl = true; + } + + if (_configuration.UseDefaultCredentials) + { + smtpClient.UseDefaultCredentials = true; + } + else + { + smtpClient.UseDefaultCredentials = false; + + var userName = _configuration.UserName; + if (!userName.IsNullOrEmpty()) + { + var password = _configuration.Password; + var domain = _configuration.Domain; + smtpClient.Credentials = !domain.IsNullOrEmpty() + ? new NetworkCredential(userName, password, domain) + : new NetworkCredential(userName, password); + } + } + + return smtpClient; + } + catch + { + smtpClient.Dispose(); + throw; + } + } + + protected override async Task SendEmailAsync(MailMessage mail) + { + using (var smtpClient = BuildClient()) + { + await smtpClient.SendMailAsync(mail); + } + } + + protected override void SendEmail(MailMessage mail) + { + using (var smtpClient = BuildClient()) + { + smtpClient.Send(mail); + } + } + } +} diff --git a/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/SmtpEmailSenderConfiguration.cs b/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/SmtpEmailSenderConfiguration.cs new file mode 100644 index 0000000000..b114cc43dd --- /dev/null +++ b/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/SmtpEmailSenderConfiguration.cs @@ -0,0 +1,33 @@ +using System; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Settings; + +namespace Volo.Abp.Emailing.Smtp +{ + /// + /// Implementation of that reads settings + /// from . + /// + public class SmtpEmailSenderConfiguration : EmailSenderConfiguration, ISmtpEmailSenderConfiguration, ITransientDependency + { + public virtual string Host => GetNotEmptySettingValue(EmailSettingNames.Smtp.Host); + + public virtual int Port => GetNotEmptySettingValue(EmailSettingNames.Smtp.Port).To(); + + public virtual string UserName => GetNotEmptySettingValue(EmailSettingNames.Smtp.UserName); + + public virtual string Password => GetNotEmptySettingValue(EmailSettingNames.Smtp.Password); + + public virtual string Domain => SettingManager.GetOrNull(EmailSettingNames.Smtp.Domain); + + public virtual bool EnableSsl => SettingManager.GetOrNull(EmailSettingNames.Smtp.EnableSsl).To(); + + public virtual bool UseDefaultCredentials => SettingManager.GetOrNull(EmailSettingNames.Smtp.UseDefaultCredentials).To(); + + public SmtpEmailSenderConfiguration(ISettingManager settingManager) + : base(settingManager) + { + + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingManagerSyncExtensions.cs b/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingManagerSyncExtensions.cs index f9fa53ea4c..da45dd2c1e 100644 --- a/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingManagerSyncExtensions.cs +++ b/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingManagerSyncExtensions.cs @@ -1,8 +1,23 @@ -namespace Volo.Abp.Settings +using System.Collections.Generic; +using JetBrains.Annotations; +using Volo.Abp.Threading; + +namespace Volo.Abp.Settings { public static class SettingManagerSyncExtensions { - //TODO: Add sync extension methods for all setting manager methods. - //TODO: Also add sync extension methods for all value provider extensions (like GlobalSettingManagerExtensions). + public static string GetOrNull([NotNull] this ISettingManager settingManager, [NotNull] string name) + { + Check.NotNull(settingManager, nameof(settingManager)); + + return AsyncHelper.RunSync(() => settingManager.GetOrNullAsync(name)); + } + + public static List GetAll([NotNull] this ISettingManager settingManager) + { + Check.NotNull(settingManager, nameof(settingManager)); + + return AsyncHelper.RunSync(settingManager.GetAllAsync); + } } }