diff --git a/src/Business/Grand.Business.Checkout/Commands/Handlers/Orders/OrderNotificationCommandHandler.cs b/src/Business/Grand.Business.Checkout/Commands/Handlers/Orders/OrderNotificationCommandHandler.cs index 4013d2b751..dd5618da91 100644 --- a/src/Business/Grand.Business.Checkout/Commands/Handlers/Orders/OrderNotificationCommandHandler.cs +++ b/src/Business/Grand.Business.Checkout/Commands/Handlers/Orders/OrderNotificationCommandHandler.cs @@ -60,7 +60,7 @@ await _orderService.InsertOrderNote(new OrderNote { //send email notifications await _messageProviderService.SendOrderPlacedStoreOwnerMessage(request.Order, request.Customer, - _languageSettings.DefaultAdminLanguageId); + _languageSettings.DefaultAdminLanguageId, request.Host); string orderPlacedAttachmentFilePath = string.Empty, orderPlacedAttachmentFileName = string.Empty; var orderPlacedAttachments = new List(); @@ -87,14 +87,16 @@ await _pdfService.SaveOrderToBinary(request.Order, request.Order.CustomerLanguag _logger.LogError(ex, "Error - order placed attachment file {OrderOrderNumber}", request.Order.OrderNumber); } - await _messageProviderService.SendOrderPlacedCustomerMessage(request.Order, request.Customer, request.Order.CustomerLanguageId, orderPlacedAttachmentFilePath, orderPlacedAttachmentFileName, orderPlacedAttachments); + await _messageProviderService.SendOrderPlacedCustomerMessage(request.Order, request.Customer, + request.Order.CustomerLanguageId, orderPlacedAttachmentFilePath, orderPlacedAttachmentFileName, + orderPlacedAttachments, request.Host); if (request.Order.OrderItems.Any(x => !string.IsNullOrEmpty(x.VendorId))) { var vendors = await _mediator.Send(new GetVendorsInOrderQuery { Order = request.Order }); foreach (var vendor in vendors) await _messageProviderService.SendOrderPlacedVendorMessage(request.Order, request.Customer, vendor, - _languageSettings.DefaultAdminLanguageId); + _languageSettings.DefaultAdminLanguageId, request.Host); } } catch (Exception e) diff --git a/src/Business/Grand.Business.Checkout/Commands/Handlers/Orders/PlaceOrderCommandHandler.cs b/src/Business/Grand.Business.Checkout/Commands/Handlers/Orders/PlaceOrderCommandHandler.cs index 01527bb29d..389098fd66 100644 --- a/src/Business/Grand.Business.Checkout/Commands/Handlers/Orders/PlaceOrderCommandHandler.cs +++ b/src/Business/Grand.Business.Checkout/Commands/Handlers/Orders/PlaceOrderCommandHandler.cs @@ -184,7 +184,8 @@ await UpdatePaymentTransaction(processPayment.paymentTransaction, result.PlacedO await _mediator.Send(new OrderNotificationCommand { Order = result.PlacedOrder, Customer = details.Customer, - OriginalCustomerIfImpersonated = command.OriginalCustomerIfImpersonated + OriginalCustomerIfImpersonated = command.OriginalCustomerIfImpersonated, + Host = command.Host }, cancellationToken); //check order status diff --git a/src/Business/Grand.Business.Core/Commands/Messages/Common/OrderNotificationCommand.cs b/src/Business/Grand.Business.Core/Commands/Messages/Common/OrderNotificationCommand.cs index e451bac685..85bdf36387 100644 --- a/src/Business/Grand.Business.Core/Commands/Messages/Common/OrderNotificationCommand.cs +++ b/src/Business/Grand.Business.Core/Commands/Messages/Common/OrderNotificationCommand.cs @@ -1,5 +1,6 @@ using Grand.Domain.Customers; using Grand.Domain.Orders; +using Grand.Domain.Stores; using Grand.Mediator; namespace Grand.Business.Core.Commands.Messages.Common; @@ -17,4 +18,9 @@ public class OrderNotificationCommand : IRequest /// Staff member who placed the order while impersonating the customer, if any /// public Customer OriginalCustomerIfImpersonated { get; set; } + + /// + /// Host of the storefront request that placed the order; the messages link to it + /// + public DomainHost Host { get; set; } } diff --git a/src/Business/Grand.Business.Core/Interfaces/Marketing/Contacts/IContactAttributeParser.cs b/src/Business/Grand.Business.Core/Interfaces/Marketing/Contacts/IContactAttributeParser.cs index a5dc53b7ab..a654691ba8 100644 --- a/src/Business/Grand.Business.Core/Interfaces/Marketing/Contacts/IContactAttributeParser.cs +++ b/src/Business/Grand.Business.Core/Interfaces/Marketing/Contacts/IContactAttributeParser.cs @@ -1,7 +1,9 @@ +using Grand.Business.Core.Utilities.Catalog; using Grand.Domain.Common; using Grand.Domain.Customers; using Grand.Domain.Localization; using Grand.Domain.Messages; +using Grand.Domain.Stores; namespace Grand.Business.Core.Interfaces.Marketing.Contacts; @@ -55,7 +57,7 @@ IList AddContactAttribute(IList customAttribut /// /// Language /// Attributes - /// Customer + /// Store host the file upload links point to; without one the file is named but not linked /// Separator /// A value indicating whether to encode (HTML) values /// A value indicating whether to HTML hyperlink tags could be rendered (if required) @@ -63,7 +65,7 @@ IList AddContactAttribute(IList customAttribut Task FormatAttributes( Language language, IList customAttributes, - Customer customer, + DomainHost host, string separator = "
", bool htmlEncode = true, bool allowHyperlinks = true); @@ -71,7 +73,8 @@ Task FormatAttributes( /// /// Get parse contact attributes /// - /// - /// - Task> GetParseContactAttributes(IList model); + /// Posted attributes + /// Customer whose groups limit the attributes and the store + /// Selected attributes whose conditions are met + Task> GetParseContactAttributes(IList model, CatalogAccess access); } \ No newline at end of file diff --git a/src/Business/Grand.Business.Core/Interfaces/Marketing/Contacts/IContactAttributeService.cs b/src/Business/Grand.Business.Core/Interfaces/Marketing/Contacts/IContactAttributeService.cs index 42f9a4aa35..66a0b8c26c 100644 --- a/src/Business/Grand.Business.Core/Interfaces/Marketing/Contacts/IContactAttributeService.cs +++ b/src/Business/Grand.Business.Core/Interfaces/Marketing/Contacts/IContactAttributeService.cs @@ -1,3 +1,4 @@ +using Grand.Business.Core.Utilities.Catalog; using Grand.Domain.Messages; namespace Grand.Business.Core.Interfaces.Marketing.Contacts; @@ -18,10 +19,9 @@ public interface IContactAttributeService /// /// Gets all Contact attributes /// - /// Store identifier - /// Ignore acl + /// Customer whose groups limit the result and the store, or every attribute of the store /// Contact attributes - Task> GetAllContactAttributes(string storeId = "", bool ignoreAcl = false); + Task> GetAllContactAttributes(CatalogAccess access); /// /// Gets a Contact attribute diff --git a/src/Business/Grand.Business.Core/Interfaces/Marketing/Customers/ICustomerCoordinatesService.cs b/src/Business/Grand.Business.Core/Interfaces/Marketing/Customers/ICustomerCoordinatesService.cs index 008e35bf6e..65f506715a 100644 --- a/src/Business/Grand.Business.Core/Interfaces/Marketing/Customers/ICustomerCoordinatesService.cs +++ b/src/Business/Grand.Business.Core/Interfaces/Marketing/Customers/ICustomerCoordinatesService.cs @@ -4,8 +4,6 @@ namespace Grand.Business.Core.Interfaces.Marketing.Customers; public interface ICustomerCoordinatesService { - Task<(double longitude, double latitude)> GetGeoCoordinate(); Task<(double longitude, double latitude)> GetGeoCoordinate(Customer customer); - Task SaveGeoCoordinate(double longitude, double latitude); Task SaveGeoCoordinate(Customer customer, double longitude, double latitude); } \ No newline at end of file diff --git a/src/Business/Grand.Business.Core/Interfaces/Messages/IMessageProviderService.cs b/src/Business/Grand.Business.Core/Interfaces/Messages/IMessageProviderService.cs index aa9ec68a40..f28f90038a 100644 --- a/src/Business/Grand.Business.Core/Interfaces/Messages/IMessageProviderService.cs +++ b/src/Business/Grand.Business.Core/Interfaces/Messages/IMessageProviderService.cs @@ -23,7 +23,10 @@ public interface IMessageProviderService /// Store /// Message language identifier /// Queued email identifier - Task SendCustomerRegisteredMessage(Customer customer, Store store, string languageId); + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// + Task SendCustomerRegisteredMessage(Customer customer, Store store, string languageId, DomainHost host = null); /// /// Sends a welcome message to a customer @@ -32,7 +35,10 @@ public interface IMessageProviderService /// Store /// Message language identifier /// Queued email identifier - Task SendCustomerWelcomeMessage(Customer customer, Store store, string languageId); + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// + Task SendCustomerWelcomeMessage(Customer customer, Store store, string languageId, DomainHost host = null); /// /// Sends an email validation message to a customer @@ -41,7 +47,11 @@ public interface IMessageProviderService /// Store /// Message language identifier /// Queued email identifier - Task SendCustomerEmailValidationMessage(Customer customer, Store store, string languageId); + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// + Task SendCustomerEmailValidationMessage(Customer customer, Store store, string languageId, + DomainHost host = null); /// /// Sends password recovery message to a customer @@ -50,7 +60,11 @@ public interface IMessageProviderService /// Store /// Message language identifier /// Queued email identifier - Task SendCustomerPasswordRecoveryMessage(Customer customer, Store store, string languageId); + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// + Task SendCustomerPasswordRecoveryMessage(Customer customer, Store store, string languageId, + DomainHost host = null); /// /// Sends a new customer note added notification to a customer @@ -69,7 +83,11 @@ public interface IMessageProviderService /// Store instance /// Message language identifier /// Queued email identifier - Task SendCustomerEmailTokenValidationMessage(Customer customer, Store store, string languageId); + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// + Task SendCustomerEmailTokenValidationMessage(Customer customer, Store store, string languageId, + DomainHost host = null); #endregion @@ -83,7 +101,11 @@ public interface IMessageProviderService /// Vendor instance /// Message language identifier /// Queued email identifier - Task SendOrderPlacedVendorMessage(Order order, Customer customer, Vendor vendor, string languageId); + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// + Task SendOrderPlacedVendorMessage(Order order, Customer customer, Vendor vendor, string languageId, + DomainHost host = null); /// /// Sends an order placed notification to a store owner @@ -92,7 +114,11 @@ public interface IMessageProviderService /// Customer instance /// Message language identifier /// Queued email identifier - Task SendOrderPlacedStoreOwnerMessage(Order order, Customer customer, string languageId); + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// + Task SendOrderPlacedStoreOwnerMessage(Order order, Customer customer, string languageId, + DomainHost host = null); /// /// Sends an order paid notification to a store owner @@ -141,8 +167,12 @@ Task SendOrderPaidCustomerMessage(Order order, Customer customer, string la /// /// Attachments ident /// Queued email identifier + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// Task SendOrderPlacedCustomerMessage(Order order, Customer customer, string languageId, - string attachmentFilePath = null, string attachmentFileName = null, IEnumerable attachments = null); + string attachmentFilePath = null, string attachmentFileName = null, IEnumerable attachments = null, + DomainHost host = null); /// /// Sends a shipment sent notification to a customer @@ -227,7 +257,10 @@ Task SendOrderCompletedCustomerMessage(Order order, Customer customer, stri /// Order /// Order note /// Queued email identifier - Task SendNewOrderNoteAddedCustomerMessage(Order order, OrderNote orderNote); + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// + Task SendNewOrderNoteAddedCustomerMessage(Order order, OrderNote orderNote, DomainHost host = null); #endregion @@ -239,8 +272,11 @@ Task SendOrderCompletedCustomerMessage(Order order, Customer customer, stri /// Newsletter subscription /// Language identifier /// Queued email identifier + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// Task SendNewsLetterSubscriptionActivationMessage(NewsLetterSubscription subscription, - string languageId); + string languageId, DomainHost host = null); /// /// Sends a newsletter subscription deactivation message @@ -248,8 +284,11 @@ Task SendNewsLetterSubscriptionActivationMessage(NewsLetterSubscription sub /// Newsletter subscription /// Language identifier /// Queued email identifier + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// Task SendNewsLetterSubscriptionDeactivationMessage(NewsLetterSubscription subscription, - string languageId); + string languageId, DomainHost host = null); #endregion @@ -266,8 +305,11 @@ Task SendNewsLetterSubscriptionDeactivationMessage(NewsLetterSubscription s /// Friend's email /// Personal message /// Queued email identifier + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// Task SendProductEmailAFriendMessage(Customer customer, Store store, string languageId, - Product product, string customerEmail, string friendsEmail, string personalMessage); + Product product, string customerEmail, string friendsEmail, string personalMessage, DomainHost host = null); /// /// Sends wishlist "email a friend" message @@ -279,16 +321,23 @@ Task SendProductEmailAFriendMessage(Customer customer, Store store, string /// Friend's email /// Personal message /// Queued email identifier + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// Task SendWishlistEmailAFriendMessage(Customer customer, Store store, string languageId, - string customerEmail, string friendsEmail, string personalMessage); + string customerEmail, string friendsEmail, string personalMessage, DomainHost host = null); /// /// Sends "email a friend" message /// /// Queued email identifier + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// Task SendProductQuestionMessage(Customer customer, Store store, string languageId, - Product product, string customerEmail, string fullName, string phone, string message, string ipaddress); + Product product, string customerEmail, string fullName, string phone, string message, string ipaddress, + DomainHost host = null); #endregion @@ -301,8 +350,11 @@ Task SendProductQuestionMessage(Customer customer, Store store, string lang /// Order /// Message language identifier /// Queued email identifier + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// Task SendNewMerchandiseReturnStoreOwnerMessage(MerchandiseReturn merchandiseReturn, Order order, - string languageId); + string languageId, DomainHost host = null); /// @@ -322,8 +374,11 @@ Task SendMerchandiseReturnStatusChangedCustomerMessage(MerchandiseReturn me /// /// /// + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// Task SendNewMerchandiseReturnCustomerMessage(MerchandiseReturn merchandiseReturn, Order order, - string languageId); + string languageId, DomainHost host = null); /// /// Sends a new merchandise return note added notification to a customer @@ -347,8 +402,11 @@ Task SendNewMerchandiseReturnNoteAddedCustomerMessage(MerchandiseReturn mer /// Store /// Message language identifier /// Queued email identifier + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// Task SendNewVendorAccountApplyStoreOwnerMessage(Customer customer, Vendor vendor, Store store, - string languageId); + string languageId, DomainHost host = null); /// @@ -368,7 +426,11 @@ Task SendNewVendorAccountApplyStoreOwnerMessage(Customer customer, Vendor v /// Store /// Message language identifier /// Queued email identifier - Task SendProductReviewMessage(Product product, ProductReview productReview, Store store, string languageId); + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// + Task SendProductReviewMessage(Product product, ProductReview productReview, Store store, string languageId, + DomainHost host = null); /// /// Sends a vendor review notification message to a store owner @@ -377,7 +439,11 @@ Task SendNewVendorAccountApplyStoreOwnerMessage(Customer customer, Vendor v /// Store /// Message language identifier /// Queued email identifier - Task SendVendorReviewMessage(VendorReview vendorReview, Store store, string languageId); + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// + Task SendVendorReviewMessage(VendorReview vendorReview, Store store, string languageId, + DomainHost host = null); /// /// Sends a gift voucher notification @@ -413,13 +479,20 @@ Task SendQuantityBelowStoreOwnerMessage(Product product, ProductAttributeCo /// Customer /// Message language identifier /// Queued email identifier - Task SendCustomerDeleteStoreOwnerMessage(Customer customer, string languageId); + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// + Task SendCustomerDeleteStoreOwnerMessage(Customer customer, string languageId, DomainHost host = null); /// /// Sends a blog comment notification message to a store owner /// /// Queued email identifier - Task SendBlogCommentMessage(BlogPost blogPost, BlogComment blogComment, string languageId); + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// + Task SendBlogCommentMessage(BlogPost blogPost, BlogComment blogComment, string languageId, + DomainHost host = null); /// /// Sends an article comment notification message to a store owner @@ -428,8 +501,11 @@ Task SendQuantityBelowStoreOwnerMessage(Product product, ProductAttributeCo /// Article comment /// Message language identifier /// Queued email identifier + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// Task SendArticleCommentMessage(KnowledgebaseArticle article, KnowledgebaseArticleComment articleComment, - string languageId); + string languageId, DomainHost host = null); /// /// Sends a news comment notification message to a store owner @@ -438,7 +514,11 @@ Task SendArticleCommentMessage(KnowledgebaseArticle article, KnowledgebaseA /// News comment /// Message language identifier /// Queued email identifier - Task SendNewsCommentMessage(NewsItem newsItem, NewsComment newsComment, string languageId); + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// + Task SendNewsCommentMessage(NewsItem newsItem, NewsComment newsComment, string languageId, + DomainHost host = null); /// /// Sends a 'Back in stock' notification message to a customer @@ -466,9 +546,12 @@ Task SendBackInStockMessage(Customer customer, Product product, OutOfStockS /// Custom Attributes /// ip address /// Queued email identifier + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// Task SendContactUsMessage(Customer customer, Store store, string languageId, string senderEmail, string senderName, string subject, string body, string attrInfo, IList customAttributes, - string ipaddress); + string ipaddress, DomainHost host = null); /// /// Sends "contact vendor" message @@ -483,8 +566,11 @@ Task SendContactUsMessage(Customer customer, Store store, string languageId /// Email body /// Ip Address /// Queued email identifier + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// Task SendContactVendorMessage(Customer customer, Store store, Vendor vendor, string languageId, - string senderEmail, string senderName, string subject, string body, string ipaddress); + string senderEmail, string senderName, string subject, string body, string ipaddress, DomainHost host = null); /// /// Sends auction ended notification to a customer (winner) diff --git a/src/Business/Grand.Business.Marketing/Services/Contacts/ContactAttributeParser.cs b/src/Business/Grand.Business.Marketing/Services/Contacts/ContactAttributeParser.cs index 5d9c109a18..b98d8880ca 100644 --- a/src/Business/Grand.Business.Marketing/Services/Contacts/ContactAttributeParser.cs +++ b/src/Business/Grand.Business.Marketing/Services/Contacts/ContactAttributeParser.cs @@ -1,11 +1,12 @@ using Grand.Business.Core.Extensions; using Grand.Business.Core.Interfaces.Marketing.Contacts; +using Grand.Business.Core.Utilities.Catalog; using Grand.Domain.Catalog; using Grand.Domain.Common; using Grand.Domain.Customers; using Grand.Domain.Localization; using Grand.Domain.Messages; -using Grand.Infrastructure; +using Grand.Domain.Stores; using Grand.SharedKernel.Extensions; using System.Net; @@ -17,15 +18,10 @@ namespace Grand.Business.Marketing.Services.Contacts; public class ContactAttributeParser : IContactAttributeParser { private readonly IContactAttributeService _contactAttributeService; - private readonly IContextAccessor _contextAccessor; - public ContactAttributeParser( - IContactAttributeService contactAttributeService, - IContextAccessor contextAccessor - ) + public ContactAttributeParser(IContactAttributeService contactAttributeService) { _contactAttributeService = contactAttributeService; - _contextAccessor = contextAccessor; } /// @@ -161,7 +157,7 @@ public virtual IList RemoveContactAttribute(IList /// Language /// Attributes - /// Customer + /// Store host the file upload links point to; without one the file is named but not linked /// Separator /// A value indicating whether to encode (HTML) values /// A value indicating whether to HTML hyperlink tags could be rendered (if required) @@ -169,7 +165,7 @@ public virtual IList RemoveContactAttribute(IList FormatAttributes( Language language, IList customAttributes, - Customer customer, + DomainHost host, string separator = "
", bool htmlEncode = true, bool allowHyperlinks = true) @@ -212,10 +208,19 @@ public virtual async Task FormatAttributes( var attributeName = attribute.GetTranslation(a => a.Name, language.Id); if (allowHyperlinks) { - var downloadLink = - $"{_contextAccessor.StoreContext.CurrentHost.Url.TrimEnd('/')}/download/getfileupload/?downloadId={downloadGuid}"; - attributeText = - $"{attribute.GetTranslation(a => a.TextPrompt, language.Id)}"; + var textPrompt = attribute.GetTranslation(a => a.TextPrompt, language.Id); + if (host != null) + { + var downloadLink = + $"{host.Url.TrimEnd('/')}/download/getfileupload/?downloadId={downloadGuid}"; + attributeText = + $"{textPrompt}"; + } + else + { + //without a host the file cannot be linked, so name it instead of dropping it + attributeText = $"{textPrompt} ({downloadGuid})"; + } } formattedAttribute = $"{attributeName}: {attributeText}"; @@ -256,10 +261,11 @@ public virtual async Task FormatAttributes( return result.ToString(); } - public async Task> GetParseContactAttributes(IList model) + public async Task> GetParseContactAttributes(IList model, + CatalogAccess access) { var customAttributes = new List(); - var contactAttributes = await _contactAttributeService.GetAllContactAttributes(); + var contactAttributes = await _contactAttributeService.GetAllContactAttributes(access); foreach (var attribute in contactAttributes) switch (attribute.AttributeControlType) { diff --git a/src/Business/Grand.Business.Marketing/Services/Contacts/ContactAttributeService.cs b/src/Business/Grand.Business.Marketing/Services/Contacts/ContactAttributeService.cs index 6e8ede082c..0e73733050 100644 --- a/src/Business/Grand.Business.Marketing/Services/Contacts/ContactAttributeService.cs +++ b/src/Business/Grand.Business.Marketing/Services/Contacts/ContactAttributeService.cs @@ -1,8 +1,8 @@ using Grand.Business.Core.Interfaces.Marketing.Contacts; +using Grand.Business.Core.Utilities.Catalog; using Grand.Data; using Grand.Domain.Customers; using Grand.Domain.Messages; -using Grand.Infrastructure; using Grand.Infrastructure.Caching; using Grand.Infrastructure.Caching.Constants; using Grand.Infrastructure.Configuration; @@ -24,12 +24,11 @@ public class ContactAttributeService : IContactAttributeService public ContactAttributeService(ICacheBase cacheBase, IRepository contactAttributeRepository, IMediator mediator, - IContextAccessor contextAccessor, AccessControlConfig accessControlConfig) + AccessControlConfig accessControlConfig) { _cacheBase = cacheBase; _contactAttributeRepository = contactAttributeRepository; _mediator = mediator; - _contextAccessor = contextAccessor; _accessControlConfig = accessControlConfig; } @@ -40,7 +39,6 @@ public ContactAttributeService(ICacheBase cacheBase, private readonly IRepository _contactAttributeRepository; private readonly IMediator _mediator; private readonly ICacheBase _cacheBase; - private readonly IContextAccessor _contextAccessor; private readonly AccessControlConfig _accessControlConfig; #endregion @@ -69,13 +67,16 @@ public virtual async Task DeleteContactAttribute(ContactAttribute contactAttribu /// /// Gets all contact attributes /// - /// Store identifier - /// + /// Customer whose groups limit the result and the store, or every attribute of the store /// Contact attributes - public virtual async Task> GetAllContactAttributes(string storeId = "", - bool ignoreAcl = false) + public virtual async Task> GetAllContactAttributes(CatalogAccess access) { - var key = string.Format(CacheKey.CONTACTATTRIBUTES_ALL_KEY, storeId, ignoreAcl); + ArgumentNullException.ThrowIfNull(access); + + var applyAcl = !access.ShowHidden && !_accessControlConfig.IgnoreAcl; + var allowedCustomerGroupsIds = applyAcl ? access.Customer.GetCustomerGroupIds() : []; + var key = string.Format(CacheKey.CONTACTATTRIBUTES_ALL_KEY, access.StoreId, access.ShowHidden, + string.Join(",", allowedCustomerGroupsIds)); return await _cacheBase.GetAsync(key, async () => { var query = from p in _contactAttributeRepository.Table @@ -83,21 +84,15 @@ public virtual async Task> GetAllContactAttributes(strin query = query.OrderBy(c => c.DisplayOrder); - if ((string.IsNullOrEmpty(storeId) || _accessControlConfig.IgnoreStoreLimitations) && - (ignoreAcl || _accessControlConfig.IgnoreAcl)) - return await _contactAttributeRepository.ToListAsync(query); - if (!ignoreAcl && !_accessControlConfig.IgnoreAcl) - { - var allowedCustomerGroupsIds = _contextAccessor.WorkContext.CurrentCustomer.GetCustomerGroupIds(); + if (applyAcl) query = from p in query where !p.LimitedToGroups || allowedCustomerGroupsIds.Any(x => p.CustomerGroups.Contains(x)) select p; - } //Store acl - if (!string.IsNullOrEmpty(storeId) && !_accessControlConfig.IgnoreStoreLimitations) + if (!string.IsNullOrEmpty(access.StoreId) && !_accessControlConfig.IgnoreStoreLimitations) query = from p in query - where !p.LimitedToStores || p.Stores.Contains(storeId) + where !p.LimitedToStores || p.Stores.Contains(access.StoreId) select p; return await _contactAttributeRepository.ToListAsync(query); }); diff --git a/src/Business/Grand.Business.Marketing/Services/Customers/CustomerCoordinatesService.cs b/src/Business/Grand.Business.Marketing/Services/Customers/CustomerCoordinatesService.cs index b4e4bd615b..fd348f86ab 100644 --- a/src/Business/Grand.Business.Marketing/Services/Customers/CustomerCoordinatesService.cs +++ b/src/Business/Grand.Business.Marketing/Services/Customers/CustomerCoordinatesService.cs @@ -3,7 +3,6 @@ using Grand.Data; using Grand.Domain.Common; using Grand.Domain.Customers; -using Grand.Infrastructure; using Grand.Mediator; namespace Grand.Business.Marketing.Services.Customers; @@ -12,36 +11,22 @@ public class CustomerCoordinatesService : ICustomerCoordinatesService { private readonly IRepository _customerRepository; private readonly IMediator _mediator; - private readonly IContextAccessor _contextAccessor; public CustomerCoordinatesService( IRepository customerRepository, - IContextAccessor contextAccessor, IMediator mediator) { _customerRepository = customerRepository; - _contextAccessor = contextAccessor; _mediator = mediator; } - public Task<(double longitude, double latitude)> GetGeoCoordinate() - { - return GetGeoCoordinate(_contextAccessor.WorkContext.CurrentCustomer); - } - - public async Task<(double longitude, double latitude)> GetGeoCoordinate(Customer customer) + public Task<(double longitude, double latitude)> GetGeoCoordinate(Customer customer) { ArgumentNullException.ThrowIfNull(customer); - if (customer.Coordinates == null) - await Task.FromResult((0, 0)); - - return await Task.FromResult((customer.Coordinates!.X, customer.Coordinates!.Y)); - } - - public async Task SaveGeoCoordinate(double longitude, double latitude) - { - await SaveGeoCoordinate(_contextAccessor.WorkContext.CurrentCustomer, longitude, latitude); + return customer.Coordinates == null + ? Task.FromResult((0d, 0d)) + : Task.FromResult((customer.Coordinates.X, customer.Coordinates.Y)); } public async Task SaveGeoCoordinate(Customer customer, double longitude, double latitude) diff --git a/src/Business/Grand.Business.Messages/Services/MessageProviderService.cs b/src/Business/Grand.Business.Messages/Services/MessageProviderService.cs index 4778a0c1ec..d73e19d7b9 100644 --- a/src/Business/Grand.Business.Messages/Services/MessageProviderService.cs +++ b/src/Business/Grand.Business.Messages/Services/MessageProviderService.cs @@ -18,7 +18,6 @@ using Grand.Domain.Shipping; using Grand.Domain.Stores; using Grand.Domain.Vendors; -using Grand.Infrastructure; using Grand.SharedKernel.Extensions; using Grand.Mediator; using System.Net; @@ -30,7 +29,6 @@ public class MessageProviderService : IMessageProviderService #region Ctor public MessageProviderService( - IContextAccessor contextAccessor, IMessageTemplateService messageTemplateService, IQueuedEmailService queuedEmailService, ILanguageService languageService, @@ -41,7 +39,6 @@ public MessageProviderService( EmailAccountSettings emailAccountSettings, CommonSettings commonSettings) { - _contextAccessor = contextAccessor; _messageTemplateService = messageTemplateService; _queuedEmailService = queuedEmailService; _languageService = languageService; @@ -56,7 +53,6 @@ public MessageProviderService( #endregion #region Fields - private readonly IContextAccessor _contextAccessor; private readonly IMessageTemplateService _messageTemplateService; private readonly IQueuedEmailService _queuedEmailService; private readonly ILanguageService _languageService; @@ -68,8 +64,6 @@ public MessageProviderService( private readonly EmailAccountSettings _emailAccountSettings; private readonly CommonSettings _commonSettings; - private DomainHost CurrentHost => _contextAccessor.StoreContext.CurrentHost; - #endregion #region Utilities @@ -119,11 +113,12 @@ protected virtual async Task EnsureLanguageIsActive(string languageId, return language; } - private void AddCustomerTokensIfNotNull(LiquidObjectBuilder builder, Customer customer, Store store, Language language) + private void AddCustomerTokensIfNotNull(LiquidObjectBuilder builder, Customer customer, Store store, + DomainHost host, Language language) { if (customer != null) { - builder.AddCustomerTokens(customer, store, CurrentHost, language); + builder.AddCustomerTokens(customer, store, host, language); } } @@ -142,8 +137,11 @@ private void AddCustomerTokensIfNotNull(LiquidObjectBuilder builder, Customer cu /// Message template name /// Send email to email account /// Customer note + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// protected virtual async Task SendCustomerMessage(Customer customer, Store store, string languageId, - string templateName, bool toEmailAccount = false, CustomerNote customerNote = null) + string templateName, bool toEmailAccount = false, CustomerNote customerNote = null, DomainHost host = null) { ArgumentNullException.ThrowIfNull(customer); @@ -156,9 +154,10 @@ protected virtual async Task SendCustomerMessage(Customer customer, Store s //email account var emailAccount = await GetEmailAccountOfMessageTemplate(messageTemplate, language.Id); + host = store.LinkHost(host); var builder = new LiquidObjectBuilder(_mediator); builder.AddStoreTokens(store, language, emailAccount) - .AddCustomerTokens(customer, store, CurrentHost, language, customerNote); + .AddCustomerTokens(customer, store, host, language, customerNote); var liquidObject = await builder.BuildAsync(); //event notification @@ -178,9 +177,14 @@ protected virtual async Task SendCustomerMessage(Customer customer, Store s /// Store identifier /// Message language identifier /// Queued email identifier - public virtual async Task SendCustomerRegisteredMessage(Customer customer, Store store, string languageId) + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// + public virtual async Task SendCustomerRegisteredMessage(Customer customer, Store store, string languageId, + DomainHost host = null) { - return await SendCustomerMessage(customer, store, languageId, MessageTemplateNames.CustomerRegistered, true); + return await SendCustomerMessage(customer, store, languageId, MessageTemplateNames.CustomerRegistered, true, + host: host); } /// @@ -189,9 +193,13 @@ public virtual async Task SendCustomerRegisteredMessage(Customer customer, /// Customer instance /// Store /// Message language identifier - public virtual async Task SendCustomerWelcomeMessage(Customer customer, Store store, string languageId) + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// + public virtual async Task SendCustomerWelcomeMessage(Customer customer, Store store, string languageId, + DomainHost host = null) { - return await SendCustomerMessage(customer, store, languageId, MessageTemplateNames.CustomerWelcome); + return await SendCustomerMessage(customer, store, languageId, MessageTemplateNames.CustomerWelcome, host: host); } /// @@ -200,9 +208,14 @@ public virtual async Task SendCustomerWelcomeMessage(Customer customer, Sto /// Customer instance /// Store /// Message language identifier - public virtual async Task SendCustomerEmailValidationMessage(Customer customer, Store store, string languageId) + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// + public virtual async Task SendCustomerEmailValidationMessage(Customer customer, Store store, string languageId, + DomainHost host = null) { - return await SendCustomerMessage(customer, store, languageId, MessageTemplateNames.CustomerEmailValidation); + return await SendCustomerMessage(customer, store, languageId, MessageTemplateNames.CustomerEmailValidation, + host: host); } /// @@ -211,10 +224,14 @@ public virtual async Task SendCustomerEmailValidationMessage(Customer custo /// Customer instance /// Store /// Message language identifier + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// public virtual async Task SendCustomerPasswordRecoveryMessage(Customer customer, Store store, - string languageId) + string languageId, DomainHost host = null) { - return await SendCustomerMessage(customer, store, languageId, MessageTemplateNames.CustomerPasswordRecovery); + return await SendCustomerMessage(customer, store, languageId, MessageTemplateNames.CustomerPasswordRecovery, + host: host); } /// @@ -238,11 +255,14 @@ public virtual async Task SendNewCustomerNoteMessage(CustomerNote customerN /// Customer instance /// Store instance /// Message language identifier + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// public virtual async Task SendCustomerEmailTokenValidationMessage(Customer customer, Store store, - string languageId) + string languageId, DomainHost host = null) { return await SendCustomerMessage(customer, store, languageId, - MessageTemplateNames.CustomerEmailTokenValidationMessage); + MessageTemplateNames.CustomerEmailTokenValidationMessage, host: host); } #endregion @@ -255,10 +275,14 @@ public virtual async Task SendCustomerEmailTokenValidationMessage(Customer /// Order instance /// Customer /// Message language identifier - public virtual async Task SendOrderPlacedStoreOwnerMessage(Order order, Customer customer, string languageId) + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// + public virtual async Task SendOrderPlacedStoreOwnerMessage(Order order, Customer customer, string languageId, + DomainHost host = null) { return await SendOrderStoreOwnerMessage(MessageTemplateNames.SendOrderPlacedStoreOwnerMessage, order, customer, - languageId); + languageId, host: host); } /// @@ -307,8 +331,11 @@ public virtual async Task SendOrderRefundedStoreOwnerMessage(Order order, d /// Customer instance /// Message language identifier /// + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// private async Task SendOrderStoreOwnerMessage(string template, Order order, Customer customer, - string languageId, double refundedAmount = 0) + string languageId, double refundedAmount = 0, DomainHost host = null) { ArgumentNullException.ThrowIfNull(order); @@ -322,10 +349,11 @@ private async Task SendOrderStoreOwnerMessage(string template, Order order, //email account var emailAccount = await GetEmailAccountOfMessageTemplate(messageTemplate, language.Id); + host = store.LinkHost(host); var liquidBuilder = new LiquidObjectBuilder(_mediator); liquidBuilder.AddStoreTokens(store, language, emailAccount) - .AddOrderTokens(order, customer, store, CurrentHost); - AddCustomerTokensIfNotNull(liquidBuilder, customer, store, language); + .AddOrderTokens(order, customer, store, host); + AddCustomerTokensIfNotNull(liquidBuilder, customer, store, host, language); var liquidObject = await liquidBuilder.BuildAsync(); //event notification @@ -351,11 +379,15 @@ private async Task SendOrderStoreOwnerMessage(string template, Order order, /// Otherwise, "AttachmentFilePath" name will be used. /// /// Attachments + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// public virtual async Task SendOrderPlacedCustomerMessage(Order order, Customer customer, string languageId, - string attachmentFilePath = null, string attachmentFileName = null, IEnumerable attachments = null) + string attachmentFilePath = null, string attachmentFileName = null, IEnumerable attachments = null, + DomainHost host = null) { return await SendOrderCustomerMessage(MessageTemplateNames.SendOrderPlacedCustomerMessage, order, customer, - languageId, attachmentFilePath, attachmentFileName, attachments); + languageId, attachmentFilePath, attachmentFileName, attachments, host: host); } /// @@ -437,9 +469,12 @@ public virtual async Task SendOrderRefundedCustomerMessage(Order order, dou /// /// Attachments ident /// + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// private async Task SendOrderCustomerMessage(string message, Order order, Customer customer, string languageId, string attachmentFilePath = null, string attachmentFileName = null, IEnumerable attachments = null, - double refundedAmount = 0) + double refundedAmount = 0, DomainHost host = null) { ArgumentNullException.ThrowIfNull(order); @@ -453,10 +488,11 @@ private async Task SendOrderCustomerMessage(string message, Order order, Cu //email account var emailAccount = await GetEmailAccountOfMessageTemplate(messageTemplate, language.Id); + host = store.LinkHost(host); var builder = new LiquidObjectBuilder(_mediator); builder.AddStoreTokens(store, language, emailAccount) - .AddOrderTokens(order, customer, store, CurrentHost); - AddCustomerTokensIfNotNull(builder, customer, store, language); + .AddOrderTokens(order, customer, store, host); + AddCustomerTokensIfNotNull(builder, customer, store, host, language); var liquidObject = await builder.BuildAsync(); //event notification @@ -480,11 +516,14 @@ private async Task SendOrderCustomerMessage(string message, Order order, Cu /// Customer instance /// Vendor instance /// Message language identifier + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// public virtual async Task SendOrderPlacedVendorMessage(Order order, Customer customer, Vendor vendor, - string languageId) + string languageId, DomainHost host = null) { return await SendOrderVendorMessage(MessageTemplateNames.SendOrderPlacedVendorMessage, order, vendor, - languageId); + languageId, host: host); } /// @@ -517,7 +556,11 @@ public virtual async Task SendOrderCancelledVendorMessage(Order order, Vend /// Order instance /// Vendor instance /// Message language identifier - private async Task SendOrderVendorMessage(string message, Order order, Vendor vendor, string languageId) + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// + private async Task SendOrderVendorMessage(string message, Order order, Vendor vendor, string languageId, + DomainHost host = null) { ArgumentNullException.ThrowIfNull(order); ArgumentNullException.ThrowIfNull(vendor); @@ -534,11 +577,12 @@ private async Task SendOrderVendorMessage(string message, Order order, Vend var customer = await _mediator.Send(new GetCustomerByIdQuery { Id = order.CustomerId }); + host = store.LinkHost(host); var builder = new LiquidObjectBuilder(_mediator); builder.AddStoreTokens(store, language, emailAccount) - .AddOrderTokens(order, customer, store, CurrentHost, vendor: vendor); + .AddOrderTokens(order, customer, store, host, vendor: vendor); - AddCustomerTokensIfNotNull(builder, customer, store, language); + AddCustomerTokensIfNotNull(builder, customer, store, host, language); var liquidObject = await builder.BuildAsync(); //event notification @@ -599,12 +643,13 @@ private async Task SendShipmentCustomerMessage(string message, Shipment shi var customer = await _mediator.Send(new GetCustomerByIdQuery { Id = order.CustomerId }); + var host = store.PrimaryHost(); var builder = new LiquidObjectBuilder(_mediator); builder.AddStoreTokens(store, language, emailAccount) - .AddShipmentTokens(shipment, order, store, CurrentHost, language) - .AddOrderTokens(order, customer, store, CurrentHost); + .AddShipmentTokens(shipment, order, store, host, language) + .AddOrderTokens(order, customer, store, host); - AddCustomerTokensIfNotNull(builder, customer, store, language); + AddCustomerTokensIfNotNull(builder, customer, store, host, language); var liquidObject = await builder.BuildAsync(); //event notification @@ -624,7 +669,11 @@ private async Task SendShipmentCustomerMessage(string message, Shipment shi /// /// Order /// Order note - public virtual async Task SendNewOrderNoteAddedCustomerMessage(Order order, OrderNote orderNote) + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// + public virtual async Task SendNewOrderNoteAddedCustomerMessage(Order order, OrderNote orderNote, + DomainHost host = null) { ArgumentNullException.ThrowIfNull(orderNote); ArgumentNullException.ThrowIfNull(order); @@ -642,11 +691,12 @@ public virtual async Task SendNewOrderNoteAddedCustomerMessage(Order order, var customer = await _mediator.Send(new GetCustomerByIdQuery { Id = order.CustomerId }); + host = store.LinkHost(host); var builder = new LiquidObjectBuilder(_mediator); builder.AddStoreTokens(store, language, emailAccount) - .AddOrderTokens(order, customer, store, CurrentHost, orderNote); + .AddOrderTokens(order, customer, store, host, orderNote); - AddCustomerTokensIfNotNull(builder, customer, store, language); + AddCustomerTokensIfNotNull(builder, customer, store, host, language); var liquidObject = await builder.BuildAsync(); //event notification @@ -669,11 +719,14 @@ public virtual async Task SendNewOrderNoteAddedCustomerMessage(Order order, /// /// Newsletter subscription /// Language identifier + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// public virtual async Task SendNewsLetterSubscriptionActivationMessage(NewsLetterSubscription subscription, - string languageId) + string languageId, DomainHost host = null) { return await SendNewsLetterSubscriptionMessage(MessageTemplateNames.SendNewsLetterSubscriptionActivationMessage, - subscription, languageId); + subscription, languageId, host: host); } /// @@ -681,11 +734,14 @@ public virtual async Task SendNewsLetterSubscriptionActivationMessage(NewsL /// /// Newsletter subscription /// Language identifier + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// public virtual async Task SendNewsLetterSubscriptionDeactivationMessage(NewsLetterSubscription subscription, - string languageId) + string languageId, DomainHost host = null) { return await SendNewsLetterSubscriptionMessage( - MessageTemplateNames.SendNewsLetterSubscriptionDeactivationMessage, subscription, languageId); + MessageTemplateNames.SendNewsLetterSubscriptionDeactivationMessage, subscription, languageId, host: host); } /// @@ -694,8 +750,11 @@ public virtual async Task SendNewsLetterSubscriptionDeactivationMessage(New /// Message template /// Newsletter subscription /// Language identifier + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// private async Task SendNewsLetterSubscriptionMessage(string message, NewsLetterSubscription subscription, - string languageId) + string languageId, DomainHost host = null) { ArgumentNullException.ThrowIfNull(subscription); @@ -709,9 +768,10 @@ private async Task SendNewsLetterSubscriptionMessage(string message, NewsLe //email account var emailAccount = await GetEmailAccountOfMessageTemplate(messageTemplate, language.Id); + host = store.LinkHost(host); var builder = new LiquidObjectBuilder(_mediator); builder.AddStoreTokens(store, language, emailAccount) - .AddNewsLetterSubscriptionTokens(subscription, store, CurrentHost); + .AddNewsLetterSubscriptionTokens(subscription, store, host); var liquidObject = await builder.BuildAsync(); //event notification @@ -739,8 +799,11 @@ private async Task SendNewsLetterSubscriptionMessage(string message, NewsLe /// Friend's email /// Personal message /// Queued email identifier + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// public virtual async Task SendProductEmailAFriendMessage(Customer customer, Store store, string languageId, - Product product, string customerEmail, string friendsEmail, string personalMessage) + Product product, string customerEmail, string friendsEmail, string personalMessage, DomainHost host = null) { ArgumentNullException.ThrowIfNull(customer); ArgumentNullException.ThrowIfNull(product); @@ -754,11 +817,12 @@ public virtual async Task SendProductEmailAFriendMessage(Customer customer, //email account var emailAccount = await GetEmailAccountOfMessageTemplate(messageTemplate, language.Id); + host = store.LinkHost(host); var builder = new LiquidObjectBuilder(_mediator); builder.AddStoreTokens(store, language, emailAccount) .AddEmailAFriendTokens(personalMessage, customerEmail, friendsEmail) - .AddCustomerTokens(customer, store, CurrentHost, language) - .AddProductTokens(product, language, store, CurrentHost); + .AddCustomerTokens(customer, store, host, language) + .AddProductTokens(product, language, store, host); var liquidObject = await builder.BuildAsync(); //event notification @@ -782,8 +846,11 @@ public virtual async Task SendProductEmailAFriendMessage(Customer customer, /// Friend's email /// Personal message /// Queued email identifier + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// public virtual async Task SendWishlistEmailAFriendMessage(Customer customer, Store store, string languageId, - string customerEmail, string friendsEmail, string personalMessage) + string customerEmail, string friendsEmail, string personalMessage, DomainHost host = null) { ArgumentNullException.ThrowIfNull(customer); @@ -797,9 +864,10 @@ public virtual async Task SendWishlistEmailAFriendMessage(Customer customer var emailAccount = await GetEmailAccountOfMessageTemplate(messageTemplate, language.Id); + host = store.LinkHost(host); var builder = new LiquidObjectBuilder(_mediator); builder.AddStoreTokens(store, language, emailAccount) - .AddCustomerTokens(customer, store, CurrentHost, language) + .AddCustomerTokens(customer, store, host, language) .AddEmailAFriendTokens(personalMessage, customerEmail, friendsEmail); var liquidObject = await builder.BuildAsync(); @@ -820,8 +888,12 @@ public virtual async Task SendWishlistEmailAFriendMessage(Customer customer /// Sends "email a friend" message /// /// Queued email identifier + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// public virtual async Task SendProductQuestionMessage(Customer customer, Store store, string languageId, - Product product, string customerEmail, string fullName, string phone, string message, string ipaddress) + Product product, string customerEmail, string fullName, string phone, string message, string ipaddress, + DomainHost host = null) { ArgumentNullException.ThrowIfNull(customer); ArgumentNullException.ThrowIfNull(product); @@ -835,10 +907,11 @@ public virtual async Task SendProductQuestionMessage(Customer customer, Sto //email account var emailAccount = await GetEmailAccountOfMessageTemplate(messageTemplate, language.Id); + host = store.LinkHost(host); var builder = new LiquidObjectBuilder(_mediator); builder.AddStoreTokens(store, language, emailAccount) - .AddCustomerTokens(customer, store, CurrentHost, language) - .AddProductTokens(product, language, store, CurrentHost); + .AddCustomerTokens(customer, store, host, language) + .AddProductTokens(product, language, store, host); var liquidObject = await builder.BuildAsync(); liquidObject.AskQuestion = new LiquidAskQuestion(message, customerEmail, fullName, phone); @@ -896,8 +969,11 @@ await _mediator.Send(new InsertContactUsCommand { /// Merchandise return /// Order /// Message language identifier + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// public virtual async Task SendNewMerchandiseReturnStoreOwnerMessage(MerchandiseReturn merchandiseReturn, - Order order, string languageId) + Order order, string languageId, DomainHost host = null) { ArgumentNullException.ThrowIfNull(merchandiseReturn); @@ -914,12 +990,13 @@ public virtual async Task SendNewMerchandiseReturnStoreOwnerMessage(Merchan var customer = await _mediator.Send(new GetCustomerByIdQuery { Id = merchandiseReturn.CustomerId }); + host = store.LinkHost(host); var builder = new LiquidObjectBuilder(_mediator); builder.AddStoreTokens(store, language, emailAccount); - AddCustomerTokensIfNotNull(builder, customer, store, language); + AddCustomerTokensIfNotNull(builder, customer, store, host, language); - builder.AddMerchandiseReturnTokens(merchandiseReturn, store, CurrentHost, order, language); + builder.AddMerchandiseReturnTokens(merchandiseReturn, store, host, order, language); var liquidObject = await builder.BuildAsync(); @@ -971,12 +1048,13 @@ public virtual async Task SendMerchandiseReturnStatusChangedCustomerMessage var customer = await _mediator.Send(new GetCustomerByIdQuery { Id = merchandiseReturn.CustomerId }); + var host = store.PrimaryHost(); var builder = new LiquidObjectBuilder(_mediator); builder.AddStoreTokens(store, language, emailAccount); - AddCustomerTokensIfNotNull(builder, customer, store, language); + AddCustomerTokensIfNotNull(builder, customer, store, host, language); - builder.AddMerchandiseReturnTokens(merchandiseReturn, store, CurrentHost, order, language); + builder.AddMerchandiseReturnTokens(merchandiseReturn, store, host, order, language); var liquidObject = await builder.BuildAsync(); //event notification await _mediator.MessageTokensAdded(messageTemplate, liquidObject); @@ -995,8 +1073,11 @@ public virtual async Task SendMerchandiseReturnStatusChangedCustomerMessage /// Merchandise return /// Order /// Message language identifier + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// public virtual async Task SendNewMerchandiseReturnCustomerMessage(MerchandiseReturn merchandiseReturn, - Order order, string languageId) + Order order, string languageId, DomainHost host = null) { ArgumentNullException.ThrowIfNull(merchandiseReturn); @@ -1013,11 +1094,12 @@ public virtual async Task SendNewMerchandiseReturnCustomerMessage(Merchandi var customer = await _mediator.Send(new GetCustomerByIdQuery { Id = merchandiseReturn.CustomerId }); + host = store.LinkHost(host); var builder = new LiquidObjectBuilder(_mediator); builder.AddStoreTokens(store, language, emailAccount); - AddCustomerTokensIfNotNull(builder, customer, store, language); + AddCustomerTokensIfNotNull(builder, customer, store, host, language); - builder.AddMerchandiseReturnTokens(merchandiseReturn, store, CurrentHost, order, language); + builder.AddMerchandiseReturnTokens(merchandiseReturn, store, host, order, language); var liquidObject = await builder.BuildAsync(); //event notification await _mediator.MessageTokensAdded(messageTemplate, liquidObject); @@ -1055,11 +1137,12 @@ public virtual async Task SendNewMerchandiseReturnNoteAddedCustomerMessage( var customer = await _mediator.Send(new GetCustomerByIdQuery { Id = merchandiseReturn.CustomerId }); + var host = store.PrimaryHost(); var builder = new LiquidObjectBuilder(_mediator); builder.AddStoreTokens(store, language, emailAccount); - AddCustomerTokensIfNotNull(builder, customer, store, language); + AddCustomerTokensIfNotNull(builder, customer, store, host, language); - builder.AddMerchandiseReturnTokens(merchandiseReturn, store, CurrentHost, order, language, + builder.AddMerchandiseReturnTokens(merchandiseReturn, store, host, order, language, merchandiseReturnNote); var liquidObject = await builder.BuildAsync(); //event notification @@ -1084,8 +1167,11 @@ public virtual async Task SendNewMerchandiseReturnNoteAddedCustomerMessage( /// Vendor /// Store /// Message language identifier + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// public virtual async Task SendNewVendorAccountApplyStoreOwnerMessage(Customer customer, Vendor vendor, - Store store, string languageId) + Store store, string languageId, DomainHost host = null) { ArgumentNullException.ThrowIfNull(customer); ArgumentNullException.ThrowIfNull(vendor); @@ -1099,8 +1185,9 @@ public virtual async Task SendNewVendorAccountApplyStoreOwnerMessage(Custom //email account var emailAccount = await GetEmailAccountOfMessageTemplate(messageTemplate, language.Id); + host = store.LinkHost(host); var builder = new LiquidObjectBuilder(_mediator).AddStoreTokens(store, language, emailAccount) - .AddCustomerTokens(customer, store, CurrentHost, language) + .AddCustomerTokens(customer, store, host, language) .AddVendorTokens(vendor, language); var liquidObject = await builder.BuildAsync(); @@ -1192,8 +1279,11 @@ public virtual async Task SendGiftVoucherMessage(GiftVoucher giftVoucher, O /// Product review /// Store /// Message language identifier + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// public virtual async Task SendProductReviewMessage(Product product, ProductReview productReview, - Store store, string languageId) + Store store, string languageId, DomainHost host = null) { ArgumentNullException.ThrowIfNull(productReview); @@ -1208,10 +1298,11 @@ public virtual async Task SendProductReviewMessage(Product product, Product var customer = await _mediator.Send(new GetCustomerByIdQuery { Id = productReview.CustomerId }); + host = store.LinkHost(host); var builder = new LiquidObjectBuilder(_mediator); builder.AddStoreTokens(store, language, emailAccount) .AddProductReviewTokens(product, productReview); - AddCustomerTokensIfNotNull(builder, customer, store, language); + AddCustomerTokensIfNotNull(builder, customer, store, host, language); var liquidObject = await builder.BuildAsync(); //event notification @@ -1232,8 +1323,11 @@ public virtual async Task SendProductReviewMessage(Product product, Product /// Vendor review /// Store /// Message language identifier + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// public virtual async Task SendVendorReviewMessage(VendorReview vendorReview, Store store, - string languageId) + string languageId, DomainHost host = null) { ArgumentNullException.ThrowIfNull(vendorReview); @@ -1250,10 +1344,11 @@ public virtual async Task SendVendorReviewMessage(VendorReview vendorReview //vendor var vendor = await _mediator.Send(new GetVendorByIdQuery { Id = vendorReview.VendorId }); + host = store.LinkHost(host); var builder = new LiquidObjectBuilder(_mediator); builder.AddStoreTokens(store, language, emailAccount) .AddVendorReviewTokens(vendor, vendorReview); - AddCustomerTokensIfNotNull(builder, customer, store, language); + AddCustomerTokensIfNotNull(builder, customer, store, host, language); builder.AddVendorTokens(vendor, language); var liquidObject = await builder.BuildAsync(); @@ -1288,9 +1383,10 @@ public virtual async Task SendQuantityBelowStoreOwnerMessage(Product produc //email account var emailAccount = await GetEmailAccountOfMessageTemplate(messageTemplate, language.Id); + var host = store.PrimaryHost(); var builder = new LiquidObjectBuilder(_mediator); builder.AddStoreTokens(store, language, emailAccount) - .AddProductTokens(product, language, store, CurrentHost); + .AddProductTokens(product, language, store, host); var liquidObject = await builder.BuildAsync(); //event notification await _mediator.MessageTokensAdded(messageTemplate, liquidObject); @@ -1325,10 +1421,11 @@ public virtual async Task SendQuantityBelowStoreOwnerMessage(Product produc //email account var emailAccount = await GetEmailAccountOfMessageTemplate(messageTemplate, language.Id); + var host = store.PrimaryHost(); var builder = new LiquidObjectBuilder(_mediator); builder.AddStoreTokens(store, language, emailAccount) - .AddProductTokens(product, language, store, CurrentHost) - .AddAttributeCombinationTokens(product, combination, language, CurrentHost); + .AddProductTokens(product, language, store, host) + .AddAttributeCombinationTokens(product, combination, language, host); var liquidObject = await builder.BuildAsync(); //event notification await _mediator.MessageTokensAdded(messageTemplate, liquidObject); @@ -1346,7 +1443,11 @@ public virtual async Task SendQuantityBelowStoreOwnerMessage(Product produc /// /// Customer /// Message language identifier - public virtual async Task SendCustomerDeleteStoreOwnerMessage(Customer customer, string languageId) + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// + public virtual async Task SendCustomerDeleteStoreOwnerMessage(Customer customer, string languageId, + DomainHost host = null) { ArgumentNullException.ThrowIfNull(customer); @@ -1360,9 +1461,10 @@ public virtual async Task SendCustomerDeleteStoreOwnerMessage(Customer cust //email account var emailAccount = await GetEmailAccountOfMessageTemplate(messageTemplate, language.Id); + host = store.LinkHost(host); var builder = new LiquidObjectBuilder(_mediator); builder.AddStoreTokens(store, language, emailAccount) - .AddCustomerTokens(customer, store, CurrentHost, language); + .AddCustomerTokens(customer, store, host, language); var liquidObject = await builder.BuildAsync(); @@ -1377,7 +1479,11 @@ public virtual async Task SendCustomerDeleteStoreOwnerMessage(Customer cust /// /// Sends a blog comment notification message to a store owner /// - public virtual async Task SendBlogCommentMessage(BlogPost blogPost, BlogComment blogComment, string languageId) + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// + public virtual async Task SendBlogCommentMessage(BlogPost blogPost, BlogComment blogComment, string languageId, + DomainHost host = null) { ArgumentNullException.ThrowIfNull(blogComment); @@ -1391,13 +1497,14 @@ public virtual async Task SendBlogCommentMessage(BlogPost blogPost, BlogCom //email account var emailAccount = await GetEmailAccountOfMessageTemplate(messageTemplate, language.Id); + host = store.LinkHost(host); var builder = new LiquidObjectBuilder(_mediator); builder.AddStoreTokens(store, language, emailAccount) - .AddBlogCommentTokens(blogPost, blogComment, store, CurrentHost, language); + .AddBlogCommentTokens(blogPost, blogComment, store, host, language); var customer = await _mediator.Send(new GetCustomerByIdQuery { Id = blogComment.CustomerId }); if (customer != null && await _groupService.IsRegistered(customer)) - builder.AddCustomerTokens(customer, store, CurrentHost, language); + builder.AddCustomerTokens(customer, store, host, language); var liquidObject = await builder.BuildAsync(); //event notification @@ -1417,8 +1524,11 @@ public virtual async Task SendBlogCommentMessage(BlogPost blogPost, BlogCom /// /// Article comment /// Message language identifier + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// public virtual async Task SendArticleCommentMessage(KnowledgebaseArticle article, - KnowledgebaseArticleComment articleComment, string languageId) + KnowledgebaseArticleComment articleComment, string languageId, DomainHost host = null) { ArgumentNullException.ThrowIfNull(articleComment); @@ -1432,13 +1542,14 @@ public virtual async Task SendArticleCommentMessage(KnowledgebaseArticle ar //email account var emailAccount = await GetEmailAccountOfMessageTemplate(messageTemplate, language.Id); + host = store.LinkHost(host); var builder = new LiquidObjectBuilder(_mediator); builder.AddStoreTokens(store, language, emailAccount) - .AddArticleCommentTokens(article, articleComment, store, CurrentHost, language); + .AddArticleCommentTokens(article, articleComment, store, host, language); var customer = await _mediator.Send(new GetCustomerByIdQuery { Id = articleComment.CustomerId }); if (customer != null && await _groupService.IsRegistered(customer)) - builder.AddCustomerTokens(customer, store, CurrentHost, language); + builder.AddCustomerTokens(customer, store, host, language); var liquidObject = await builder.BuildAsync(); //event notification await _mediator.MessageTokensAdded(messageTemplate, liquidObject); @@ -1456,7 +1567,11 @@ public virtual async Task SendArticleCommentMessage(KnowledgebaseArticle ar /// News item /// News comment /// Message language identifier - public virtual async Task SendNewsCommentMessage(NewsItem newsItem, NewsComment newsComment, string languageId) + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// + public virtual async Task SendNewsCommentMessage(NewsItem newsItem, NewsComment newsComment, string languageId, + DomainHost host = null) { ArgumentNullException.ThrowIfNull(newsComment); @@ -1470,11 +1585,12 @@ public virtual async Task SendNewsCommentMessage(NewsItem newsItem, NewsCom //email account var emailAccount = await GetEmailAccountOfMessageTemplate(messageTemplate, language.Id); + host = store.LinkHost(host); var builder = new LiquidObjectBuilder(_mediator); builder.AddStoreTokens(store, language, emailAccount) - .AddNewsCommentTokens(newsItem, newsComment, store, CurrentHost, language); + .AddNewsCommentTokens(newsItem, newsComment, store, host, language); var customer = await _mediator.Send(new GetCustomerByIdQuery { Id = newsComment.CustomerId }); - AddCustomerTokensIfNotNull(builder, customer, store, language); + AddCustomerTokensIfNotNull(builder, customer, store, host, language); var liquidObject = await builder.BuildAsync(); //event notification @@ -1509,11 +1625,12 @@ public virtual async Task SendBackInStockMessage(Customer customer, Product //email account var emailAccount = await GetEmailAccountOfMessageTemplate(messageTemplate, language.Id); + var host = store.PrimaryHost(); var builder = new LiquidObjectBuilder(_mediator); builder.AddStoreTokens(store, language, emailAccount); - AddCustomerTokensIfNotNull(builder, customer, store, language); + AddCustomerTokensIfNotNull(builder, customer, store, host, language); - builder.AddOutOfStockTokens(product, subscription, store, CurrentHost, language); + builder.AddOutOfStockTokens(product, subscription, store, host, language); var liquidObject = await builder.BuildAsync(); //event notification await _mediator.MessageTokensAdded(messageTemplate, liquidObject); @@ -1539,10 +1656,13 @@ public virtual async Task SendBackInStockMessage(Customer customer, Product /// Attr info /// CustomAttributes /// Ip address + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// public virtual async Task SendContactUsMessage(Customer customer, Store store, string languageId, string senderEmail, string senderName, string subject, string body, string attrInfo, IList customAttributes, - string ipaddress) + string ipaddress, DomainHost host = null) { var language = await EnsureLanguageIsActive(languageId, store.Id); var messageTemplate = await GetMessageTemplate("Service.ContactUs", store.Id); @@ -1568,9 +1688,10 @@ public virtual async Task SendContactUsMessage(Customer customer, Store sto fromName = senderName; } + host = store.LinkHost(host); var builder = new LiquidObjectBuilder(_mediator); builder.AddStoreTokens(store, language, emailAccount) - .AddCustomerTokens(customer, store, CurrentHost, language); + .AddCustomerTokens(customer, store, host, language); var liquidObject = await builder.BuildAsync(); liquidObject.ContactUs = new LiquidContactUs(senderEmail, senderName, body, attrInfo); @@ -1615,9 +1736,12 @@ await _mediator.Send(new InsertContactUsCommand { /// Email subject. Pass null if you want a message template subject to be used. /// Email body /// Ip address + /// + /// Host of the storefront request; links use it when it is one of the store's domains, else the primary domain + /// public virtual async Task SendContactVendorMessage(Customer customer, Store store, Vendor vendor, string languageId, string senderEmail, - string senderName, string subject, string body, string ipaddress) + string senderName, string subject, string body, string ipaddress, DomainHost host = null) { ArgumentNullException.ThrowIfNull(vendor); @@ -1647,9 +1771,10 @@ public virtual async Task SendContactVendorMessage(Customer customer, Store fromName = senderName; } + host = store.LinkHost(host); var builder = new LiquidObjectBuilder(_mediator); builder.AddStoreTokens(store, language, emailAccount) - .AddCustomerTokens(customer, store, CurrentHost, language); + .AddCustomerTokens(customer, store, host, language); var liquidObject = await builder.BuildAsync(); liquidObject.ContactUs = new LiquidContactUs(senderEmail, senderName, body, ""); //event notification @@ -1701,10 +1826,11 @@ public virtual async Task SendAuctionWinEndedCustomerMessage(Product produc var emailAccount = await GetEmailAccountOfMessageTemplate(messageTemplate, language.Id); + var host = store.PrimaryHost(); var builder = new LiquidObjectBuilder(_mediator); builder.AddAuctionTokens(product, bid) - .AddCustomerTokens(customer, store, CurrentHost, language) - .AddProductTokens(product, language, store, CurrentHost) + .AddCustomerTokens(customer, store, host, language) + .AddProductTokens(product, language, store, host) .AddStoreTokens(store, language, emailAccount); var liquidObject = await builder.BuildAsync(); @@ -1736,9 +1862,10 @@ public virtual async Task SendAuctionEndedLostCustomerMessage(Product produ var emailAccount = await GetEmailAccountOfMessageTemplate(messageTemplate, language.Id); + var host = store.PrimaryHost(); var builder = new LiquidObjectBuilder(_mediator); builder.AddAuctionTokens(product, bid) - .AddProductTokens(product, language, store, CurrentHost) + .AddProductTokens(product, language, store, host) .AddStoreTokens(store, language, emailAccount); var liquidObject = await builder.BuildAsync(); @@ -1750,7 +1877,7 @@ public virtual async Task SendAuctionEndedLostCustomerMessage(Product produ var customer = await _mediator.Send(new GetCustomerByIdQuery { Id = item.Key }); if (string.IsNullOrEmpty(languageId)) languageId = customer.GetUserFieldFromEntity(SystemCustomerFieldNames.LanguageId); - builder2.AddCustomerTokens(customer, store, CurrentHost, language); + builder2.AddCustomerTokens(customer, store, host, language); var liquidObject2 = await builder2.BuildAsync(); //event notification @@ -1781,8 +1908,9 @@ public virtual async Task SendAuctionEndedBinCustomerMessage(Product produc var language = await EnsureLanguageIsActive(languageId, store.Id); var emailAccount = await GetEmailAccountOfMessageTemplate(messageTemplate, language.Id); + var host = store.PrimaryHost(); var builder = new LiquidObjectBuilder(_mediator); - builder.AddProductTokens(product, language, store, CurrentHost) + builder.AddProductTokens(product, language, store, host) .AddStoreTokens(store, language, emailAccount); var liquidObject = await builder.BuildAsync(); @@ -1797,7 +1925,7 @@ public virtual async Task SendAuctionEndedBinCustomerMessage(Product produc if (string.IsNullOrEmpty(languageId)) languageId = customer.GetUserFieldFromEntity(SystemCustomerFieldNames.LanguageId); - AddCustomerTokensIfNotNull(builder2, customer, store, language); + AddCustomerTokensIfNotNull(builder2, customer, store, host, language); var liquidObject2 = await builder2.BuildAsync(); //event notification await _mediator.MessageTokensAdded(messageTemplate, liquidObject2); @@ -1838,7 +1966,7 @@ public virtual async Task SendAuctionEndedStoreOwnerMessage(Product product emailAccount = await GetEmailAccountOfMessageTemplate(messageTemplate, language.Id); builder.AddAuctionTokens(product, bid) - .AddCustomerTokens(customer, store, CurrentHost, language) + .AddCustomerTokens(customer, store, store.PrimaryHost(), language) .AddStoreTokens(store, language, emailAccount); } else @@ -1850,7 +1978,7 @@ public virtual async Task SendAuctionEndedStoreOwnerMessage(Product product return 0; emailAccount = await GetEmailAccountOfMessageTemplate(messageTemplate, language.Id); - builder.AddProductTokens(product, language, store, CurrentHost); + builder.AddProductTokens(product, language, store, store.PrimaryHost()); } var liquidObject = await builder.BuildAsync(); @@ -1890,9 +2018,10 @@ public virtual async Task SendOutBidCustomerMessage(Product product, string var emailAccount = await GetEmailAccountOfMessageTemplate(messageTemplate, language.Id); + var host = store.PrimaryHost(); var builder = new LiquidObjectBuilder(_mediator); builder.AddAuctionTokens(product, bid) - .AddCustomerTokens(customer, store, CurrentHost, language) + .AddCustomerTokens(customer, store, host, language) .AddStoreTokens(store, language, emailAccount); var liquidObject = await builder.BuildAsync(); diff --git a/src/Core/Grand.Domain/Stores/StoreExtensions.cs b/src/Core/Grand.Domain/Stores/StoreExtensions.cs index cf5f7d08d2..61cf90363c 100644 --- a/src/Core/Grand.Domain/Stores/StoreExtensions.cs +++ b/src/Core/Grand.Domain/Stores/StoreExtensions.cs @@ -33,4 +33,35 @@ public static DomainHost HostValue(this Store store, string host) ? null : store.Domains.FirstOrDefault(x => x.HostName.Equals(host, StringComparison.OrdinalIgnoreCase)); } + + /// + /// Return the host links to a store point to: its primary domain, else its first domain, else its url + /// + /// Store + /// DomainHost + public static DomainHost PrimaryHost(this Store store) + { + ArgumentNullException.ThrowIfNull(store); + return store.Domains?.FirstOrDefault(x => x.Primary) ?? store.Domains?.FirstOrDefault() ?? new DomainHost { + Id = int.MinValue.ToString(), + Url = store.Url, + HostName = "temporary-store" + }; + } + + /// + /// Return the host links to a store point to for a request to a given host: that host when it is one of the + /// store's domains, else the store's primary host + /// + /// Store + /// Host the request was made to; null when there is no request + /// DomainHost + public static DomainHost LinkHost(this Store store, DomainHost host) + { + ArgumentNullException.ThrowIfNull(store); + var domain = string.IsNullOrEmpty(host?.HostName) + ? null + : store.Domains?.FirstOrDefault(x => x.HostName.Equals(host.HostName, StringComparison.OrdinalIgnoreCase)); + return domain ?? store.PrimaryHost(); + } } \ No newline at end of file diff --git a/src/Core/Grand.Infrastructure/Caching/Constants/MessagesCacheKey.cs b/src/Core/Grand.Infrastructure/Caching/Constants/MessagesCacheKey.cs index 6339f6c409..29568bd759 100644 --- a/src/Core/Grand.Infrastructure/Caching/Constants/MessagesCacheKey.cs +++ b/src/Core/Grand.Infrastructure/Caching/Constants/MessagesCacheKey.cs @@ -9,9 +9,10 @@ public static partial class CacheKey /// /// /// {0} : store ID - /// {1} : ignore ACL? + /// {1} : show hidden? + /// {2} : customer group IDs, empty when hidden attributes are shown /// - public static string CONTACTATTRIBUTES_ALL_KEY => "Grand.contactattribute.all-{0}-{1}"; + public static string CONTACTATTRIBUTES_ALL_KEY => "Grand.contactattribute.all-{0}-{1}-{2}"; /// /// Key for caching diff --git a/src/Plugins/Authentication.Facebook/Infrastructure/Cache/FacebookAuthenticationEventConsumer.cs b/src/Plugins/Authentication.Facebook/Infrastructure/Cache/FacebookAuthenticationEventConsumer.cs index 3bef435ae1..2d5c75209c 100644 --- a/src/Plugins/Authentication.Facebook/Infrastructure/Cache/FacebookAuthenticationEventConsumer.cs +++ b/src/Plugins/Authentication.Facebook/Infrastructure/Cache/FacebookAuthenticationEventConsumer.cs @@ -23,6 +23,7 @@ public class FacebookAuthenticationEventConsumer : INotificationHandler _contextAccessor.WorkContext.WorkingLanguage.Id; private Store CurrentStore => _contextAccessor.StoreContext.CurrentStore; + private DomainHost CurrentHost => _contextAccessor.StoreContext.CurrentHost; #endregion @@ -67,10 +68,12 @@ public async Task Handle(RegisteredByExternalMethod eventMessage, CancellationTo //notifications for admin if (_customerSettings.NotifyNewCustomerRegistration) - await _messageProviderService.SendCustomerRegisteredMessage(eventMessage.Customer, CurrentStore, WorkingLanguageId); + await _messageProviderService.SendCustomerRegisteredMessage(eventMessage.Customer, CurrentStore, WorkingLanguageId, + CurrentHost); //send welcome message - await _messageProviderService.SendCustomerWelcomeMessage(eventMessage.Customer, CurrentStore, WorkingLanguageId); + await _messageProviderService.SendCustomerWelcomeMessage(eventMessage.Customer, CurrentStore, WorkingLanguageId, + CurrentHost); } #endregion } \ No newline at end of file diff --git a/src/Plugins/Authentication.Google/Infrastructure/GoogleAuthenticationEventConsumer.cs b/src/Plugins/Authentication.Google/Infrastructure/GoogleAuthenticationEventConsumer.cs index bfb44a73ee..24c4d16695 100644 --- a/src/Plugins/Authentication.Google/Infrastructure/GoogleAuthenticationEventConsumer.cs +++ b/src/Plugins/Authentication.Google/Infrastructure/GoogleAuthenticationEventConsumer.cs @@ -22,6 +22,7 @@ public class GoogleAuthenticationEventConsumer : INotificationHandler _contextAccessor.WorkContext.WorkingLanguage.Id; private Store CurrentStore => _contextAccessor.StoreContext.CurrentStore; + private DomainHost CurrentHost => _contextAccessor.StoreContext.CurrentHost; #endregion #region Ctor @@ -66,10 +67,12 @@ public async Task Handle(RegisteredByExternalMethod eventMessage, CancellationTo //notifications for admin if (_customerSettings.NotifyNewCustomerRegistration) - await _messageProviderService.SendCustomerRegisteredMessage(eventMessage.Customer, CurrentStore, WorkingLanguageId); + await _messageProviderService.SendCustomerRegisteredMessage(eventMessage.Customer, CurrentStore, WorkingLanguageId, + CurrentHost); //send welcome message - await _messageProviderService.SendCustomerWelcomeMessage(eventMessage.Customer, CurrentStore, WorkingLanguageId); + await _messageProviderService.SendCustomerWelcomeMessage(eventMessage.Customer, CurrentStore, WorkingLanguageId, + CurrentHost); } #endregion diff --git a/src/Tests/Grand.Business.Marketing.Tests/Services/Contacts/ContactAttributeParserTests.cs b/src/Tests/Grand.Business.Marketing.Tests/Services/Contacts/ContactAttributeParserTests.cs index 2a15794706..14064ad672 100644 --- a/src/Tests/Grand.Business.Marketing.Tests/Services/Contacts/ContactAttributeParserTests.cs +++ b/src/Tests/Grand.Business.Marketing.Tests/Services/Contacts/ContactAttributeParserTests.cs @@ -1,12 +1,13 @@ using Grand.Business.Core.Interfaces.Common.Localization; using Grand.Business.Core.Interfaces.Marketing.Contacts; +using Grand.Business.Core.Utilities.Catalog; using Grand.Business.Marketing.Services.Contacts; using Grand.Domain.Catalog; using Grand.Domain.Common; using Grand.Domain.Customers; using Grand.Domain.Localization; using Grand.Domain.Messages; -using Grand.Infrastructure; +using Grand.Domain.Stores; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; @@ -18,20 +19,18 @@ public class ContactAttributeParserTests private List _contactAtr; private Mock _contactAttributeServiceMock; private ContactAttributeParser _parser; - private Mock _workContextMock; private List customAtr; [TestInitialize] public void Init() { _contactAttributeServiceMock = new Mock(); - _workContextMock = new Mock(); var _translationServiceMock = new Mock(); { _translationServiceMock.Setup(x => x.GetResource(It.IsAny())).Returns("Warning{0}"); } - _parser = new ContactAttributeParser(_contactAttributeServiceMock.Object, _workContextMock.Object); + _parser = new ContactAttributeParser(_contactAttributeServiceMock.Object); customAtr = [ new CustomAttribute { Key = "key1", Value = "value1" }, new CustomAttribute { Key = "key2", Value = "value2" }, @@ -120,14 +119,54 @@ public void RemoveContactAttributeTest() [TestMethod] public async Task FormatAttributesTest() { - _contactAttributeServiceMock.Setup(c => c.GetAllContactAttributes("", false)) - .Returns(() => Task.FromResult((IList)_contactAtr)); _contactAttributeServiceMock.Setup(c => c.GetContactAttributeById(It.IsAny())).Returns((string w) => Task.FromResult(_contactAtr.FirstOrDefault(a => a.Id.Equals(w)))); var result = await _parser.FormatAttributes(new Language(), new List { new() { Key = "key1", Value = "value1" } - }, new Customer()); + }, null); Assert.AreEqual("name1: value1", result); } + + [TestMethod] + public async Task FormatAttributes_FileUpload_LinksToTheGivenHost() + { + var result = await FormatFileUpload(new DomainHost { Url = "https://shop.example/" }); + + StringAssert.Contains(result, "href=\"https://shop.example/download/getfileupload/?downloadId="); + } + + [TestMethod] + public async Task FormatAttributes_FileUploadWithoutHost_NamesTheFileWithoutALink() + { + var downloadGuid = Guid.NewGuid(); + + var result = await FormatFileUpload(null, downloadGuid); + + Assert.AreEqual($"file: download ({downloadGuid})", result); + } + + [TestMethod] + public async Task GetParseContactAttributes_ReadsTheAttributesOfTheGivenAccess() + { + var access = CatalogAccess.For(new Customer(), "store1"); + _contactAttributeServiceMock.Setup(c => c.GetAllContactAttributes(access)) + .ReturnsAsync(_contactAtr.Take(1).ToList()); + + var result = await _parser.GetParseContactAttributes( + [new CustomAttribute { Key = "key1", Value = " text " }], access); + + Assert.HasCount(1, result); + Assert.AreEqual("text", result[0].Value); + } + + private async Task FormatFileUpload(DomainHost host, Guid? downloadGuid = null) + { + var attribute = new ContactAttribute { + Id = "file", Name = "file", TextPrompt = "download", AttributeControlType = AttributeControlType.FileUpload + }; + _contactAttributeServiceMock.Setup(c => c.GetContactAttributeById("file")).ReturnsAsync(attribute); + return await _parser.FormatAttributes(new Language(), + [new CustomAttribute { Key = "file", Value = (downloadGuid ?? Guid.NewGuid()).ToString() }], host); + } } \ No newline at end of file diff --git a/src/Tests/Grand.Business.Marketing.Tests/Services/Contacts/ContactAttributeServiceTests.cs b/src/Tests/Grand.Business.Marketing.Tests/Services/Contacts/ContactAttributeServiceTests.cs index 62c5404d2e..ae43ce5309 100644 --- a/src/Tests/Grand.Business.Marketing.Tests/Services/Contacts/ContactAttributeServiceTests.cs +++ b/src/Tests/Grand.Business.Marketing.Tests/Services/Contacts/ContactAttributeServiceTests.cs @@ -1,10 +1,9 @@ -using Grand.Business.Marketing.Services.Contacts; +using Grand.Business.Core.Utilities.Catalog; +using Grand.Business.Marketing.Services.Contacts; using Grand.Data; using Grand.Data.Tests.MongoDb; using Grand.Domain.Customers; using Grand.Domain.Messages; -using Grand.Domain.Stores; -using Grand.Infrastructure; using Grand.Infrastructure.Caching; using Grand.Infrastructure.Configuration; using Grand.Infrastructure.Tests.Caching; @@ -21,21 +20,17 @@ public class ContactAttributeServiceTests private ContactAttributeService _contactAttributeService; private Mock _mediatorMock; private IRepository _repository; - private Mock _workContextMock; [TestInitialize] public void Init() { _repository = new MongoDBRepositoryTest(); _mediatorMock = new Mock(); - _workContextMock = new Mock(); - _workContextMock.Setup(c => c.StoreContext.CurrentStore).Returns(() => new Store { Id = "" }); - _workContextMock.Setup(c => c.WorkContext.CurrentCustomer).Returns(() => new Customer()); var accessControlConfig = new AccessControlConfig(); _cacheBase = new MemoryCacheBase(MemoryCacheTest.Get(), _mediatorMock.Object, new CacheConfig { DefaultCacheTimeMinutes = 1 }); _contactAttributeService = new ContactAttributeService(_cacheBase, _repository, _mediatorMock.Object, - _workContextMock.Object, accessControlConfig); + accessControlConfig); } [TestMethod] @@ -64,12 +59,67 @@ public async Task GetAllContactAttributesTest() await _contactAttributeService.InsertContactAttribute(new ContactAttribute()); //Act - var result = await _contactAttributeService.GetAllContactAttributes(); + var result = await _contactAttributeService.GetAllContactAttributes(CatalogAccess.For(new Customer(), "")); //Assert Assert.HasCount(3, result); } + [TestMethod] + public async Task GetAllContactAttributes_CustomersInDifferentGroups_SeeTheirOwnAttributes() + { + //Arrange + await _contactAttributeService.InsertContactAttribute(new ContactAttribute { Name = "public" }); + await _contactAttributeService.InsertContactAttribute(new ContactAttribute { + Name = "vip", LimitedToGroups = true, CustomerGroups = ["vip"] + }); + var vip = new Customer(); + vip.Groups.Add("vip"); + var guest = new Customer(); + guest.Groups.Add("guests"); + + //Act + var forVip = await _contactAttributeService.GetAllContactAttributes(CatalogAccess.For(vip, "")); + var forGuest = await _contactAttributeService.GetAllContactAttributes(CatalogAccess.For(guest, "")); + + //Assert + Assert.HasCount(2, forVip); + Assert.HasCount(1, forGuest); + Assert.AreEqual("public", forGuest[0].Name); + } + + [TestMethod] + public async Task GetAllContactAttributes_IncludingHidden_ReturnsGroupLimitedAttributes() + { + //Arrange + await _contactAttributeService.InsertContactAttribute(new ContactAttribute { + Name = "vip", LimitedToGroups = true, CustomerGroups = ["vip"] + }); + + //Act + var result = await _contactAttributeService.GetAllContactAttributes(CatalogAccess.IncludingHidden()); + + //Assert + Assert.HasCount(1, result); + } + + [TestMethod] + public async Task GetAllContactAttributes_Store_LeavesOutAttributesOfOtherStores() + { + //Arrange + await _contactAttributeService.InsertContactAttribute(new ContactAttribute { + Name = "other", LimitedToStores = true, Stores = ["store2"] + }); + await _contactAttributeService.InsertContactAttribute(new ContactAttribute { Name = "all" }); + + //Act + var result = await _contactAttributeService.GetAllContactAttributes(CatalogAccess.IncludingHidden("store1")); + + //Assert + Assert.HasCount(1, result); + Assert.AreEqual("all", result[0].Name); + } + [TestMethod] public async Task GetContactAttributeByIdTest() { diff --git a/src/Tests/Grand.Business.Marketing.Tests/Services/Customers/CustomerCoordinatesServiceTests.cs b/src/Tests/Grand.Business.Marketing.Tests/Services/Customers/CustomerCoordinatesServiceTests.cs index 5cfb417523..4d45a63fd9 100644 --- a/src/Tests/Grand.Business.Marketing.Tests/Services/Customers/CustomerCoordinatesServiceTests.cs +++ b/src/Tests/Grand.Business.Marketing.Tests/Services/Customers/CustomerCoordinatesServiceTests.cs @@ -3,8 +3,6 @@ using Grand.Data; using Grand.Data.Tests.MongoDb; using Grand.Domain.Customers; -using Grand.Domain.Stores; -using Grand.Infrastructure; using Grand.Mediator; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; @@ -14,36 +12,27 @@ namespace Grand.Business.Marketing.Tests.Services.Customers; [TestClass] public class CustomerCoordinatesServiceTests { - private Customer _customer; private CustomerCoordinatesService _customerCoordinatesService; private Mock _mediatorMock; private IRepository _repository; - private Mock _workContextMock; [TestInitialize] public void Init() { _repository = new MongoDBRepositoryTest(); _mediatorMock = new Mock(); - _workContextMock = new Mock(); - _workContextMock.Setup(c => c.StoreContext.CurrentStore).Returns(() => new Store { Id = "" }); - _customer = new Customer(); - _workContextMock.Setup(c => c.WorkContext.CurrentCustomer).Returns(() => _customer); - _customerCoordinatesService = - new CustomerCoordinatesService(_repository, _workContextMock.Object, _mediatorMock.Object); + _customerCoordinatesService = new CustomerCoordinatesService(_repository, _mediatorMock.Object); } [TestMethod] - public async Task GetGeoCoordinateTest_WorkContext() + public async Task GetGeoCoordinateTest_CustomerWithoutCoordinates_ReturnsZero() { - //Arrange - await _customerCoordinatesService.SaveGeoCoordinate(1, 2); //Act - var result = await _customerCoordinatesService.GetGeoCoordinate(); + var result = await _customerCoordinatesService.GetGeoCoordinate(new Customer()); //Assert - Assert.AreEqual(1, result.longitude); - Assert.AreEqual(2, result.latitude); + Assert.AreEqual(0, result.longitude); + Assert.AreEqual(0, result.latitude); } [TestMethod] @@ -73,15 +62,4 @@ public async Task SaveGeoCoordinateTest_Customer() Assert.AreEqual(2, _repository.Table.FirstOrDefault(x => x.Id == customer.Id).Coordinates.Y); _mediatorMock.Verify(c => c.Publish(It.IsAny(), default), Times.Once); } - - [TestMethod] - public async Task SaveGeoCoordinateTest_WorkContext() - { - //Act - await _customerCoordinatesService.SaveGeoCoordinate(1, 2); - //Assert - Assert.AreEqual(1, _workContextMock.Object.WorkContext.CurrentCustomer.Coordinates.X); - Assert.AreEqual(2, _workContextMock.Object.WorkContext.CurrentCustomer.Coordinates.Y); - _mediatorMock.Verify(c => c.Publish(It.IsAny(), default), Times.Once); - } } \ No newline at end of file diff --git a/src/Tests/Grand.Business.Messages.Tests/Services/MessageProviderServiceTest.cs b/src/Tests/Grand.Business.Messages.Tests/Services/MessageProviderServiceTest.cs index 087d816510..165fa4b4f4 100644 --- a/src/Tests/Grand.Business.Messages.Tests/Services/MessageProviderServiceTest.cs +++ b/src/Tests/Grand.Business.Messages.Tests/Services/MessageProviderServiceTest.cs @@ -1,8 +1,10 @@ -using Grand.Business.Core.Interfaces.Common.Directory; +using Grand.Business.Core.Events.Messages; +using Grand.Business.Core.Interfaces.Common.Directory; using Grand.Business.Core.Interfaces.Common.Localization; using Grand.Business.Core.Interfaces.Common.Stores; using Grand.Business.Core.Interfaces.Messages; using Grand.Business.Core.Queries.Messages; +using Grand.Business.Core.Utilities.Messages.DotLiquidDrops; using Grand.Business.Messages.Services; using Grand.Domain; using Grand.Domain.Catalog; @@ -12,7 +14,6 @@ using Grand.Domain.Messages; using Grand.Domain.Stores; using Grand.Domain.Vendors; -using Grand.Infrastructure; using Grand.Mediator; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; @@ -33,7 +34,6 @@ public class MessageProviderServiceTest private Mock _messageTokenProviderMock; private Product _product; private Mock _queuedEmailServiceMock; - private Mock _contextAccessorMock; private Mock _storeServiceMock; [TestInitialize] @@ -63,8 +63,6 @@ public void Init() .Returns(Task.FromResult(new Store { Url = "https://localhost:44350/" })); _storeServiceMock.Setup(x => x.GetAllStores()).Returns(Task.FromResult(new List() as IList)); - _contextAccessorMock = new Mock(); - _contextAccessorMock.Setup(x => x.StoreContext.CurrentHost).Returns(new DomainHost { Url = "https://localhost:44350/" }); _mediatorMock = new Mock(); _mediatorMock.Setup(x => x.Send(It.IsAny(), default)) .Returns(Task.FromResult(new Customer { Email = "sdfsdf@mail.com" })); @@ -73,7 +71,6 @@ public void Init() _commonSetting = new Mock(); _messageService = new MessageProviderService( - _contextAccessorMock.Object, _messageTemplateServiceMock.Object, _queuedEmailServiceMock.Object, _languageServiceMock.Object, @@ -120,4 +117,41 @@ public async Task SendNotificationMethodToEmailIsNull() Assert.AreEqual(0, result); } -} \ No newline at end of file + + [TestMethod] + public async Task SendCustomerMessage_LinksPointToThePrimaryDomainOfTheStore() + { + var store = new Store { + Url = "https://store.example/", + Domains = { + new DomainHost { HostName = "second.example", Url = "https://second.example/" }, + new DomainHost { HostName = "primary.example", Url = "https://primary.example/", Primary = true } + } + }; + + var url = await PasswordRecoveryUrl(store); + + StringAssert.StartsWith(url, "https://primary.example/"); + } + + [TestMethod] + public async Task SendCustomerMessage_StoreWithoutDomains_LinksPointToTheStoreUrl() + { + var url = await PasswordRecoveryUrl(new Store { Url = "https://store.example/" }); + + StringAssert.StartsWith(url, "https://store.example/"); + } + + private async Task PasswordRecoveryUrl(Store store) + { + LiquidCustomer liquidCustomer = null; + _mediatorMock.Setup(x => x.Publish(It.IsAny>(), It.IsAny())) + .Callback, CancellationToken>((e, _) => + liquidCustomer = (LiquidCustomer)e.LiquidDrop); + + await _messageService.SendCustomerPasswordRecoveryMessage(new Customer { Email = "a@b.c" }, store, ""); + + Assert.IsNotNull(liquidCustomer); + return liquidCustomer.PasswordRecoveryURL; + } +} diff --git a/src/Tests/Grand.Domain.Tests/Stores/StoreExtensionsTests.cs b/src/Tests/Grand.Domain.Tests/Stores/StoreExtensionsTests.cs index fc609aedf9..fc3d8ae33e 100644 --- a/src/Tests/Grand.Domain.Tests/Stores/StoreExtensionsTests.cs +++ b/src/Tests/Grand.Domain.Tests/Stores/StoreExtensionsTests.cs @@ -28,6 +28,45 @@ public void ContainsHostValueTest_False() Assert.IsFalse(store.ContainsHostValue("google")); } + [TestMethod] + public void PrimaryHost_ReturnsThePrimaryDomain() + { + store.Domains[1].Primary = true; + + Assert.AreEqual("yahoo.com", store.PrimaryHost().HostName); + } + + [TestMethod] + public void PrimaryHost_WithoutAPrimaryDomain_ReturnsTheFirstDomain() + { + Assert.AreEqual("google.com", store.PrimaryHost().HostName); + } + + [TestMethod] + public void PrimaryHost_WithoutDomains_FallsBackToTheStoreUrl() + { + var withoutDomains = new Store { Url = "https://shop.example/", Domains = null }; + + Assert.AreEqual("https://shop.example/", withoutDomains.PrimaryHost().Url); + } + + [TestMethod] + public void LinkHost_ReturnsTheRequestHostWhenItIsOneOfTheStoreDomains() + { + store.Domains[0].Primary = true; + + Assert.AreEqual("yahoo.com", store.LinkHost(new DomainHost { HostName = "Yahoo.com" }).HostName); + } + + [TestMethod] + public void LinkHost_ForeignOrMissingHost_ReturnsThePrimaryDomain() + { + store.Domains[1].Primary = true; + + Assert.AreEqual("yahoo.com", store.LinkHost(new DomainHost { HostName = "other.example" }).HostName); + Assert.AreEqual("yahoo.com", store.LinkHost(null).HostName); + } + [TestMethod] public void HostValueTest_NotNull() { diff --git a/src/Web/Grand.Web.AdminShared/Services/ContactAttributeViewModelService.cs b/src/Web/Grand.Web.AdminShared/Services/ContactAttributeViewModelService.cs index fbdea8e259..5c091efeae 100644 --- a/src/Web/Grand.Web.AdminShared/Services/ContactAttributeViewModelService.cs +++ b/src/Web/Grand.Web.AdminShared/Services/ContactAttributeViewModelService.cs @@ -1,4 +1,5 @@ using Grand.Business.Core.Interfaces.Marketing.Contacts; +using Grand.Business.Core.Utilities.Catalog; using Grand.Domain.Catalog; using Grand.Domain.Common; using Grand.Domain.Messages; @@ -21,7 +22,7 @@ public class ContactAttributeViewModelService( public virtual async Task> PrepareContactAttributeListModel() { var contactAttributes = - await contactAttributeService.GetAllContactAttributes(contextAccessor.WorkContext.CurrentCustomer.StaffStoreId, true); + await contactAttributeService.GetAllContactAttributes(CatalogAccess.IncludingHidden(contextAccessor.WorkContext.CurrentCustomer.StaffStoreId)); return contactAttributes.Select(x => { var attributeModel = x.ToModel(); @@ -49,7 +50,7 @@ public virtual async Task PrepareConditionAttributes(ContactAttributeModel model EnableCondition = contactAttribute.ConditionAttribute.Any(), SelectedAttributeId = selectedAttribute != null ? selectedAttribute.Id : "", ConditionAttributes = - (await contactAttributeService.GetAllContactAttributes(contextAccessor.WorkContext.CurrentCustomer.StaffStoreId, true)) + (await contactAttributeService.GetAllContactAttributes(CatalogAccess.IncludingHidden(contextAccessor.WorkContext.CurrentCustomer.StaffStoreId))) //ignore this attribute and non-combinable attributes .Where(x => x.Id != contactAttribute.Id && x.CanBeUsedAsCondition()) .Select(x => diff --git a/src/Web/Grand.Web/Commands/Handler/Blogs/InsertBlogCommentCommandHandler.cs b/src/Web/Grand.Web/Commands/Handler/Blogs/InsertBlogCommentCommandHandler.cs index 33507ac323..b727efdaa8 100644 --- a/src/Web/Grand.Web/Commands/Handler/Blogs/InsertBlogCommentCommandHandler.cs +++ b/src/Web/Grand.Web/Commands/Handler/Blogs/InsertBlogCommentCommandHandler.cs @@ -52,7 +52,7 @@ public async Task Handle(InsertBlogCommentCommand request, Cancella //notify a store owner if (_blogSettings.NotifyAboutNewBlogComments) await _messageProviderService.SendBlogCommentMessage(request.BlogPost, comment, - _languageSettings.DefaultAdminLanguageId); + _languageSettings.DefaultAdminLanguageId, host: _contextAccessor.StoreContext.CurrentHost); return comment; } diff --git a/src/Web/Grand.Web/Commands/Handler/Contact/ContactAttributeChangeCommandHandler.cs b/src/Web/Grand.Web/Commands/Handler/Contact/ContactAttributeChangeCommandHandler.cs index 81f66e0af4..b3359e6f37 100644 --- a/src/Web/Grand.Web/Commands/Handler/Contact/ContactAttributeChangeCommandHandler.cs +++ b/src/Web/Grand.Web/Commands/Handler/Contact/ContactAttributeChangeCommandHandler.cs @@ -1,4 +1,5 @@ using Grand.Business.Core.Interfaces.Marketing.Contacts; +using Grand.Business.Core.Utilities.Catalog; using Grand.Domain.Catalog; using Grand.Domain.Common; using Grand.Web.Commands.Models.Contact; @@ -26,7 +27,7 @@ public ContactAttributeChangeCommandHandler(IContactAttributeService contactAttr var enabledAttributeIds = new List(); var disabledAttributeIds = new List(); - var attributes = await _contactAttributeService.GetAllContactAttributes(request.Store.Id); + var attributes = await _contactAttributeService.GetAllContactAttributes(CatalogAccess.For(request.Customer, request.Store.Id)); foreach (var attribute in attributes) { var conditionMet = await _contactAttributeParser.IsConditionMet(attribute, customAttributes); @@ -43,7 +44,7 @@ public ContactAttributeChangeCommandHandler(IContactAttributeService contactAttr private async Task> ParseContactAttributes(ContactAttributeChangeCommand request) { var customAttributes = new List(); - var contactAttributes = await _contactAttributeService.GetAllContactAttributes(request.Store.Id); + var contactAttributes = await _contactAttributeService.GetAllContactAttributes(CatalogAccess.For(request.Customer, request.Store.Id)); foreach (var attribute in contactAttributes) switch (attribute.AttributeControlType) { diff --git a/src/Web/Grand.Web/Commands/Handler/Contact/ContactUsCommandHandler.cs b/src/Web/Grand.Web/Commands/Handler/Contact/ContactUsCommandHandler.cs index f8f95eef60..9ac1531bdb 100644 --- a/src/Web/Grand.Web/Commands/Handler/Contact/ContactUsCommandHandler.cs +++ b/src/Web/Grand.Web/Commands/Handler/Contact/ContactUsCommandHandler.cs @@ -1,5 +1,6 @@ using Grand.Business.Core.Extensions; using Grand.Business.Core.Interfaces.Marketing.Contacts; +using Grand.Business.Core.Utilities.Catalog; using Grand.Domain.Catalog; using Grand.Domain.Common; using Grand.Domain.Customers; @@ -47,7 +48,7 @@ public async Task Handle(ContactUsCommand request, CancellationT { var model = new List(); - var contactAttributes = await _contactAttributeService.GetAllContactAttributes(request.Store.Id); + var contactAttributes = await _contactAttributeService.GetAllContactAttributes(CatalogAccess.For(request.Customer, request.Store.Id)); foreach (var attribute in contactAttributes) { var attributeModel = new ContactUsModel.ContactAttributeModel { diff --git a/src/Web/Grand.Web/Commands/Handler/Contact/ContactUsSendCommandHandler.cs b/src/Web/Grand.Web/Commands/Handler/Contact/ContactUsSendCommandHandler.cs index f710d92e06..e92788fa0e 100644 --- a/src/Web/Grand.Web/Commands/Handler/Contact/ContactUsSendCommandHandler.cs +++ b/src/Web/Grand.Web/Commands/Handler/Contact/ContactUsSendCommandHandler.cs @@ -1,8 +1,9 @@ using Grand.Business.Core.Interfaces.Common.Localization; using Grand.Business.Core.Interfaces.Marketing.Contacts; using Grand.Business.Core.Interfaces.Messages; +using Grand.Business.Core.Utilities.Catalog; using Grand.Domain.Common; -using Grand.Infrastructure; +using Grand.Domain.Stores; using Grand.SharedKernel.Extensions; using Grand.Web.Commands.Models.Contact; using Grand.Web.Models.Contact; @@ -16,15 +17,13 @@ public class ContactUsSendCommandHandler : IRequestHandler Handle(ContactUsSendCommand request, CancellationToken cancellationToken) { var attributes = await _contactAttributeParser.GetParseContactAttributes(request.Model.Attributes - .Select(x => new CustomAttribute { Key = x.Key, Value = x.Value }).ToList()); + .Select(x => new CustomAttribute { Key = x.Key, Value = x.Value }).ToList(), + CatalogAccess.For(request.Customer, request.Store.Id)); request.Model.ContactAttribute = attributes; request.Model.ContactAttributeInfo = - await _contactAttributeParser.FormatAttributes(_contextAccessor.WorkContext.WorkingLanguage, attributes, - _contextAccessor.WorkContext.CurrentCustomer); - request.Model = await SendContactUs(request, _contextAccessor.StoreContext.CurrentStore); + await _contactAttributeParser.FormatAttributes(request.Language, attributes, + request.Store.LinkHost(request.Host)); + request.Model = await SendContactUs(request); return request.Model; } - private async Task SendContactUs(ContactUsSendCommand request, Domain.Stores.Store store) + private async Task SendContactUs(ContactUsSendCommand request) { var subject = _commonSettings.SubjectFieldOnContactUsForm ? request.Model.Subject : null; var body = FormatText.ConvertText(request.Model.Enquiry); await _messageProviderService.SendContactUsMessage - (_contextAccessor.WorkContext.CurrentCustomer, store, _contextAccessor.WorkContext.WorkingLanguage.Id, request.Model.Email.Trim(), + (request.Customer, request.Store, request.Language.Id, request.Model.Email.Trim(), request.Model.FullName, subject, - body, request.Model.ContactAttributeInfo, request.Model.ContactAttribute, request.IpAddress); + body, request.Model.ContactAttributeInfo, request.Model.ContactAttribute, request.IpAddress, + host: request.Host); request.Model.SuccessfullySent = true; - request.Model.Result = _translationService.GetResource("ContactUs.YourEnquiryHasBeenSent"); + request.Model.Result = _translationService.GetResource("ContactUs.YourEnquiryHasBeenSent", request.Language.Id); return request.Model; } diff --git a/src/Web/Grand.Web/Commands/Handler/Customers/CustomerRegisteredCommandHandler.cs b/src/Web/Grand.Web/Commands/Handler/Customers/CustomerRegisteredCommandHandler.cs index 89411e7232..f69a4aa29b 100644 --- a/src/Web/Grand.Web/Commands/Handler/Customers/CustomerRegisteredCommandHandler.cs +++ b/src/Web/Grand.Web/Commands/Handler/Customers/CustomerRegisteredCommandHandler.cs @@ -184,7 +184,7 @@ await _newsLetterSubscriptionService.GetNewsLetterSubscriptionByEmailAndStoreId( //notifications if (_customerSettings.NotifyNewCustomerRegistration) await _messageProviderService.SendCustomerRegisteredMessage(request.Customer, request.Store, - _languageSettings.DefaultAdminLanguageId); + _languageSettings.DefaultAdminLanguageId, host: request.Host); //New customer has a free shipping for the first order if (_customerSettings.RegistrationFreeShipping) diff --git a/src/Web/Grand.Web/Commands/Handler/Customers/DeleteAccountCommandHandler.cs b/src/Web/Grand.Web/Commands/Handler/Customers/DeleteAccountCommandHandler.cs index 1fb4e1aff5..f53d42701a 100644 --- a/src/Web/Grand.Web/Commands/Handler/Customers/DeleteAccountCommandHandler.cs +++ b/src/Web/Grand.Web/Commands/Handler/Customers/DeleteAccountCommandHandler.cs @@ -33,7 +33,7 @@ public async Task Handle(DeleteAccountCommand request, CancellationToken c { //send notification to customer await _messageProviderService.SendCustomerDeleteStoreOwnerMessage(request.Customer, - _languageSettings.DefaultAdminLanguageId); + _languageSettings.DefaultAdminLanguageId, host: request.Host); //delete emails await _queuedEmailService.DeleteCustomerEmail(request.Customer.Email); diff --git a/src/Web/Grand.Web/Commands/Handler/Customers/PasswordRecoverySendCommandHandler.cs b/src/Web/Grand.Web/Commands/Handler/Customers/PasswordRecoverySendCommandHandler.cs index 15a2a12e78..50a0be2f58 100644 --- a/src/Web/Grand.Web/Commands/Handler/Customers/PasswordRecoverySendCommandHandler.cs +++ b/src/Web/Grand.Web/Commands/Handler/Customers/PasswordRecoverySendCommandHandler.cs @@ -42,7 +42,7 @@ await _customerService.UpdateUserField(request.Customer, SystemCustomerF //send email await _messageProviderService.SendCustomerPasswordRecoveryMessage(request.Customer, request.Store, - request.Language.Id); + request.Language.Id, host: request.Host); return true; } diff --git a/src/Web/Grand.Web/Commands/Handler/News/InsertNewsCommentCommandHandler.cs b/src/Web/Grand.Web/Commands/Handler/News/InsertNewsCommentCommandHandler.cs index 7f48ff0f84..5337d3112a 100644 --- a/src/Web/Grand.Web/Commands/Handler/News/InsertNewsCommentCommandHandler.cs +++ b/src/Web/Grand.Web/Commands/Handler/News/InsertNewsCommentCommandHandler.cs @@ -53,7 +53,7 @@ public async Task Handle(InsertNewsCommentCommand request, Cancella //notify a store owner; if (_newsSettings.NotifyAboutNewNewsComments) await _messageProviderService.SendNewsCommentMessage(request.NewsItem, comment, - _languageSettings.DefaultAdminLanguageId); + _languageSettings.DefaultAdminLanguageId, host: _contextAccessor.StoreContext.CurrentHost); return comment; } diff --git a/src/Web/Grand.Web/Commands/Handler/Newsletter/SubscribeNewsletterHandler.cs b/src/Web/Grand.Web/Commands/Handler/Newsletter/SubscribeNewsletterHandler.cs index 0ac363c1da..07f42e4cbd 100644 --- a/src/Web/Grand.Web/Commands/Handler/Newsletter/SubscribeNewsletterHandler.cs +++ b/src/Web/Grand.Web/Commands/Handler/Newsletter/SubscribeNewsletterHandler.cs @@ -55,14 +55,16 @@ await _newsLetterSubscriptionService.GetNewsLetterSubscriptionByEmailAndStoreId( { if (!subscription.Active) await _messageProviderService.SendNewsLetterSubscriptionActivationMessage(subscription, - _contextAccessor.WorkContext.WorkingLanguage.Id); + _contextAccessor.WorkContext.WorkingLanguage.Id, + host: _contextAccessor.StoreContext.CurrentHost); model.Result = _translationService.GetResource("Newsletter.SubscribeEmailSent"); } else { if (subscription.Active) await _messageProviderService.SendNewsLetterSubscriptionDeactivationMessage(subscription, - _contextAccessor.WorkContext.WorkingLanguage.Id); + _contextAccessor.WorkContext.WorkingLanguage.Id, + host: _contextAccessor.StoreContext.CurrentHost); model.Result = _translationService.GetResource("Newsletter.UnsubscribeEmailSent"); } } @@ -78,7 +80,7 @@ await _messageProviderService.SendNewsLetterSubscriptionDeactivationMessage(subs await _newsLetterSubscriptionService.InsertNewsLetterSubscription(subscription); await _messageProviderService.SendNewsLetterSubscriptionActivationMessage(subscription, - _contextAccessor.WorkContext.WorkingLanguage.Id); + _contextAccessor.WorkContext.WorkingLanguage.Id, host: _contextAccessor.StoreContext.CurrentHost); model.Result = _translationService.GetResource("Newsletter.SubscribeEmailSent"); var modelCategory = await PrepareNewsletterCategory(subscription.Id); diff --git a/src/Web/Grand.Web/Commands/Handler/Orders/InsertOrderNoteCommandHandler.cs b/src/Web/Grand.Web/Commands/Handler/Orders/InsertOrderNoteCommandHandler.cs index d9874ab64c..e89823e69d 100644 --- a/src/Web/Grand.Web/Commands/Handler/Orders/InsertOrderNoteCommandHandler.cs +++ b/src/Web/Grand.Web/Commands/Handler/Orders/InsertOrderNoteCommandHandler.cs @@ -28,7 +28,8 @@ public async Task Handle(InsertOrderNoteCommand request, Cancellation await _orderService.InsertOrderNote(orderNote); //email - await _messageProviderService.SendNewOrderNoteAddedCustomerMessage(request.Order, orderNote); + await _messageProviderService.SendNewOrderNoteAddedCustomerMessage(request.Order, orderNote, + host: request.Host); return orderNote; } diff --git a/src/Web/Grand.Web/Commands/Handler/Orders/MerchandiseReturnSubmitCommandHandler.cs b/src/Web/Grand.Web/Commands/Handler/Orders/MerchandiseReturnSubmitCommandHandler.cs index a078dfe56d..d0ae78bf22 100644 --- a/src/Web/Grand.Web/Commands/Handler/Orders/MerchandiseReturnSubmitCommandHandler.cs +++ b/src/Web/Grand.Web/Commands/Handler/Orders/MerchandiseReturnSubmitCommandHandler.cs @@ -86,10 +86,10 @@ public async Task Handle( //notify store owner here (email) await _messageProviderService.SendNewMerchandiseReturnStoreOwnerMessage(rr, request.Order, - _languageSettings.DefaultAdminLanguageId); + _languageSettings.DefaultAdminLanguageId, host: _contextAccessor.StoreContext.CurrentHost); //notify customer await _messageProviderService.SendNewMerchandiseReturnCustomerMessage(rr, request.Order, - request.Order.CustomerLanguageId); + request.Order.CustomerLanguageId, host: _contextAccessor.StoreContext.CurrentHost); return rr; } diff --git a/src/Web/Grand.Web/Commands/Handler/Products/InsertProductReviewCommandHandler.cs b/src/Web/Grand.Web/Commands/Handler/Products/InsertProductReviewCommandHandler.cs index a5c6d75f8a..03b6e7466d 100644 --- a/src/Web/Grand.Web/Commands/Handler/Products/InsertProductReviewCommandHandler.cs +++ b/src/Web/Grand.Web/Commands/Handler/Products/InsertProductReviewCommandHandler.cs @@ -76,7 +76,7 @@ public async Task Handle(InsertProductReviewCommand request, Canc //notify store owner if (_catalogSettings.NotifyStoreOwnerAboutNewProductReviews) await _messageProviderService.SendProductReviewMessage(request.Product, productReview, request.Store, - _languageSettings.DefaultAdminLanguageId); + _languageSettings.DefaultAdminLanguageId, host: request.Host); return productReview; } diff --git a/src/Web/Grand.Web/Commands/Handler/Products/SendProductAskQuestionMessageCommandHandler.cs b/src/Web/Grand.Web/Commands/Handler/Products/SendProductAskQuestionMessageCommandHandler.cs index 83520d81ed..51f70bac44 100644 --- a/src/Web/Grand.Web/Commands/Handler/Products/SendProductAskQuestionMessageCommandHandler.cs +++ b/src/Web/Grand.Web/Commands/Handler/Products/SendProductAskQuestionMessageCommandHandler.cs @@ -18,7 +18,7 @@ public async Task Handle(SendProductAskQuestionMessageCommand request, Can { await _messageProviderService.SendProductQuestionMessage(request.Customer, request.Store, request.Language.Id, request.Product, request.Model.Email, request.Model.FullName, request.Model.Phone, - FormatText.ConvertText(request.Model.Message), request.RemoteIpAddress); + FormatText.ConvertText(request.Model.Message), request.RemoteIpAddress, host: request.Host); return true; } diff --git a/src/Web/Grand.Web/Commands/Handler/Products/SendProductEmailAFriendMessageCommandHandler.cs b/src/Web/Grand.Web/Commands/Handler/Products/SendProductEmailAFriendMessageCommandHandler.cs index 1b07274228..859c35e073 100644 --- a/src/Web/Grand.Web/Commands/Handler/Products/SendProductEmailAFriendMessageCommandHandler.cs +++ b/src/Web/Grand.Web/Commands/Handler/Products/SendProductEmailAFriendMessageCommandHandler.cs @@ -19,7 +19,7 @@ public async Task Handle(SendProductEmailAFriendMessageCommand request, Ca await _messageProviderService.SendProductEmailAFriendMessage(request.Customer, request.Store, request.Language.Id, request.Product, request.Model.YourEmailAddress, request.Model.FriendEmail, - FormatText.ConvertText(request.Model.PersonalMessage)); + FormatText.ConvertText(request.Model.PersonalMessage), host: request.Host); return true; } diff --git a/src/Web/Grand.Web/Commands/Handler/Vendors/ContactVendorCommandHandler.cs b/src/Web/Grand.Web/Commands/Handler/Vendors/ContactVendorCommandHandler.cs index 146dc56909..12bc5db696 100644 --- a/src/Web/Grand.Web/Commands/Handler/Vendors/ContactVendorCommandHandler.cs +++ b/src/Web/Grand.Web/Commands/Handler/Vendors/ContactVendorCommandHandler.cs @@ -32,7 +32,8 @@ public async Task Handle(ContactVendorSendCommand request, C await _messageProviderService.SendContactVendorMessage(_contextAccessor.WorkContext.CurrentCustomer, request.Store, request.Vendor, _contextAccessor.WorkContext.WorkingLanguage.Id, - request.Model.Email.Trim(), request.Model.FullName, subject, body, request.IpAddress); + request.Model.Email.Trim(), request.Model.FullName, subject, body, request.IpAddress, + host: _contextAccessor.StoreContext.CurrentHost); request.Model.SuccessfullySent = true; request.Model.Result = _translationService.GetResource("ContactVendor.YourEnquiryHasBeenSent"); diff --git a/src/Web/Grand.Web/Commands/Handler/Vendors/InsertVendorReviewCommandHandler.cs b/src/Web/Grand.Web/Commands/Handler/Vendors/InsertVendorReviewCommandHandler.cs index 5a211190f4..09437083fc 100644 --- a/src/Web/Grand.Web/Commands/Handler/Vendors/InsertVendorReviewCommandHandler.cs +++ b/src/Web/Grand.Web/Commands/Handler/Vendors/InsertVendorReviewCommandHandler.cs @@ -60,7 +60,7 @@ public async Task Handle(InsertVendorReviewCommand request, Cancel //notify store owner if (_vendorSettings.NotifyVendorAboutNewVendorReviews) await _messageProviderService.SendVendorReviewMessage(vendorReview, request.Store, - _languageSettings.DefaultAdminLanguageId); + _languageSettings.DefaultAdminLanguageId, host: _contextAccessor.StoreContext.CurrentHost); return vendorReview; } diff --git a/src/Web/Grand.Web/Commands/Models/Contact/ContactUsSendCommand.cs b/src/Web/Grand.Web/Commands/Models/Contact/ContactUsSendCommand.cs index 57e33054e1..a8e144e9af 100644 --- a/src/Web/Grand.Web/Commands/Models/Contact/ContactUsSendCommand.cs +++ b/src/Web/Grand.Web/Commands/Models/Contact/ContactUsSendCommand.cs @@ -1,10 +1,17 @@ -using Grand.Web.Models.Contact; +using Grand.Domain.Customers; +using Grand.Domain.Localization; +using Grand.Domain.Stores; +using Grand.Web.Models.Contact; using Grand.Mediator; namespace Grand.Web.Commands.Models.Contact; public class ContactUsSendCommand : IRequest { + public Customer Customer { get; set; } + public Domain.Stores.Store Store { get; set; } + public Domain.Stores.DomainHost Host { get; set; } + public Language Language { get; set; } public ContactUsModel Model { get; set; } public string IpAddress { get; set; } } \ No newline at end of file diff --git a/src/Web/Grand.Web/Commands/Models/Customers/CustomerRegisteredCommand.cs b/src/Web/Grand.Web/Commands/Models/Customers/CustomerRegisteredCommand.cs index c47d5ac0aa..46a2dec659 100644 --- a/src/Web/Grand.Web/Commands/Models/Customers/CustomerRegisteredCommand.cs +++ b/src/Web/Grand.Web/Commands/Models/Customers/CustomerRegisteredCommand.cs @@ -9,6 +9,7 @@ public class CustomerRegisteredCommand : IRequest { public Customer Customer { get; set; } public Domain.Stores.Store Store { get; set; } + public Domain.Stores.DomainHost Host { get; set; } public RegisterModel Model { get; set; } public IList CustomerAttributes { get; set; } } \ No newline at end of file diff --git a/src/Web/Grand.Web/Commands/Models/Customers/DeleteAccountCommand.cs b/src/Web/Grand.Web/Commands/Models/Customers/DeleteAccountCommand.cs index 7eae751cc8..1203be1cd9 100644 --- a/src/Web/Grand.Web/Commands/Models/Customers/DeleteAccountCommand.cs +++ b/src/Web/Grand.Web/Commands/Models/Customers/DeleteAccountCommand.cs @@ -7,5 +7,6 @@ public class DeleteAccountCommand : IRequest { public Customer Customer { get; set; } public Domain.Stores.Store Store { get; set; } + public Domain.Stores.DomainHost Host { get; set; } public string IpAddress { get; set; } } \ No newline at end of file diff --git a/src/Web/Grand.Web/Commands/Models/Customers/PasswordRecoverySendCommand.cs b/src/Web/Grand.Web/Commands/Models/Customers/PasswordRecoverySendCommand.cs index e53ffd43cd..6d0d6a46e7 100644 --- a/src/Web/Grand.Web/Commands/Models/Customers/PasswordRecoverySendCommand.cs +++ b/src/Web/Grand.Web/Commands/Models/Customers/PasswordRecoverySendCommand.cs @@ -10,5 +10,6 @@ public class PasswordRecoverySendCommand : IRequest public PasswordRecoveryModel Model { get; set; } public Customer Customer { get; set; } public Domain.Stores.Store Store { get; set; } + public Domain.Stores.DomainHost Host { get; set; } public Language Language { get; set; } } \ No newline at end of file diff --git a/src/Web/Grand.Web/Commands/Models/Orders/InsertOrderNoteCommand.cs b/src/Web/Grand.Web/Commands/Models/Orders/InsertOrderNoteCommand.cs index 423c0477fe..6f43689ae9 100644 --- a/src/Web/Grand.Web/Commands/Models/Orders/InsertOrderNoteCommand.cs +++ b/src/Web/Grand.Web/Commands/Models/Orders/InsertOrderNoteCommand.cs @@ -10,4 +10,5 @@ public class InsertOrderNoteCommand : IRequest public Order Order { get; set; } public Language Language { get; set; } public AddOrderNoteModel OrderNote { get; set; } + public Domain.Stores.DomainHost Host { get; set; } } \ No newline at end of file diff --git a/src/Web/Grand.Web/Commands/Models/Products/InsertProductReviewCommand.cs b/src/Web/Grand.Web/Commands/Models/Products/InsertProductReviewCommand.cs index 02c723b8cc..7587ae8a5c 100644 --- a/src/Web/Grand.Web/Commands/Models/Products/InsertProductReviewCommand.cs +++ b/src/Web/Grand.Web/Commands/Models/Products/InsertProductReviewCommand.cs @@ -8,6 +8,7 @@ namespace Grand.Web.Commands.Models.Products; public class InsertProductReviewCommand : IRequest { public Domain.Stores.Store Store { get; set; } + public Domain.Stores.DomainHost Host { get; set; } public Customer Customer { get; set; } public Product Product { get; set; } public ProductReviewsModel Model { get; set; } diff --git a/src/Web/Grand.Web/Commands/Models/Products/SendProductAskQuestionMessageCommand.cs b/src/Web/Grand.Web/Commands/Models/Products/SendProductAskQuestionMessageCommand.cs index 723807d455..0ed29717ac 100644 --- a/src/Web/Grand.Web/Commands/Models/Products/SendProductAskQuestionMessageCommand.cs +++ b/src/Web/Grand.Web/Commands/Models/Products/SendProductAskQuestionMessageCommand.cs @@ -10,6 +10,7 @@ public class SendProductAskQuestionMessageCommand : IRequest { public Customer Customer { get; set; } public Domain.Stores.Store Store { get; set; } + public Domain.Stores.DomainHost Host { get; set; } public Language Language { get; set; } public Product Product { get; set; } public ProductAskQuestionModel Model { get; set; } diff --git a/src/Web/Grand.Web/Commands/Models/Products/SendProductEmailAFriendMessageCommand.cs b/src/Web/Grand.Web/Commands/Models/Products/SendProductEmailAFriendMessageCommand.cs index 2716b12521..b454816e00 100644 --- a/src/Web/Grand.Web/Commands/Models/Products/SendProductEmailAFriendMessageCommand.cs +++ b/src/Web/Grand.Web/Commands/Models/Products/SendProductEmailAFriendMessageCommand.cs @@ -10,6 +10,7 @@ public class SendProductEmailAFriendMessageCommand : IRequest { public Customer Customer { get; set; } public Domain.Stores.Store Store { get; set; } + public Domain.Stores.DomainHost Host { get; set; } public Language Language { get; set; } public Product Product { get; set; } public ProductEmailAFriendModel Model { get; set; } diff --git a/src/Web/Grand.Web/Controllers/AccountController.cs b/src/Web/Grand.Web/Controllers/AccountController.cs index 6314695ff5..74fdbb1c61 100644 --- a/src/Web/Grand.Web/Controllers/AccountController.cs +++ b/src/Web/Grand.Web/Controllers/AccountController.cs @@ -297,6 +297,7 @@ public async Task TwoFactorAuthorization() if (_customerSettings.TwoFactorAuthenticationType != TwoFactorAuthenticationType.AppVerification) await _mediator.Send(new GetTwoFactorAuthentication { + Host = _contextAccessor.StoreContext.CurrentHost, Customer = customer, Language = _contextAccessor.WorkContext.WorkingLanguage, Store = _contextAccessor.StoreContext.CurrentStore @@ -432,6 +433,7 @@ public virtual async Task> PasswordRecovery( var customer = await _customerService.GetCustomerByEmail(model.Email, CustomerStoreId); await _mediator.Send(new PasswordRecoverySendCommand { + Host = _contextAccessor.StoreContext.CurrentHost, Customer = customer, Store = _contextAccessor.StoreContext.CurrentStore, Language = _contextAccessor.WorkContext.WorkingLanguage, @@ -603,6 +605,7 @@ public virtual async Task Register(RegisterModel model, string re { SelectedAttributes = model.SelectedAttributes }); await _mediator.Send(new CustomerRegisteredCommand { + Host = _contextAccessor.StoreContext.CurrentHost, Customer = _contextAccessor.WorkContext.CurrentCustomer, CustomerAttributes = customerAttributes, Model = model, @@ -627,7 +630,8 @@ await _customerService.UpdateUserField(_contextAccessor.WorkContext.CurrentCusto SystemCustomerFieldNames.AccountActivationToken, Guid.NewGuid().ToString()); await _messageProviderService.SendCustomerEmailValidationMessage( _contextAccessor.WorkContext.CurrentCustomer, _contextAccessor.StoreContext.CurrentStore, - _contextAccessor.WorkContext.WorkingLanguage.Id); + _contextAccessor.WorkContext.WorkingLanguage.Id, + host: _contextAccessor.StoreContext.CurrentHost); //result result = RedirectToRoute("RegisterResult", @@ -644,7 +648,8 @@ await _messageProviderService.SendCustomerEmailValidationMessage( { //send customer welcome message await _messageProviderService.SendCustomerWelcomeMessage(_contextAccessor.WorkContext.CurrentCustomer, - _contextAccessor.StoreContext.CurrentStore, _contextAccessor.WorkContext.WorkingLanguage.Id); + _contextAccessor.StoreContext.CurrentStore, _contextAccessor.WorkContext.WorkingLanguage.Id, + host: _contextAccessor.StoreContext.CurrentHost); var redirectUrl = Url.RouteUrl("RegisterResult", new { resultId = (int)UserRegistrationType.Standard }, HttpContext.Request.Scheme); @@ -791,7 +796,7 @@ public virtual async Task AccountActivation(string token, string //send welcome message await _messageProviderService.SendCustomerWelcomeMessage(customer, _contextAccessor.StoreContext.CurrentStore, - _contextAccessor.WorkContext.WorkingLanguage.Id); + _contextAccessor.WorkContext.WorkingLanguage.Id, host: _contextAccessor.StoreContext.CurrentHost); var model = new AccountActivationModel { Result = _translationService.GetResource("Account.AccountActivation.Activated") @@ -1152,6 +1157,7 @@ public virtual async Task DeleteAccount(DeleteAccountModel model, //delete account await _mediator.Send(new DeleteAccountCommand { + Host = _contextAccessor.StoreContext.CurrentHost, Customer = _contextAccessor.WorkContext.CurrentCustomer, Store = _contextAccessor.StoreContext.CurrentStore, IpAddress = HttpContext.Connection?.RemoteIpAddress?.ToString() @@ -1182,6 +1188,7 @@ public async Task EnableTwoFactorAuthenticator([FromServices] Oid return RedirectToRoute("CustomerInfo"); var model = await _mediator.Send(new GetTwoFactorAuthentication { + Host = _contextAccessor.StoreContext.CurrentHost, Customer = _contextAccessor.WorkContext.CurrentCustomer, Language = _contextAccessor.WorkContext.WorkingLanguage, Store = _contextAccessor.StoreContext.CurrentStore @@ -1242,6 +1249,7 @@ public async Task DisableTwoFactorAuthenticator([FromServices] Oi return RedirectToRoute("CustomerInfo"); _ = await _mediator.Send(new GetTwoFactorAuthentication { + Host = _contextAccessor.StoreContext.CurrentHost, Customer = _contextAccessor.WorkContext.CurrentCustomer, Language = _contextAccessor.WorkContext.WorkingLanguage, Store = _contextAccessor.StoreContext.CurrentStore diff --git a/src/Web/Grand.Web/Controllers/CheckoutController.cs b/src/Web/Grand.Web/Controllers/CheckoutController.cs index 3494ce8927..cdeaabeb80 100644 --- a/src/Web/Grand.Web/Controllers/CheckoutController.cs +++ b/src/Web/Grand.Web/Controllers/CheckoutController.cs @@ -14,6 +14,7 @@ using Grand.Domain.Orders; using Grand.Domain.Payments; using Grand.Domain.Shipping; +using Grand.Domain.Stores; using Grand.Infrastructure.Configuration; using Grand.Infrastructure; using Grand.Infrastructure.Extensions; @@ -723,7 +724,7 @@ public virtual async Task ConfirmOrder() var placeOrderResult = await _mediator.Send(new PlaceOrderCommand { Context = _contextAccessor.CalculationContext(), Language = _contextAccessor.WorkContext.WorkingLanguage, - Host = _contextAccessor.StoreContext.CurrentHost, + Host = _contextAccessor.StoreContext.CurrentStore.LinkHost(_contextAccessor.StoreContext.CurrentHost), OriginalCustomerIfImpersonated = _contextAccessor.WorkContext.OriginalCustomerIfImpersonated }); if (placeOrderResult.Success) diff --git a/src/Web/Grand.Web/Controllers/ContactController.cs b/src/Web/Grand.Web/Controllers/ContactController.cs index 4a3a5bc810..74d8372332 100644 --- a/src/Web/Grand.Web/Controllers/ContactController.cs +++ b/src/Web/Grand.Web/Controllers/ContactController.cs @@ -76,6 +76,10 @@ public virtual async Task> Index( if (ModelState.IsValid) { var result = await _mediator.Send(new ContactUsSendCommand { + Host = _contextAccessor.StoreContext.CurrentHost, + Customer = _contextAccessor.WorkContext.CurrentCustomer, + Store = _contextAccessor.StoreContext.CurrentStore, + Language = _contextAccessor.WorkContext.WorkingLanguage, Model = model, IpAddress = HttpContext?.Connection?.RemoteIpAddress?.ToString() }); diff --git a/src/Web/Grand.Web/Controllers/KnowledgebaseController.cs b/src/Web/Grand.Web/Controllers/KnowledgebaseController.cs index d098c7381b..6090b1324e 100644 --- a/src/Web/Grand.Web/Controllers/KnowledgebaseController.cs +++ b/src/Web/Grand.Web/Controllers/KnowledgebaseController.cs @@ -315,7 +315,7 @@ public virtual async Task ArticleCommentAdd(KnowledgebaseArticleM //notify a store owner if (_knowledgebaseSettings.NotifyAboutNewArticleComments) await _messageProviderService.SendArticleCommentMessage(article, comment, - _languageSettings.DefaultAdminLanguageId); + _languageSettings.DefaultAdminLanguageId, host: _contextAccessor.StoreContext.CurrentHost); //The text boxes should be cleared after a comment has been posted //That' why we reload the page diff --git a/src/Web/Grand.Web/Controllers/OrderController.cs b/src/Web/Grand.Web/Controllers/OrderController.cs index 22410d0eb5..e373bd2959 100644 --- a/src/Web/Grand.Web/Controllers/OrderController.cs +++ b/src/Web/Grand.Web/Controllers/OrderController.cs @@ -146,8 +146,10 @@ public virtual async Task> AddOrderNote(AddOrder if (!await order.Access(_contextAccessor.WorkContext.CurrentCustomer, _groupService)) return Challenge(); - await _mediator.Send(new InsertOrderNoteCommand - { Order = order, OrderNote = model, Language = _contextAccessor.WorkContext.WorkingLanguage }); + await _mediator.Send(new InsertOrderNoteCommand { + Order = order, OrderNote = model, Language = _contextAccessor.WorkContext.WorkingLanguage, + Host = _contextAccessor.StoreContext.CurrentHost + }); //notification await _mediator.Publish(new OrderNoteEvent(order, model)); diff --git a/src/Web/Grand.Web/Controllers/ProductController.cs b/src/Web/Grand.Web/Controllers/ProductController.cs index 85791be6e7..cbcc224e52 100644 --- a/src/Web/Grand.Web/Controllers/ProductController.cs +++ b/src/Web/Grand.Web/Controllers/ProductController.cs @@ -151,6 +151,7 @@ public virtual async Task ProductEmailAFriend(ProductEmailAFriend if (ModelState.IsValid) { await _mediator.Send(new SendProductEmailAFriendMessageCommand { + Host = _contextAccessor.StoreContext.CurrentHost, Customer = _contextAccessor.WorkContext.CurrentCustomer, Product = product, Language = _contextAccessor.WorkContext.WorkingLanguage, @@ -210,6 +211,7 @@ public virtual async Task AskQuestionOnProduct(ProductAskQuestion // email await _mediator.Send(new SendProductAskQuestionMessageCommand { + Host = _contextAccessor.StoreContext.CurrentHost, Customer = _contextAccessor.WorkContext.CurrentCustomer, Language = _contextAccessor.WorkContext.WorkingLanguage, Store = _contextAccessor.StoreContext.CurrentStore, @@ -573,6 +575,7 @@ public virtual async Task ProductReviews( if (ModelState.IsValid) { var productReview = await _mediator.Send(new InsertProductReviewCommand { + Host = _contextAccessor.StoreContext.CurrentHost, Customer = _contextAccessor.WorkContext.CurrentCustomer, Store = _contextAccessor.StoreContext.CurrentStore, Model = model, diff --git a/src/Web/Grand.Web/Controllers/VendorController.cs b/src/Web/Grand.Web/Controllers/VendorController.cs index 929b92e4c3..55f0bafd62 100644 --- a/src/Web/Grand.Web/Controllers/VendorController.cs +++ b/src/Web/Grand.Web/Controllers/VendorController.cs @@ -168,7 +168,8 @@ await _customerService.UpdateCustomerField(_contextAccessor.WorkContext.CurrentC //notify store owner here (email) await _messageProviderService.SendNewVendorAccountApplyStoreOwnerMessage(_contextAccessor.WorkContext.CurrentCustomer, - vendor, _contextAccessor.StoreContext.CurrentStore, _languageSettings.DefaultAdminLanguageId); + vendor, _contextAccessor.StoreContext.CurrentStore, _languageSettings.DefaultAdminLanguageId, + host: _contextAccessor.StoreContext.CurrentHost); model.DisableFormInput = true; model.Result = _translationService.GetResource("Vendors.ApplyAccount.Submitted"); diff --git a/src/Web/Grand.Web/Controllers/WishlistController.cs b/src/Web/Grand.Web/Controllers/WishlistController.cs index 4456f6b92a..71a0e029d8 100644 --- a/src/Web/Grand.Web/Controllers/WishlistController.cs +++ b/src/Web/Grand.Web/Controllers/WishlistController.cs @@ -228,7 +228,8 @@ public virtual async Task EmailWishlist(WishlistEmailAFriendModel await messageProviderService.SendWishlistEmailAFriendMessage(_contextAccessor.WorkContext.CurrentCustomer, _contextAccessor.StoreContext.CurrentStore, _contextAccessor.WorkContext.WorkingLanguage.Id, model.YourEmailAddress, - model.FriendEmail, FormatText.ConvertText(model.PersonalMessage)); + model.FriendEmail, FormatText.ConvertText(model.PersonalMessage), + host: _contextAccessor.StoreContext.CurrentHost); model.SuccessfullySent = true; model.Result = _translationService.GetResource("Wishlist.EmailAFriend.SuccessfullySent"); diff --git a/src/Web/Grand.Web/Features/Handlers/Customers/GetTwoFactorAuthenticationHandler.cs b/src/Web/Grand.Web/Features/Handlers/Customers/GetTwoFactorAuthenticationHandler.cs index 3b520265af..204c5751a0 100644 --- a/src/Web/Grand.Web/Features/Handlers/Customers/GetTwoFactorAuthenticationHandler.cs +++ b/src/Web/Grand.Web/Features/Handlers/Customers/GetTwoFactorAuthenticationHandler.cs @@ -37,7 +37,7 @@ public GetTwoFactorAuthenticationHandler( }; if (_customerSetting.TwoFactorAuthenticationType == TwoFactorAuthenticationType.EmailVerification) await _messageProviderService.SendCustomerEmailTokenValidationMessage(request.Customer, request.Store, - request.Customer.Id); + request.Customer.Id, host: request.Host); return model; } diff --git a/src/Web/Grand.Web/Features/Models/Customers/GetTwoFactorAuthentication.cs b/src/Web/Grand.Web/Features/Models/Customers/GetTwoFactorAuthentication.cs index 8643c1e407..67de80d633 100644 --- a/src/Web/Grand.Web/Features/Models/Customers/GetTwoFactorAuthentication.cs +++ b/src/Web/Grand.Web/Features/Models/Customers/GetTwoFactorAuthentication.cs @@ -11,4 +11,5 @@ public class GetTwoFactorAuthentication : IRequest new CustomAttribute { Key = z.Key, Value = z.Value }).ToList(), - contextAccessor.StoreContext.CurrentStore.Id); + contextAccessor.CatalogAccess()); if (contactAttributeWarnings.Any()) foreach (var item in contactAttributeWarnings) context.AddFailure(item); @@ -68,7 +70,7 @@ public ContactUsValidator( } private async Task> GetContactAttributesWarnings(IList customAttributes, - string storeId) + CatalogAccess access) { var warnings = new List(); @@ -76,7 +78,7 @@ private async Task> GetContactAttributesWarnings(IList