using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Threading.Tasks; using Nop.Core; using Nop.Core.Domain.Blogs; using Nop.Core.Domain.Catalog; using Nop.Core.Domain.Common; using Nop.Core.Domain.Customers; using Nop.Core.Domain.Forums; using Nop.Core.Domain.Messages; using Nop.Core.Domain.News; using Nop.Core.Domain.Orders; using Nop.Core.Domain.Shipping; using Nop.Core.Domain.Vendors; using Nop.Core.Events; using Nop.Services.Affiliates; using Nop.Services.Catalog; using Nop.Services.Common; using Nop.Services.Customers; using Nop.Services.Localization; using Nop.Services.Orders; using Nop.Services.Stores; using Nop.Services.Messages; namespace Nop.Plugin.Misc.BillingoInvoicing.Services { /// /// Workflow message service /// public partial class BillingoEmailMessageService { #region Fields private readonly CommonSettings _commonSettings; private readonly EmailAccountSettings _emailAccountSettings; private readonly IAddressService _addressService; private readonly IAffiliateService _affiliateService; private readonly ICustomerService _customerService; private readonly IEmailAccountService _emailAccountService; private readonly IEventPublisher _eventPublisher; private readonly ILanguageService _languageService; private readonly ILocalizationService _localizationService; private readonly IMessageTemplateService _messageTemplateService; private readonly IMessageTokenProvider _messageTokenProvider; private readonly IOrderService _orderService; private readonly IProductService _productService; private readonly IQueuedEmailService _queuedEmailService; private readonly IStoreContext _storeContext; private readonly IStoreService _storeService; private readonly ITokenizer _tokenizer; private readonly MessagesSettings _messagesSettings; #endregion #region Ctor public BillingoEmailMessageService(CommonSettings commonSettings, EmailAccountSettings emailAccountSettings, IAddressService addressService, IAffiliateService affiliateService, ICustomerService customerService, IEmailAccountService emailAccountService, IEventPublisher eventPublisher, ILanguageService languageService, ILocalizationService localizationService, IMessageTemplateService messageTemplateService, IMessageTokenProvider messageTokenProvider, IOrderService orderService, IProductService productService, IQueuedEmailService queuedEmailService, IStoreContext storeContext, IStoreService storeService, ITokenizer tokenizer, MessagesSettings messagesSettings) { _commonSettings = commonSettings; _emailAccountSettings = emailAccountSettings; _addressService = addressService; _affiliateService = affiliateService; _customerService = customerService; _emailAccountService = emailAccountService; _eventPublisher = eventPublisher; _languageService = languageService; _localizationService = localizationService; _messageTemplateService = messageTemplateService; _messageTokenProvider = messageTokenProvider; _orderService = orderService; _productService = productService; _queuedEmailService = queuedEmailService; _storeContext = storeContext; _storeService = storeService; _tokenizer = tokenizer; _messagesSettings = messagesSettings; } #endregion #region Utilities /// /// Get active message templates by the name /// /// Message template name /// Store identifier /// /// A task that represents the asynchronous operation /// The task result contains the list of message templates /// protected virtual async Task> GetActiveMessageTemplatesAsync(string messageTemplateName, int storeId) { //get message templates by the name var messageTemplates = await _messageTemplateService.GetMessageTemplatesByNameAsync(messageTemplateName, storeId); //no template found if (!messageTemplates?.Any() ?? true) return new List(); //filter active templates messageTemplates = messageTemplates.Where(messageTemplate => messageTemplate.IsActive).ToList(); return messageTemplates; } /// /// Get EmailAccount to use with a message templates /// /// Message template /// Language identifier /// /// A task that represents the asynchronous operation /// The task result contains the emailAccount /// protected virtual async Task GetEmailAccountOfMessageTemplateAsync(MessageTemplate messageTemplate, int languageId) { var emailAccountId = await _localizationService.GetLocalizedAsync(messageTemplate, mt => mt.EmailAccountId, languageId); //some 0 validation (for localizable "Email account" dropdownlist which saves 0 if "Standard" value is chosen) if (emailAccountId == 0) emailAccountId = messageTemplate.EmailAccountId; var emailAccount = (await _emailAccountService.GetEmailAccountByIdAsync(emailAccountId) ?? await _emailAccountService.GetEmailAccountByIdAsync(_emailAccountSettings.DefaultEmailAccountId)) ?? (await _emailAccountService.GetAllEmailAccountsAsync()).FirstOrDefault(); return emailAccount; } /// /// Ensure language is active /// /// Language identifier /// Store identifier /// /// A task that represents the asynchronous operation /// The task result contains the return a value language identifier /// protected virtual async Task EnsureLanguageIsActiveAsync(int languageId, int storeId) { //load language by specified ID var language = await _languageService.GetLanguageByIdAsync(languageId); if (language == null || !language.Published) { //load any language from the specified store language = (await _languageService.GetAllLanguagesAsync(storeId: storeId)).FirstOrDefault(); } if (language == null || !language.Published) { //load any language language = (await _languageService.GetAllLanguagesAsync()).FirstOrDefault(); } if (language == null) throw new Exception("No active language could be loaded"); return language.Id; } /// /// Get email and name to send email for store owner /// /// Message template email account /// Email address and name to send email fore store owner protected virtual async Task<(string email, string name)> GetStoreOwnerNameAndEmailAsync(EmailAccount messageTemplateEmailAccount) { var storeOwnerEmailAccount = _messagesSettings.UseDefaultEmailAccountForSendStoreOwnerEmails ? await _emailAccountService.GetEmailAccountByIdAsync(_emailAccountSettings.DefaultEmailAccountId) : null; storeOwnerEmailAccount ??= messageTemplateEmailAccount; return (storeOwnerEmailAccount.Email, storeOwnerEmailAccount.DisplayName); } #endregion #region Methods public virtual async Task SendErrorToStoreOwnerNotificationMessageAsync(string errorMessage, int languageId, Order order) { var store = await _storeContext.GetCurrentStoreAsync(); languageId = await EnsureLanguageIsActiveAsync(languageId, store.Id); //tokens var commonTokens = new List(); await _messageTokenProvider.AddOrderTokensAsync(commonTokens, order, languageId); await _messageTokenProvider.AddCustomerTokensAsync(commonTokens, order.CustomerId); var messageTemplates = await _messageTemplateService.GetMessageTemplatesByNameAsync(MessageTemplateSystemNames.ORDER_PAID_STORE_OWNER_NOTIFICATION, store.Id); var messageTemplate = messageTemplates.First(); //email account var emailAccount = await GetEmailAccountOfMessageTemplateAsync(messageTemplate, languageId); var tokens = new List(commonTokens); await _messageTokenProvider.AddStoreTokensAsync(tokens, store, emailAccount); var (toEmail, toName) = await GetStoreOwnerNameAndEmailAsync(emailAccount); return await SendNotificationAsync(errorMessage, emailAccount, languageId, tokens, toEmail, toName); } #region Common /// /// Send notification /// /// Message template /// Email account /// Language identifier /// Tokens /// Recipient email address /// Recipient name /// Attachment file path /// Attachment file name /// "Reply to" email /// "Reply to" name /// Sender email. If specified, then it overrides passed "emailAccount" details /// Sender name. If specified, then it overrides passed "emailAccount" details /// Subject. If specified, then it overrides subject of a message template /// /// A task that represents the asynchronous operation /// The task result contains the queued email identifier /// public virtual async Task SendNotificationAsync(string errorMessage, EmailAccount emailAccount, int languageId, IList tokens, string toEmailAddress, string toName, string attachmentFilePath = null, string attachmentFileName = null, string replyToEmailAddress = null, string replyToName = null, string fromEmail = null, string fromName = null, string subject = null) { if (emailAccount == null) throw new ArgumentNullException(nameof(emailAccount)); if (string.IsNullOrEmpty(subject)) subject = "BillingoInvoicing error occured"; var body = errorMessage; //Replace subject and body tokens var subjectReplaced = _tokenizer.Replace(subject, tokens, false); var bodyReplaced = _tokenizer.Replace(body, tokens, true); //limit name length toName = CommonHelper.EnsureMaximumLength(toName, 300); var email = new QueuedEmail { Priority = QueuedEmailPriority.High, From = !string.IsNullOrEmpty(fromEmail) ? fromEmail : emailAccount.Email, FromName = !string.IsNullOrEmpty(fromName) ? fromName : emailAccount.DisplayName, To = toEmailAddress, ToName = toName, ReplyTo = replyToEmailAddress, ReplyToName = replyToName, CC = string.Empty, Subject = subjectReplaced, Body = bodyReplaced, AttachmentFilePath = attachmentFilePath, AttachmentFileName = attachmentFileName, CreatedOnUtc = DateTime.UtcNow, EmailAccountId = emailAccount.Id, }; await _queuedEmailService.InsertQueuedEmailAsync(email); return email.Id; } #endregion #endregion } }