Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
using Grand.Business.Core.Extensions;
using Grand.Business.Core.Extensions;
using Grand.Business.Core.Interfaces.Catalog.Prices;
using Grand.Business.Core.Interfaces.Catalog.Products;
using Grand.Business.Core.Interfaces.Catalog.Tax;
using Grand.Business.Core.Utilities.Checkout;
using Grand.Domain.Catalog;
using Grand.Domain.Common;
using Grand.Domain.Customers;
using Grand.Domain.Localization;
using Grand.Domain.Orders;
using Grand.Domain.Tax;
using Grand.Infrastructure;
using Grand.Domain.Stores;
using Grand.SharedKernel.Extensions;
using System.Net;

Expand All @@ -23,16 +23,13 @@ public class ProductAttributeFormatter : IProductAttributeFormatter
private readonly IProductAttributeService _productAttributeService;
private readonly IProductService _productService;
private readonly ITaxService _taxService;
private readonly IContextAccessor _contextAccessor;

public ProductAttributeFormatter(IContextAccessor contextAccessor,
IProductAttributeService productAttributeService,
public ProductAttributeFormatter(IProductAttributeService productAttributeService,
ITaxService taxService,
IPriceFormatter priceFormatter,
IPricingService priceCalculationService,
IProductService productService)
{
_contextAccessor = contextAccessor;
_productAttributeService = productAttributeService;
_taxService = taxService;
_priceFormatter = priceFormatter;
Expand All @@ -45,19 +42,9 @@ public ProductAttributeFormatter(IContextAccessor contextAccessor,
/// </summary>
/// <param name="product">Product</param>
/// <param name="customAttributes">Attributes</param>
/// <returns>Attributes</returns>
public virtual Task<string> FormatAttributes(Product product, IList<CustomAttribute> customAttributes)
{
var customer = _contextAccessor.WorkContext.CurrentCustomer;
return FormatAttributes(product, customAttributes, customer);
}

/// <summary>
/// Formats attributes
/// </summary>
/// <param name="product">Product</param>
/// <param name="customAttributes">Attributes</param>
/// <param name="customer">Customer</param>
/// <param name="context">Customer, store, currency and tax display the prices are rendered for (required when renderPrices is true)</param>
/// <param name="language">Language of the attribute names</param>
/// <param name="host">Store address used for file download links (read only when allowHyperlinks is true; without it the link is not rendered)</param>
/// <param name="separator">Separator</param>
/// <param name="htmlEncode">A value indicating whether to encode (HTML) values</param>
/// <param name="renderPrices">A value indicating whether to render prices</param>
Expand All @@ -67,7 +54,8 @@ public virtual Task<string> FormatAttributes(Product product, IList<CustomAttrib
/// <param name="showInAdmin">Show in admin</param>
/// <returns>Attributes</returns>
public virtual async Task<string> FormatAttributes(Product product, IList<CustomAttribute> customAttributes,
Customer customer, string separator = "<br />", bool htmlEncode = true, bool renderPrices = true,
CalculationContext context, Language language, DomainHost host,
string separator = "<br />", bool htmlEncode = true, bool renderPrices = true,
bool renderProductAttributes = true, bool renderGiftVoucherAttributes = true,
bool allowHyperlinks = true, bool showInAdmin = false)
{
Expand All @@ -76,17 +64,15 @@ public virtual async Task<string> FormatAttributes(Product product, IList<Custom
if (customAttributes == null || !customAttributes.Any())
return result.ToString();

var langId = _contextAccessor.WorkContext.WorkingLanguage != null
? _contextAccessor.WorkContext.WorkingLanguage.Id
: customer?.GetUserFieldFromEntity<string>(SystemCustomerFieldNames.LanguageId);
if (renderPrices)
ArgumentNullException.ThrowIfNull(context);

if (string.IsNullOrEmpty(langId))
langId = "";
var langId = language?.Id ?? "";

//attributes
if (renderProductAttributes)
{
result = await PrepareFormattedAttribute(product, customAttributes, customer, langId, separator,
result = await PrepareFormattedAttribute(product, customAttributes, context, langId, host, separator,
htmlEncode, renderPrices, allowHyperlinks, showInAdmin);

if (product.ProductTypeId == ProductType.BundledProduct)
Expand All @@ -106,8 +92,8 @@ public virtual async Task<string> FormatAttributes(Product product, IList<Custom
else
result.Append(
$"{(htmlEncode ? WebUtility.HtmlEncode(p1.GetTranslation(x => x.Name, langId)) : p1.GetTranslation(x => x.Name, langId))}");
var formattedAttribute = await PrepareFormattedAttribute(p1, customAttributes, customer, langId,
separator, htmlEncode,
var formattedAttribute = await PrepareFormattedAttribute(p1, customAttributes, context, langId,
host, separator, htmlEncode,
renderPrices, allowHyperlinks, showInAdmin);
if (formattedAttribute.Length > 0)
{
Expand Down Expand Up @@ -151,11 +137,10 @@ public virtual async Task<string> FormatAttributes(Product product, IList<Custom
}

private async Task<StringBuilder> PrepareFormattedAttribute(Product product,
IList<CustomAttribute> customAttributes, Customer customer, string langId,
string separator, bool htmlEncode, bool renderPrices,
IList<CustomAttribute> customAttributes, CalculationContext context, string langId,
DomainHost host, string separator, bool htmlEncode, bool renderPrices,
bool allowHyperlinks, bool showInAdmin)
{
var priceCustomer = customer ?? _contextAccessor.WorkContext.CurrentCustomer;
var result = new StringBuilder();
var attributes = product.ParseProductAttributeMappings(customAttributes);
for (var i = 0; i < attributes.Count; i++)
Expand Down Expand Up @@ -194,10 +179,10 @@ private async Task<StringBuilder> PrepareFormattedAttribute(Product product,
{
var attributeText = string.Empty;
var attributeName = productAttribute.GetTranslation(a => a.Name, langId);
if (allowHyperlinks)
if (allowHyperlinks && host != null)
{
var downloadLink =
$"{_contextAccessor.StoreContext.CurrentHost.Url.TrimEnd('/')}/download/getfileupload/?downloadId={downloadGuid}";
$"{host.Url.TrimEnd('/')}/download/getfileupload/?downloadId={downloadGuid}";
attributeText =
$"<a href=\"{downloadLink}\" class=\"fileuploadattribute\">{attribute.GetTranslation(a => a.TextPrompt, langId)}</a>";
}
Expand Down Expand Up @@ -236,25 +221,23 @@ private async Task<StringBuilder> PrepareFormattedAttribute(Product product,
{
var attributeValuePriceAdjustment =
await _pricingService.GetProductAttributeValuePriceAdjustment(attributeValue,
priceCustomer, _contextAccessor.StoreContext.CurrentStore,
_contextAccessor.WorkContext.WorkingCurrency);
context.Customer, context.Store, context.Currency);
var (priceAdjustmentBase, _) = await _taxService.GetProductPrice(product,
attributeValuePriceAdjustment,
_contextAccessor.WorkContext.TaxDisplayType == TaxDisplayType.IncludingTax,
priceCustomer, _contextAccessor.StoreContext.CurrentStore);
context.IncludingTax, context.Customer, context.Store);
switch (priceAdjustmentBase)
{
case > 0:
{
var priceAdjustmentStr = _priceFormatter.FormatPrice(priceAdjustmentBase,
_contextAccessor.WorkContext.WorkingCurrency);
context.Currency);
formattedAttribute += $" [+{priceAdjustmentStr}]";
break;
}
case < 0:
{
var priceAdjustmentStr = _priceFormatter.FormatPrice(-priceAdjustmentBase,
_contextAccessor.WorkContext.WorkingCurrency);
context.Currency);
formattedAttribute += $" [-{priceAdjustmentStr}]";
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,9 @@ protected virtual async Task<PlaceOrderContainer> PreparePlaceOrderDetails(Place
var details = new PlaceOrderContainer {
Context = command.Context,
Customer = command.Context.Customer,
Store = command.Context.Store
Store = command.Context.Store,
WorkingLanguage = command.Language,
Host = command.Host
};
if (details.Customer == null)
throw new ArgumentException("Customer is not set");
Expand Down Expand Up @@ -442,7 +444,7 @@ protected virtual async Task<PlaceOrderContainer> PreparePlaceOrderDetails(Place
details.Store.Id);
details.CheckoutAttributeDescription =
await _checkoutAttributeFormatter.FormatAttributes(details.CheckoutAttributes, details.Context,
command.Language, command.Host);
details.WorkingLanguage, details.Host);

//load and validate customer shopping cart
details.Cart = details.Customer.ShoppingCartItems
Expand Down Expand Up @@ -651,7 +653,8 @@ protected virtual async Task<OrderItem> PrepareOrderItem(ShoppingCartItem sc, Pr
details.AppliedDiscounts.All(x => x.DiscountId != disc.DiscountId)))
details.AppliedDiscounts.Add(disc);
var attributeDescription =
await _productAttributeFormatter.FormatAttributes(product, sc.Attributes, details.Customer);
await _productAttributeFormatter.FormatAttributes(product, sc.Attributes, details.Context,
details.WorkingLanguage, details.Host);
var itemWeight = await GetShoppingCartItemWeight(sc);
var warehouseId = !string.IsNullOrEmpty(sc.WarehouseId)
? sc.WarehouseId
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using Grand.Business.Core.Utilities.Messages.DotLiquidDrops;
using Grand.Domain.Catalog;
using Grand.Domain.Localization;
using Grand.Domain.Stores;
using Grand.Mediator;

namespace Grand.Business.Core.Commands.Messages.Tokens;
Expand All @@ -8,4 +10,6 @@ public class GetAttributeCombinationTokensCommand : IRequest<LiquidAttributeComb
{
public Product Product { get; set; }
public ProductAttributeCombination Combination { get; set; }
public Language Language { get; set; }
public DomainHost Host { get; set; }
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using Grand.Business.Core.Utilities.Messages.DotLiquidDrops;
using Grand.Domain.Customers;
using Grand.Business.Core.Utilities.Checkout;
using Grand.Business.Core.Utilities.Messages.DotLiquidDrops;
using Grand.Domain.Localization;
using Grand.Domain.Stores;
using Grand.Mediator;
Expand All @@ -8,7 +8,7 @@ namespace Grand.Business.Core.Commands.Messages.Tokens;

public class GetShoppingCartTokensCommand : IRequest<LiquidShoppingCart>
{
public Customer Customer { get; set; }
public Store Store { get; set; }
public CalculationContext Context { get; set; }
public DomainHost Host { get; set; }
public Language Language { get; set; }
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using Grand.Business.Core.Utilities.Checkout;
using Grand.Domain.Catalog;
using Grand.Domain.Common;
using Grand.Domain.Customers;
using Grand.Domain.Localization;
using Grand.Domain.Stores;

namespace Grand.Business.Core.Interfaces.Catalog.Products;

Expand All @@ -14,15 +16,9 @@ public interface IProductAttributeFormatter
/// </summary>
/// <param name="product">Product</param>
/// <param name="customAttributes">Attributes</param>
/// <returns>Attributes</returns>
Task<string> FormatAttributes(Product product, IList<CustomAttribute> customAttributes);

/// <summary>
/// Formats attributes
/// </summary>
/// <param name="product">Product</param>
/// <param name="customAttributes">Attributes</param>
/// <param name="customer">Customer</param>
/// <param name="context">Customer, store, currency and tax display the prices are rendered for (required when renderPrices is true)</param>
/// <param name="language">Language of the attribute names</param>
/// <param name="host">Store address used for file download links (read only when allowHyperlinks is true; without it the link is not rendered)</param>
/// <param name="separator">Separator</param>
/// <param name="htmlEncode">A value indicating whether to encode (HTML) values</param>
/// <param name="renderPrices">A value indicating whether to render prices</param>
Expand All @@ -32,7 +28,8 @@ public interface IProductAttributeFormatter
/// <param name="showInAdmin">Show in admin</param>
/// <returns>Attributes</returns>
Task<string> FormatAttributes(Product product, IList<CustomAttribute> customAttributes,
Customer customer, string separator = "<br />", bool htmlEncode = true, bool renderPrices = true,
CalculationContext context, Language language, DomainHost host,
string separator = "<br />", bool htmlEncode = true, bool renderPrices = true,
bool renderProductAttributes = true, bool renderGiftVoucherAttributes = true,
bool allowHyperlinks = true, bool showInAdmin = false);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using Grand.Domain;
using Grand.Domain.Directory;
using Grand.Domain.Messages;
using Grand.Domain.Stores;
using Grand.Domain.Tax;

namespace Grand.Business.Core.Interfaces.Marketing.Campaigns;

Expand Down Expand Up @@ -65,15 +68,23 @@ Task<IPagedList<NewsLetterSubscription>> CustomerSubscriptions(Campaign campaign
/// <param name="campaign">Campaign</param>
/// <param name="emailAccount">Email account</param>
/// <param name="subscriptions">Subscriptions</param>
/// <param name="currency">Currency the shopping cart prices are rendered in</param>
/// <param name="taxDisplayType">Whether the shopping cart prices include tax</param>
/// <param name="host">Store address used for the links in the email</param>
/// <returns>Total emails sent</returns>
Task<int> SendCampaign(Campaign campaign, EmailAccount emailAccount,
IEnumerable<NewsLetterSubscription> subscriptions);
IEnumerable<NewsLetterSubscription> subscriptions, Currency currency, TaxDisplayType taxDisplayType,
DomainHost host);

/// <summary>
/// Sends a campaign to specified email
/// </summary>
/// <param name="campaign">Campaign</param>
/// <param name="emailAccount">Email account</param>
/// <param name="email">Email</param>
Task SendCampaign(Campaign campaign, EmailAccount emailAccount, string email);
/// <param name="currency">Currency the shopping cart prices are rendered in</param>
/// <param name="taxDisplayType">Whether the shopping cart prices include tax</param>
/// <param name="host">Store address used for the links in the email</param>
Task SendCampaign(Campaign campaign, EmailAccount emailAccount, string email, Currency currency,
TaxDisplayType taxDisplayType, DomainHost host);
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,17 @@ public class PlaceOrderContainer
public Customer Customer { get; set; }
public Store Store { get; set; }
public Language Language { get; set; }

/// <summary>
/// Language of the request placing the order, used for the attribute descriptions
/// </summary>
public Language WorkingLanguage { get; set; }

/// <summary>
/// Store address used for file download links in the attribute descriptions
/// </summary>
public DomainHost Host { get; set; }

public Currency Currency { get; set; }
public string AffiliateId { get; set; }
public TaxDisplayType TaxDisplayType { get; set; }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Grand.Business.Core.Commands.Messages.Tokens;
using Grand.Business.Core.Extensions;
using Grand.Business.Core.Utilities.Checkout;
using Grand.Domain.Blogs;
using Grand.Domain.Catalog;
using Grand.Domain.Customers;
Expand Down Expand Up @@ -122,18 +123,18 @@ public LiquidObjectBuilder AddCustomerTokens(Customer customer, Store store, Dom
return this;
}

public LiquidObjectBuilder AddShoppingCartTokens(Customer customer, Store store, Language language)
public LiquidObjectBuilder AddShoppingCartTokens(CalculationContext context, DomainHost host, Language language)
{
_chain.Add(async liquidObject =>
{
var liquidShoppingCart = await _mediator.Send(new GetShoppingCartTokensCommand {
Customer = customer,
Language = language,
Store = store
Context = context,
Host = host,
Language = language
});
liquidObject.ShoppingCart = liquidShoppingCart;

await _mediator.EntityTokensAdded(customer, liquidShoppingCart, liquidObject);
await _mediator.EntityTokensAdded(context.Customer, liquidShoppingCart, liquidObject);
});
return this;
}
Expand Down Expand Up @@ -231,12 +232,13 @@ public LiquidObjectBuilder AddProductTokens(Product product, Language language,
return this;
}

public LiquidObjectBuilder AddAttributeCombinationTokens(Product product, ProductAttributeCombination combination)
public LiquidObjectBuilder AddAttributeCombinationTokens(Product product, ProductAttributeCombination combination,
Language language, DomainHost host)
{
_chain.Add(async liquidObject =>
{
var liquidAttributeCombination = await _mediator.Send(new GetAttributeCombinationTokensCommand
{ Product = product, Combination = combination });
{ Product = product, Combination = combination, Language = language, Host = host });
liquidObject.AttributeCombination = liquidAttributeCombination;
await _mediator.EntityTokensAdded(combination, liquidAttributeCombination, liquidObject);
});
Expand Down
Loading
Loading