using Microsoft.AspNetCore.Mvc.Rendering; using Nop.Core; using Nop.Core.Domain.Catalog; using Nop.Core.Domain.Common; using Nop.Core.Domain.Customers; using Nop.Core.Domain.Directory; using Nop.Services.Attributes; using Nop.Services.Common; using Nop.Services.Directory; using Nop.Services.Localization; using Nop.Web.Models.Common; namespace Nop.Web.Factories; /// /// Represents the address model factory /// public partial class AddressModelFactory : IAddressModelFactory { #region Fields protected readonly AddressSettings _addressSettings; protected readonly IAddressService _addressService; protected readonly IAttributeFormatter _addressAttributeFormatter; protected readonly IAttributeParser _addressAttributeParser; protected readonly IAttributeService _addressAttributeService; protected readonly ICountryService _countryService; protected readonly ILocalizationService _localizationService; protected readonly IStateProvinceService _stateProvinceService; protected readonly IWorkContext _workContext; #endregion #region Ctor public AddressModelFactory(AddressSettings addressSettings, IAddressService addressService, IAttributeFormatter addressAttributeFormatter, IAttributeParser addressAttributeParser, IAttributeService addressAttributeService, ICountryService countryService, ILocalizationService localizationService, IStateProvinceService stateProvinceService, IWorkContext workContext) { _addressSettings = addressSettings; _addressService = addressService; _addressAttributeFormatter = addressAttributeFormatter; _addressAttributeParser = addressAttributeParser; _addressAttributeService = addressAttributeService; _countryService = countryService; _localizationService = localizationService; _stateProvinceService = stateProvinceService; _workContext = workContext; } #endregion #region Utilities /// /// Prepare address attributes /// /// Address model /// Address entity /// Overridden address attributes in XML format; pass null to use CustomAttributes of address entity /// A task that represents the asynchronous operation protected virtual async Task PrepareCustomAddressAttributesAsync(AddressModel model, Address address, string overrideAttributesXml = "") { var attributes = await _addressAttributeService.GetAllAttributesAsync(); foreach (var attribute in attributes) { var attributeModel = new AddressAttributeModel { Id = attribute.Id, ControlId = string.Format(NopCommonDefaults.AddressAttributeControlName, attribute.Id), Name = await _localizationService.GetLocalizedAsync(attribute, x => x.Name), IsRequired = attribute.IsRequired, AttributeControlType = attribute.AttributeControlType, }; if (attribute.ShouldHaveValues) { //values var attributeValues = await _addressAttributeService.GetAttributeValuesAsync(attribute.Id); foreach (var attributeValue in attributeValues) { var attributeValueModel = new AddressAttributeValueModel { Id = attributeValue.Id, Name = await _localizationService.GetLocalizedAsync(attributeValue, x => x.Name), IsPreSelected = attributeValue.IsPreSelected }; attributeModel.Values.Add(attributeValueModel); } } //set already selected attributes var selectedAddressAttributes = !string.IsNullOrEmpty(overrideAttributesXml) ? overrideAttributesXml : address?.CustomAttributes; switch (attribute.AttributeControlType) { case AttributeControlType.DropdownList: case AttributeControlType.RadioList: case AttributeControlType.Checkboxes: { if (!string.IsNullOrEmpty(selectedAddressAttributes)) { //clear default selection foreach (var item in attributeModel.Values) item.IsPreSelected = false; //select new values var selectedValues = await _addressAttributeParser.ParseAttributeValuesAsync(selectedAddressAttributes); foreach (var attributeValue in selectedValues) foreach (var item in attributeModel.Values) if (attributeValue.Id == item.Id) item.IsPreSelected = true; } } break; case AttributeControlType.ReadonlyCheckboxes: { //do nothing //values are already pre-set } break; case AttributeControlType.TextBox: case AttributeControlType.MultilineTextbox: { if (!string.IsNullOrEmpty(selectedAddressAttributes)) { var enteredText = _addressAttributeParser.ParseValues(selectedAddressAttributes, attribute.Id); if (enteredText.Any()) attributeModel.DefaultValue = enteredText[0]; } } break; case AttributeControlType.ColorSquares: case AttributeControlType.ImageSquares: case AttributeControlType.Datepicker: case AttributeControlType.FileUpload: default: //not supported attribute control types break; } model.CustomAddressAttributes.Add(attributeModel); } } #endregion #region Methods /// /// Prepare address model /// /// Address model /// Address entity /// Whether to exclude populating of model properties from the entity /// Address settings /// Countries loading function; pass null if countries do not need to load /// Whether to populate model properties with the customer fields (used with the customer entity) /// Customer entity; required if prePopulateWithCustomerFields is true /// Overridden address attributes in XML format; pass null to use CustomAttributes of the address entity /// A task that represents the asynchronous operation public virtual async Task PrepareAddressModelAsync(AddressModel model, Address address, bool excludeProperties, AddressSettings addressSettings, Func>> loadCountries = null, bool prePopulateWithCustomerFields = false, Customer customer = null, string overrideAttributesXml = "") { ArgumentNullException.ThrowIfNull(model); ArgumentNullException.ThrowIfNull(addressSettings); var languageId = customer != null ? (customer?.LanguageId ?? 0) : (await _workContext.GetWorkingLanguageAsync()).Id; if (!excludeProperties && address != null) { model.Id = address.Id; model.FirstName = address.FirstName; model.LastName = address.LastName; model.Email = address.Email; model.Company = address.Company; model.CountryId = address.CountryId; model.CountryName = await _countryService.GetCountryByAddressAsync(address) is Country country ? await _localizationService.GetLocalizedAsync(country, x => x.Name) : null; model.StateProvinceId = address.StateProvinceId; model.StateProvinceName = await _stateProvinceService.GetStateProvinceByAddressAsync(address) is StateProvince stateProvince ? await _localizationService.GetLocalizedAsync(stateProvince, x => x.Name) : null; model.County = address.County; model.City = address.City; model.Address1 = address.Address1; model.Address2 = address.Address2; model.ZipPostalCode = address.ZipPostalCode; model.PhoneNumber = address.PhoneNumber; model.FaxNumber = address.FaxNumber; } if (address == null && prePopulateWithCustomerFields) { if (customer == null) throw new Exception("Customer cannot be null when prepopulating an address"); model.Email = customer.Email; model.FirstName = customer.FirstName; model.LastName = customer.LastName; model.Company = customer.Company; model.Address1 = customer.StreetAddress; model.Address2 = customer.StreetAddress2; model.ZipPostalCode = customer.ZipPostalCode; model.City = customer.City; model.County = customer.County; model.PhoneNumber = customer.Phone; model.FaxNumber = customer.Fax; } //countries and states if (addressSettings.CountryEnabled && loadCountries != null) { var countries = await loadCountries(); if (_addressSettings.PreselectCountryIfOnlyOne && countries.Count == 1) { model.CountryId = countries[0].Id; } else { model.AvailableCountries.Add(new SelectListItem { Text = await _localizationService.GetResourceAsync("Address.SelectCountry"), Value = "0" }); } if (addressSettings.DefaultCountryId != null) model.CountryId = model.CountryId ?? addressSettings.DefaultCountryId; foreach (var c in countries) { model.AvailableCountries.Add(new SelectListItem { Text = await _localizationService.GetLocalizedAsync(c, x => x.Name), Value = c.Id.ToString(), Selected = c.Id == model.CountryId }); } if (addressSettings.StateProvinceEnabled) { var states = (await _stateProvinceService .GetStateProvincesByCountryIdAsync(model.CountryId ?? 0, languageId)) .ToList(); if (states.Any()) { model.AvailableStates.Add(new SelectListItem { Text = await _localizationService.GetResourceAsync("Address.SelectState"), Value = "0" }); foreach (var s in states) { model.AvailableStates.Add(new SelectListItem { Text = await _localizationService.GetLocalizedAsync(s, x => x.Name), Value = s.Id.ToString(), Selected = (s.Id == model.StateProvinceId) }); } } else { var anyCountrySelected = model.AvailableCountries.Any(x => x.Selected); model.AvailableStates.Add(new SelectListItem { Text = await _localizationService.GetResourceAsync(anyCountrySelected ? "Address.Other" : "Address.SelectState"), Value = "0" }); } } } //form fields model.CompanyEnabled = addressSettings.CompanyEnabled; model.CompanyRequired = addressSettings.CompanyRequired; model.StreetAddressEnabled = addressSettings.StreetAddressEnabled; model.StreetAddressRequired = addressSettings.StreetAddressRequired; model.StreetAddress2Enabled = addressSettings.StreetAddress2Enabled; model.StreetAddress2Required = addressSettings.StreetAddress2Required; model.ZipPostalCodeEnabled = addressSettings.ZipPostalCodeEnabled; model.ZipPostalCodeRequired = addressSettings.ZipPostalCodeRequired; model.CityEnabled = addressSettings.CityEnabled; model.CityRequired = addressSettings.CityRequired; model.CountyEnabled = addressSettings.CountyEnabled; model.CountyRequired = addressSettings.CountyRequired; model.CountryEnabled = addressSettings.CountryEnabled; model.StateProvinceEnabled = addressSettings.StateProvinceEnabled; model.PhoneEnabled = addressSettings.PhoneEnabled; model.PhoneRequired = addressSettings.PhoneRequired; model.FaxEnabled = addressSettings.FaxEnabled; model.FaxRequired = addressSettings.FaxRequired; model.DefaultCountryId = addressSettings.DefaultCountryId; //customer attribute services if (_addressAttributeService != null && _addressAttributeParser != null) { await PrepareCustomAddressAttributesAsync(model, address, overrideAttributesXml); } if (_addressAttributeFormatter != null && address != null) { model.FormattedCustomAddressAttributes = await _addressAttributeFormatter.FormatAttributesAsync(address.CustomAttributes); } (model.AddressLine, model.AddressFields) = await _addressService.FormatAddressAsync(address, languageId); } #endregion }