using Microsoft.AspNetCore.Mvc; using Nop.Core.Domain.Catalog; using Nop.Core.Domain.Directory; using Nop.Core.Domain.Orders; using Nop.Services.Attributes; using Nop.Services.Directory; using Nop.Services.Localization; using Nop.Services.Logging; using Nop.Services.Messages; using Nop.Services.Security; using Nop.Services.Stores; using Nop.Web.Areas.Admin.Factories; using Nop.Web.Areas.Admin.Infrastructure.Mapper.Extensions; using Nop.Web.Areas.Admin.Models.Orders; using Nop.Web.Framework.Mvc; using Nop.Web.Framework.Mvc.Filters; namespace Nop.Web.Areas.Admin.Controllers; public partial class CheckoutAttributeController : BaseAdminController { #region Fields protected readonly CurrencySettings _currencySettings; protected readonly IAttributeParser _checkoutAttributeParser; protected readonly IAttributeService _checkoutAttributeService; protected readonly ICheckoutAttributeModelFactory _checkoutAttributeModelFactory; protected readonly ICurrencyService _currencyService; protected readonly ICustomerActivityService _customerActivityService; protected readonly ILocalizationService _localizationService; protected readonly ILocalizedEntityService _localizedEntityService; protected readonly INotificationService _notificationService; protected readonly IMeasureService _measureService; protected readonly IPermissionService _permissionService; protected readonly IStoreMappingService _storeMappingService; protected readonly IStoreService _storeService; protected readonly MeasureSettings _measureSettings; #endregion #region Ctor public CheckoutAttributeController(CurrencySettings currencySettings, IAttributeParser checkoutAttributeParser, IAttributeService checkoutAttributeService, ICheckoutAttributeModelFactory checkoutAttributeModelFactory, ICurrencyService currencyService, ICustomerActivityService customerActivityService, ILocalizationService localizationService, ILocalizedEntityService localizedEntityService, INotificationService notificationService, IMeasureService measureService, IPermissionService permissionService, IStoreMappingService storeMappingService, IStoreService storeService, MeasureSettings measureSettings) { _currencySettings = currencySettings; _checkoutAttributeParser = checkoutAttributeParser; _checkoutAttributeService = checkoutAttributeService; _checkoutAttributeModelFactory = checkoutAttributeModelFactory; _currencyService = currencyService; _customerActivityService = customerActivityService; _localizationService = localizationService; _localizedEntityService = localizedEntityService; _notificationService = notificationService; _measureService = measureService; _permissionService = permissionService; _storeMappingService = storeMappingService; _storeService = storeService; _measureSettings = measureSettings; } #endregion #region Utilities protected virtual async Task UpdateAttributeLocalesAsync(CheckoutAttribute checkoutAttribute, CheckoutAttributeModel model) { foreach (var localized in model.Locales) { await _localizedEntityService.SaveLocalizedValueAsync(checkoutAttribute, x => x.Name, localized.Name, localized.LanguageId); await _localizedEntityService.SaveLocalizedValueAsync(checkoutAttribute, x => x.TextPrompt, localized.TextPrompt, localized.LanguageId); await _localizedEntityService.SaveLocalizedValueAsync(checkoutAttribute, x => x.DefaultValue, localized.DefaultValue, localized.LanguageId); } } protected virtual async Task UpdateValueLocalesAsync(CheckoutAttributeValue checkoutAttributeValue, CheckoutAttributeValueModel model) { foreach (var localized in model.Locales) { await _localizedEntityService.SaveLocalizedValueAsync(checkoutAttributeValue, x => x.Name, localized.Name, localized.LanguageId); } } protected virtual async Task SaveStoreMappingsAsync(CheckoutAttribute checkoutAttribute, CheckoutAttributeModel model) { checkoutAttribute.LimitedToStores = model.SelectedStoreIds.Any(); await _checkoutAttributeService.UpdateAttributeAsync(checkoutAttribute); var existingStoreMappings = await _storeMappingService.GetStoreMappingsAsync(checkoutAttribute); var allStores = await _storeService.GetAllStoresAsync(); foreach (var store in allStores) { if (model.SelectedStoreIds.Contains(store.Id)) { //new store if (!existingStoreMappings.Any(sm => sm.StoreId == store.Id)) await _storeMappingService.InsertStoreMappingAsync(checkoutAttribute, store.Id); } else { //remove store var storeMappingToDelete = existingStoreMappings.FirstOrDefault(sm => sm.StoreId == store.Id); if (storeMappingToDelete != null) await _storeMappingService.DeleteStoreMappingAsync(storeMappingToDelete); } } } protected virtual async Task SaveConditionAttributesAsync(CheckoutAttribute checkoutAttribute, CheckoutAttributeModel model) { string attributesXml = null; if (model.ConditionModel.EnableCondition) { var attribute = await _checkoutAttributeService.GetAttributeByIdAsync(model.ConditionModel.SelectedAttributeId); if (attribute != null) { switch (attribute.AttributeControlType) { case AttributeControlType.DropdownList: case AttributeControlType.RadioList: case AttributeControlType.ColorSquares: case AttributeControlType.ImageSquares: { var selectedAttribute = model.ConditionModel.ConditionAttributes .FirstOrDefault(x => x.Id == model.ConditionModel.SelectedAttributeId); var selectedValue = selectedAttribute?.SelectedValueId; //for conditions we should empty values save even when nothing is selected //otherwise "attributesXml" will be empty //hence we won't be able to find a selected attribute attributesXml = _checkoutAttributeParser.AddAttribute(null, attribute, string.IsNullOrEmpty(selectedValue) ? string.Empty : selectedValue); } break; case AttributeControlType.Checkboxes: { var selectedAttribute = model.ConditionModel.ConditionAttributes .FirstOrDefault(x => x.Id == model.ConditionModel.SelectedAttributeId); var selectedValues = selectedAttribute?.Values .Where(x => x.Selected) .Select(x => x.Value) .ToList(); if (selectedValues?.Any() ?? false) foreach (var value in selectedValues) attributesXml = _checkoutAttributeParser.AddAttribute(attributesXml, attribute, value); else attributesXml = _checkoutAttributeParser.AddAttribute(null, attribute, string.Empty); } break; case AttributeControlType.ReadonlyCheckboxes: case AttributeControlType.TextBox: case AttributeControlType.MultilineTextbox: case AttributeControlType.Datepicker: case AttributeControlType.FileUpload: default: //these attribute types are not supported as conditions break; } } } checkoutAttribute.ConditionAttributeXml = attributesXml; } #endregion #region Checkout attributes public virtual IActionResult Index() { return RedirectToAction("List"); } [CheckPermission(StandardPermission.Catalog.CHECKOUT_ATTRIBUTES_VIEW)] public virtual async Task List() { //prepare model var model = await _checkoutAttributeModelFactory.PrepareCheckoutAttributeSearchModelAsync(new CheckoutAttributeSearchModel()); return View(model); } [HttpPost] [CheckPermission(StandardPermission.Catalog.CHECKOUT_ATTRIBUTES_VIEW)] public virtual async Task List(CheckoutAttributeSearchModel searchModel) { //prepare model var model = await _checkoutAttributeModelFactory.PrepareCheckoutAttributeListModelAsync(searchModel); return Json(model); } [CheckPermission(StandardPermission.Catalog.CHECKOUT_ATTRIBUTES_CREATE_EDIT_DELETE)] public virtual async Task Create() { //prepare model var model = await _checkoutAttributeModelFactory.PrepareCheckoutAttributeModelAsync(new CheckoutAttributeModel(), null); return View(model); } [HttpPost, ParameterBasedOnFormName("save-continue", "continueEditing")] [CheckPermission(StandardPermission.Catalog.CHECKOUT_ATTRIBUTES_CREATE_EDIT_DELETE)] public virtual async Task Create(CheckoutAttributeModel model, bool continueEditing) { if (ModelState.IsValid) { var checkoutAttribute = model.ToEntity(); await _checkoutAttributeService.InsertAttributeAsync(checkoutAttribute); //locales await UpdateAttributeLocalesAsync(checkoutAttribute, model); //stores await SaveStoreMappingsAsync(checkoutAttribute, model); //activity log await _customerActivityService.InsertActivityAsync("AddNewCheckoutAttribute", string.Format(await _localizationService.GetResourceAsync("ActivityLog.AddNewCheckoutAttribute"), checkoutAttribute.Name), checkoutAttribute); _notificationService.SuccessNotification(await _localizationService.GetResourceAsync("Admin.Catalog.Attributes.CheckoutAttributes.Added")); if (!continueEditing) return RedirectToAction("List"); return RedirectToAction("Edit", new { id = checkoutAttribute.Id }); } //prepare model model = await _checkoutAttributeModelFactory.PrepareCheckoutAttributeModelAsync(model, null, true); //if we got this far, something failed, redisplay form return View(model); } [CheckPermission(StandardPermission.Catalog.CHECKOUT_ATTRIBUTES_VIEW)] public virtual async Task Edit(int id) { //try to get a checkout attribute with the specified id var checkoutAttribute = await _checkoutAttributeService.GetAttributeByIdAsync(id); if (checkoutAttribute == null) return RedirectToAction("List"); //prepare model var model = await _checkoutAttributeModelFactory.PrepareCheckoutAttributeModelAsync(null, checkoutAttribute); return View(model); } [HttpPost, ParameterBasedOnFormName("save-continue", "continueEditing")] [CheckPermission(StandardPermission.Catalog.CHECKOUT_ATTRIBUTES_CREATE_EDIT_DELETE)] public virtual async Task Edit(CheckoutAttributeModel model, bool continueEditing) { //try to get a checkout attribute with the specified id var checkoutAttribute = await _checkoutAttributeService.GetAttributeByIdAsync(model.Id); if (checkoutAttribute == null) return RedirectToAction("List"); if (ModelState.IsValid) { checkoutAttribute = model.ToEntity(checkoutAttribute); await SaveConditionAttributesAsync(checkoutAttribute, model); await _checkoutAttributeService.UpdateAttributeAsync(checkoutAttribute); //locales await UpdateAttributeLocalesAsync(checkoutAttribute, model); //stores await SaveStoreMappingsAsync(checkoutAttribute, model); //activity log await _customerActivityService.InsertActivityAsync("EditCheckoutAttribute", string.Format(await _localizationService.GetResourceAsync("ActivityLog.EditCheckoutAttribute"), checkoutAttribute.Name), checkoutAttribute); _notificationService.SuccessNotification(await _localizationService.GetResourceAsync("Admin.Catalog.Attributes.CheckoutAttributes.Updated")); if (!continueEditing) return RedirectToAction("List"); return RedirectToAction("Edit", new { id = checkoutAttribute.Id }); } //prepare model model = await _checkoutAttributeModelFactory.PrepareCheckoutAttributeModelAsync(model, checkoutAttribute, true); //if we got this far, something failed, redisplay form return View(model); } [HttpPost] [CheckPermission(StandardPermission.Catalog.CHECKOUT_ATTRIBUTES_CREATE_EDIT_DELETE)] public virtual async Task Delete(int id) { //try to get a checkout attribute with the specified id var checkoutAttribute = await _checkoutAttributeService.GetAttributeByIdAsync(id); if (checkoutAttribute == null) return RedirectToAction("List"); await _checkoutAttributeService.DeleteAttributeAsync(checkoutAttribute); //activity log await _customerActivityService.InsertActivityAsync("DeleteCheckoutAttribute", string.Format(await _localizationService.GetResourceAsync("ActivityLog.DeleteCheckoutAttribute"), checkoutAttribute.Name), checkoutAttribute); _notificationService.SuccessNotification(await _localizationService.GetResourceAsync("Admin.Catalog.Attributes.CheckoutAttributes.Deleted")); return RedirectToAction("List"); } [HttpPost] [CheckPermission(StandardPermission.Catalog.CHECKOUT_ATTRIBUTES_CREATE_EDIT_DELETE)] public virtual async Task DeleteSelected(ICollection selectedIds) { if (selectedIds == null || !selectedIds.Any()) return NoContent(); var checkoutAttributes = await _checkoutAttributeService.GetAttributeByIdsAsync(selectedIds.ToArray()); await _checkoutAttributeService.DeleteAttributesAsync(checkoutAttributes); foreach (var checkoutAttribute in checkoutAttributes) { //activity log await _customerActivityService.InsertActivityAsync("DeleteCheckoutAttribute", string.Format(await _localizationService.GetResourceAsync("ActivityLog.DeleteCheckoutAttribute"), checkoutAttribute.Name), checkoutAttribute); } return Json(new { Result = true }); } #endregion #region Checkout attribute values [HttpPost] [CheckPermission(StandardPermission.Catalog.CHECKOUT_ATTRIBUTES_VIEW)] public virtual async Task ValueList(CheckoutAttributeValueSearchModel searchModel) { //try to get a checkout attribute with the specified id var checkoutAttribute = await _checkoutAttributeService.GetAttributeByIdAsync(searchModel.CheckoutAttributeId) ?? throw new ArgumentException("No checkout attribute found with the specified id"); //prepare model var model = await _checkoutAttributeModelFactory.PrepareCheckoutAttributeValueListModelAsync(searchModel, checkoutAttribute); return Json(model); } [CheckPermission(StandardPermission.Catalog.CHECKOUT_ATTRIBUTES_CREATE_EDIT_DELETE)] public virtual async Task ValueCreatePopup(int checkoutAttributeId) { //try to get a checkout attribute with the specified id var checkoutAttribute = await _checkoutAttributeService.GetAttributeByIdAsync(checkoutAttributeId); if (checkoutAttribute == null) return RedirectToAction("List"); //prepare model var model = await _checkoutAttributeModelFactory .PrepareCheckoutAttributeValueModelAsync(new CheckoutAttributeValueModel(), checkoutAttribute, null); return View(model); } [HttpPost] [CheckPermission(StandardPermission.Catalog.CHECKOUT_ATTRIBUTES_CREATE_EDIT_DELETE)] public virtual async Task ValueCreatePopup(CheckoutAttributeValueModel model) { //try to get a checkout attribute with the specified id var checkoutAttribute = await _checkoutAttributeService.GetAttributeByIdAsync(model.AttributeId); if (checkoutAttribute == null) return RedirectToAction("List"); model.PrimaryStoreCurrencyCode = (await _currencyService.GetCurrencyByIdAsync(_currencySettings.PrimaryStoreCurrencyId)).CurrencyCode; model.BaseWeightIn = (await _measureService.GetMeasureWeightByIdAsync(_measureSettings.BaseWeightId)).Name; if (checkoutAttribute.AttributeControlType == AttributeControlType.ColorSquares) { //ensure valid color is chosen/entered if (string.IsNullOrEmpty(model.ColorSquaresRgb)) ModelState.AddModelError(string.Empty, "Color is required"); try { //ensure color is valid (can be instantiated) System.Drawing.ColorTranslator.FromHtml(model.ColorSquaresRgb); } catch (Exception exc) { ModelState.AddModelError(string.Empty, exc.Message); } } if (ModelState.IsValid) { var checkoutAttributeValue = model.ToEntity(); await _checkoutAttributeService.InsertAttributeValueAsync(checkoutAttributeValue); await UpdateValueLocalesAsync(checkoutAttributeValue, model); ViewBag.RefreshPage = true; return View(model); } //prepare model model = await _checkoutAttributeModelFactory.PrepareCheckoutAttributeValueModelAsync(model, checkoutAttribute, null, true); //if we got this far, something failed, redisplay form return View(model); } [CheckPermission(StandardPermission.Catalog.CHECKOUT_ATTRIBUTES_VIEW)] public virtual async Task ValueEditPopup(int id) { //try to get a checkout attribute value with the specified id var checkoutAttributeValue = await _checkoutAttributeService.GetAttributeValueByIdAsync(id); if (checkoutAttributeValue == null) return RedirectToAction("List"); //try to get a checkout attribute with the specified id var checkoutAttribute = await _checkoutAttributeService.GetAttributeByIdAsync(checkoutAttributeValue.AttributeId); if (checkoutAttribute == null) return RedirectToAction("List"); //prepare model var model = await _checkoutAttributeModelFactory.PrepareCheckoutAttributeValueModelAsync(null, checkoutAttribute, checkoutAttributeValue); return View(model); } [HttpPost] [CheckPermission(StandardPermission.Catalog.CHECKOUT_ATTRIBUTES_CREATE_EDIT_DELETE)] public virtual async Task ValueEditPopup(CheckoutAttributeValueModel model) { //try to get a checkout attribute value with the specified id var checkoutAttributeValue = await _checkoutAttributeService.GetAttributeValueByIdAsync(model.Id); if (checkoutAttributeValue == null) return RedirectToAction("List"); //try to get a checkout attribute with the specified id var checkoutAttribute = await _checkoutAttributeService.GetAttributeByIdAsync(checkoutAttributeValue.AttributeId); if (checkoutAttribute == null) return RedirectToAction("List"); model.PrimaryStoreCurrencyCode = (await _currencyService.GetCurrencyByIdAsync(_currencySettings.PrimaryStoreCurrencyId)).CurrencyCode; model.BaseWeightIn = (await _measureService.GetMeasureWeightByIdAsync(_measureSettings.BaseWeightId)).Name; if (checkoutAttribute.AttributeControlType == AttributeControlType.ColorSquares) { //ensure valid color is chosen/entered if (string.IsNullOrEmpty(model.ColorSquaresRgb)) ModelState.AddModelError(string.Empty, "Color is required"); try { //ensure color is valid (can be instantiated) System.Drawing.ColorTranslator.FromHtml(model.ColorSquaresRgb); } catch (Exception exc) { ModelState.AddModelError(string.Empty, exc.Message); } } if (ModelState.IsValid) { checkoutAttributeValue = model.ToEntity(checkoutAttributeValue); await _checkoutAttributeService.UpdateAttributeValueAsync(checkoutAttributeValue); await UpdateValueLocalesAsync(checkoutAttributeValue, model); ViewBag.RefreshPage = true; return View(model); } //prepare model model = await _checkoutAttributeModelFactory.PrepareCheckoutAttributeValueModelAsync(model, checkoutAttribute, checkoutAttributeValue, true); //if we got this far, something failed, redisplay form return View(model); } [HttpPost] [CheckPermission(StandardPermission.Catalog.CHECKOUT_ATTRIBUTES_CREATE_EDIT_DELETE)] public virtual async Task ValueDelete(int id) { //try to get a checkout attribute value with the specified id var checkoutAttributeValue = await _checkoutAttributeService.GetAttributeValueByIdAsync(id) ?? throw new ArgumentException("No checkout attribute value found with the specified id", nameof(id)); await _checkoutAttributeService.DeleteAttributeValueAsync(checkoutAttributeValue); return new NullJsonResult(); } #endregion }