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
Expand Up @@ -8,6 +8,7 @@
using Grand.Business.Core.Utilities.Customers;
using Grand.Data;
using Grand.Domain.Customers;
using Grand.Domain.Localization;
using Grand.Domain.Stores;
using Grand.Infrastructure.Extensions;
using Grand.SharedKernel.Extensions;
Expand Down Expand Up @@ -70,14 +71,15 @@ public ExternalAuthenticationService(
/// <param name="currentLoggedInUser">Current logged-in user</param>
/// <param name="customer">Customer of the request (a guest when nobody is logged in)</param>
/// <param name="storeId">Store identifier</param>
/// <param name="language">Working language of the request</param>
/// <param name="returnUrl">URL to which the user will return after authentication</param>
/// <returns>Result of an authentication</returns>
protected virtual async Task<IActionResult> AuthenticateExistingUser(Customer associatedUser,
Customer currentLoggedInUser, Customer customer, string storeId, string returnUrl)
Customer currentLoggedInUser, Customer customer, string storeId, Language language, string returnUrl)
{
//log in guest user
if (currentLoggedInUser == null)
return await LoginUser(associatedUser, customer, storeId, returnUrl);
return await LoginUser(associatedUser, customer, storeId, language, returnUrl);

//account is already assigned to another user
if (currentLoggedInUser.Id != associatedUser.Id)
Expand All @@ -95,10 +97,11 @@ protected virtual async Task<IActionResult> AuthenticateExistingUser(Customer as
/// <param name="parameters">Authentication parameters received from external authentication method</param>
/// <param name="customer">Customer of the request (a guest when nobody is logged in)</param>
/// <param name="storeId">Store identifier</param>
/// <param name="language">Working language of the request</param>
/// <param name="returnUrl">URL to which the user will return after authentication</param>
/// <returns>Result of an authentication</returns>
protected virtual async Task<IActionResult> AuthenticateNewUser(Customer currentLoggedInUser,
ExternalAuthParam parameters, Customer customer, string storeId, string returnUrl)
ExternalAuthParam parameters, Customer customer, string storeId, Language language, string returnUrl)
{
//associate external account with logged-in user
if (currentLoggedInUser != null)
Expand All @@ -111,7 +114,7 @@ protected virtual async Task<IActionResult> AuthenticateNewUser(Customer current

//or try to register new user
if (_customerSettings.UserRegistrationType != UserRegistrationType.Disabled)
return await RegisterNewUser(parameters, customer, storeId, returnUrl);
return await RegisterNewUser(parameters, customer, storeId, language, returnUrl);

//registration is disabled
return Error(["Registration is disabled"]);
Expand All @@ -123,10 +126,11 @@ protected virtual async Task<IActionResult> AuthenticateNewUser(Customer current
/// <param name="parameters">Authentication parameters received from external authentication method</param>
/// <param name="customer">Guest customer of the request; registration turns it into the new user</param>
/// <param name="storeId">Store identifier</param>
/// <param name="language">Working language of the request</param>
/// <param name="returnUrl">URL to which the user will return after authentication</param>
/// <returns>Result of an authentication</returns>
protected virtual async Task<IActionResult> RegisterNewUser(ExternalAuthParam parameters, Customer customer,
string storeId, string returnUrl)
string storeId, Language language, string returnUrl)
{
var approved =
_customerSettings.UserRegistrationType is UserRegistrationType.Standard
Expand All @@ -147,7 +151,7 @@ _customerSettings.UserRegistrationType is UserRegistrationType.Standard
await _mediator.Publish(new RegisteredByExternalMethod(customer, parameters));

//raise customer registered event
await _mediator.Publish(new CustomerRegisteredEvent(customer));
await _mediator.Publish(new CustomerRegisteredEvent(customer, language));

//associate external account with registered user
await AssociateCustomer(customer, parameters);
Expand All @@ -169,13 +173,14 @@ _customerSettings.UserRegistrationType is UserRegistrationType.Standard
/// <param name="user">User to login</param>
/// <param name="previousCustomer">Customer of the request before the login (the guest whose cart is merged)</param>
/// <param name="storeId">Store identifier</param>
/// <param name="language">Working language of the request</param>
/// <param name="returnUrl">URL to which the user will return after authentication</param>
/// <returns>Result of an authentication</returns>
protected virtual async Task<IActionResult> LoginUser(Customer user, Customer previousCustomer, string storeId,
string returnUrl)
Language language, string returnUrl)
{
//raise event
await _mediator.Publish(new CustomerLoggedInEvent(user, previousCustomer, storeId));
await _mediator.Publish(new CustomerLoggedInEvent(user, previousCustomer, storeId, language));

//authenticate
await _authenticationService.SignIn(user, false);
Expand Down Expand Up @@ -270,14 +275,16 @@ public virtual bool AuthenticationProviderIsAvailable(string systemName, Custome
/// <param name="parameters">External authentication parameters</param>
/// <param name="customer">Customer of the request (a guest when nobody is logged in)</param>
/// <param name="store">Store of the request</param>
/// <param name="language">Working language of the request</param>
/// <param name="returnUrl">URL to which the user will return after authentication</param>
/// <returns>Result of an authentication</returns>
public virtual async Task<IActionResult> Authenticate(ExternalAuthParam parameters, Customer customer, Store store,
string returnUrl = null)
Language language, string returnUrl = null)
{
ArgumentNullException.ThrowIfNull(parameters);
ArgumentNullException.ThrowIfNull(customer);
ArgumentNullException.ThrowIfNull(store);
ArgumentNullException.ThrowIfNull(language);

if (!AuthenticationProviderIsAvailable(parameters.ProviderSystemName, customer, store))
return Error(["External authentication method cannot be loaded"]);
Expand All @@ -288,10 +295,11 @@ public virtual async Task<IActionResult> Authenticate(ExternalAuthParam paramete
//authenticate associated user if already exists
var associatedUser = await GetCustomer(parameters);
if (associatedUser != null)
return await AuthenticateExistingUser(associatedUser, currentLoggedInUser, customer, store.Id, returnUrl);
return await AuthenticateExistingUser(associatedUser, currentLoggedInUser, customer, store.Id, language,
returnUrl);

//or associate and authenticate new user
return await AuthenticateNewUser(currentLoggedInUser, parameters, customer, store.Id, returnUrl);
return await AuthenticateNewUser(currentLoggedInUser, parameters, customer, store.Id, language, returnUrl);
}

#endregion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Grand.Data;
using Grand.Domain.Common;
using Grand.Domain.Customers;
using Grand.Domain.Localization;
using Grand.Domain.Permissions;
using Grand.Infrastructure.Configuration;
using Grand.Mediator;
Expand Down Expand Up @@ -146,7 +147,8 @@ await RequiresPassword(linkedCustomer, storeId))
return result;
}

public virtual async Task<OidcLoginResult> ResolveForLogin(string sub, string email, bool emailVerified, string storeId)
public virtual async Task<OidcLoginResult> ResolveForLogin(string sub, string email, bool emailVerified, string storeId,
Language language)
{
if (string.IsNullOrEmpty(sub))
return OidcLoginResult.Refused(OidcLoginRefusal.InvalidIdentity);
Expand Down Expand Up @@ -186,7 +188,7 @@ public virtual async Task<OidcLoginResult> ResolveForLogin(string sub, string em
{
//provider-only with automatic accounts: the provider decides who gets in, no form and no registration type
if (_loginConfig is { ProviderOnly: true, AutoProvision: true })
return await ProvisionForLogin(sub, email, emailVerified, storeId);
return await ProvisionForLogin(sub, email, emailVerified, storeId, language);

var customerSettings = await _settingService.LoadSetting<CustomerSettings>(storeId);
if (customerSettings.UserRegistrationType == UserRegistrationType.Disabled)
Expand Down Expand Up @@ -277,7 +279,8 @@ private async Task<OidcCustomerResult> Register(string sub, string email, string
if (existing != null)
return await Usable(await _customerService.GetCustomerById(existing.CustomerId), sub, storeId);

var (outcome, customer, winner) = await CreateLinkedAccount(sub, email, storeId);
//an access token carries no working language: the registration falls back to the store's
var (outcome, customer, winner) = await CreateLinkedAccount(sub, email, storeId, null);
return outcome switch {
Provisioning.UserNameTaken => OidcCustomerResult.Forbidden("The user name is already taken"),
Provisioning.LinkedToOtherIdentity => LinkedToOtherIdentity(customer, sub),
Expand All @@ -290,14 +293,15 @@ private async Task<OidcCustomerResult> Register(string sub, string email, string
/// ProviderOnly + AutoProvision: the account of an unknown, provider-verified e-mail, created and linked at its first
/// browser sign-in (the profile sync of the sign-in fills in the names)
/// </summary>
private async Task<OidcLoginResult> ProvisionForLogin(string sub, string email, bool emailVerified, string storeId)
private async Task<OidcLoginResult> ProvisionForLogin(string sub, string email, bool emailVerified, string storeId,
Language language)
{
var (outcome, customer, _) = await CreateLinkedAccount(sub, email, storeId);
var (outcome, customer, _) = await CreateLinkedAccount(sub, email, storeId, language);
return outcome switch {
Provisioning.UserNameTaken => OidcLoginResult.Refused(OidcLoginRefusal.AccountUnusable),
Provisioning.LinkedToOtherIdentity => OidcLoginResult.Refused(OidcLoginRefusal.LinkedToOtherIdentity),
//another request of the same user created the account first: its link answers now
Provisioning.LostRace => await ResolveForLogin(sub, email, emailVerified, storeId),
Provisioning.LostRace => await ResolveForLogin(sub, email, emailVerified, storeId, language),
_ => OidcLoginResult.SignIn(customer)
};
}
Expand All @@ -315,7 +319,7 @@ private enum Provisioning
/// e-mail validation / admin approval does not apply: access comes from the provider and from permissions an admin grants
/// </summary>
private async Task<(Provisioning Outcome, Customer Customer, Customer Winner)> CreateLinkedAccount(string sub,
string email, string storeId)
string email, string storeId, Language language)
{
//the e-mail becomes the user name - another account may hold it as its user name without that e-mail
var normalizedEmail = email.ToLowerInvariant();
Expand Down Expand Up @@ -347,7 +351,7 @@ await _customerManagerService.RegisterCustomer(new RegistrationRequest(customer,
}

//only after the link is stored, so the loser of a race sends no welcome message
await _mediator.Publish(new CustomerRegisteredEvent(customer));
await _mediator.Publish(new CustomerRegisteredEvent(customer, language));
_logger.LogInformation("Subject {Subject} registered as customer {CustomerId} (store {StoreId})",
sub, customer.Id, storeId);
return (Provisioning.Created, customer, null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public async Task<bool> Handle(AddRequiredProductsCommand request, CancellationT
//not in the cart
if (alreadyInTheCart) continue;
if (!product.AutoAddRequiredProducts) continue;
var addToCart = await _shoppingCartService.AddToCart(customer,
var addToCart = await _shoppingCartService.AddToCart(customer, request.Language,
rp.Id,
shoppingCartType,
storeId,
Expand All @@ -63,7 +63,8 @@ await Handle(new AddRequiredProductsCommand {
Customer = customer,
ShoppingCartType = shoppingCartType,
StoreId = storeId,
Product = rp
Product = rp,
Language = request.Language
}, cancellationToken);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
using Grand.Business.Core.Commands.Checkout.Orders;
using Grand.Business.Core.Extensions;
using Grand.Business.Core.Interfaces.Checkout.Orders;
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.Customers;
using Grand.Domain.Orders;
using Grand.Mediator;

namespace Grand.Business.Checkout.Commands.Handlers.Orders;
Expand All @@ -14,6 +17,8 @@ public class AwardLoyaltyPointsCommandHandler : IRequestHandler<AwardLoyaltyPoin
private readonly ILoyaltyPointsService _loyaltyPointsService;
private readonly IMediator _mediator;
private readonly IOrderService _orderService;
private readonly ILanguageService _languageService;
private readonly IStoreService _storeService;
private readonly ITranslationService _translationService;

public AwardLoyaltyPointsCommandHandler(
Expand All @@ -22,13 +27,17 @@ public AwardLoyaltyPointsCommandHandler(
IMediator mediator,
IOrderService orderService,
ITranslationService translationService,
ILanguageService languageService,
IStoreService storeService,
ICurrencyService currencyService)
{
_customerService = customerService;
_loyaltyPointsService = loyaltyPointsService;
_mediator = mediator;
_orderService = orderService;
_translationService = translationService;
_languageService = languageService;
_storeService = storeService;
_currencyService = currencyService;
}

Expand All @@ -49,7 +58,7 @@ await _currencyService.ConvertToPrimaryStoreCurrency(
//add loyalty points
await _loyaltyPointsService.AddLoyaltyPointsHistory(customer.Id, points,
request.Order.StoreId,
string.Format(_translationService.GetResource("LoyaltyPoints.Message.EarnedForOrder"),
string.Format(await GetMessage("LoyaltyPoints.Message.EarnedForOrder", request.Order),
request.Order.OrderNumber));

request.Order.CalcLoyaltyPoints += points;
Expand All @@ -58,4 +67,12 @@ await _loyaltyPointsService.AddLoyaltyPointsHistory(customer.Id, points,

return true;
}

//the message is written in the language the order was placed in, or in the store's language when that is gone
private async Task<string> GetMessage(string resourceKey, Order order)
{
var store = await _storeService.GetStoreById(order.StoreId);
var language = await _languageService.GetLanguageOrStoreDefault(order.CustomerLanguageId, store);
return language != null ? _translationService.GetResource(resourceKey, language.Id) : "";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -458,15 +458,15 @@ await _checkoutAttributeFormatter.FormatAttributes(details.CheckoutAttributes, d
//validate the entire shopping cart
var warnings =
await _shoppingCartValidator.GetShoppingCartWarnings(details.Cart, details.CheckoutAttributes, details.Context,
true, true);
details.WorkingLanguage, true, true);
if (warnings.Any()) throw new GrandException(string.Join(", ", warnings));

//validate individual cart items
foreach (var sci in details.Cart)
{
var product = await _productService.GetProductById(sci.ProductId);
var sciWarnings = await _shoppingCartValidator.GetShoppingCartItemWarnings(details.Customer, sci, product,
new ShoppingCartValidatorOptions());
new ShoppingCartValidatorOptions(), details.WorkingLanguage);
if (sciWarnings.Any())
{
var warningsSb = new StringBuilder();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public ReOrderCommandHandler(
public async Task<IList<string>> Handle(ReOrderCommand request, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(request.Order);
ArgumentNullException.ThrowIfNull(request.Language);

var warnings = new List<string>();
var customer = await _customerService.GetCustomerById(request.Order.CustomerId);
Expand All @@ -42,7 +43,8 @@ public async Task<IList<string>> Handle(ReOrderCommand request, CancellationToke
if (product != null)
{
if (product.ProductTypeId == ProductType.SimpleProduct)
warnings.AddRange((await _shoppingCartService.AddToCart(customer, orderItem.ProductId,
warnings.AddRange((await _shoppingCartService.AddToCart(customer,
request.Language, orderItem.ProductId,
ShoppingCartType.ShoppingCart, request.Order.StoreId, orderItem.WarehouseId,
orderItem.Attributes,
product.EnteredPrice
Expand Down
Loading
Loading