private async Task <StringBuilder> PrepareFormattedAttribute(Product product, string attributesXml, string langId,
                                                                     string serapator, bool htmlEncode, bool renderPrices,
                                                                     bool allowHyperlinks, bool showInAdmin)
        {
            var result     = new StringBuilder();
            var attributes = _productAttributeParser.ParseProductAttributeMappings(product, attributesXml);

            for (int i = 0; i < attributes.Count; i++)
            {
                var productAttribute = await _productAttributeService.GetProductAttributeById(attributes[i].ProductAttributeId);

                var attribute = attributes[i];
                var valuesStr = _productAttributeParser.ParseValues(attributesXml, attribute.Id);
                for (int j = 0; j < valuesStr.Count; j++)
                {
                    string valueStr           = valuesStr[j];
                    string formattedAttribute = string.Empty;
                    if (!attribute.ShouldHaveValues())
                    {
                        //no values
                        if (attribute.AttributeControlType == AttributeControlType.MultilineTextbox)
                        {
                            //multiline textbox
                            var attributeName = productAttribute.GetLocalized(a => a.Name, langId);
                            //encode (if required)
                            if (htmlEncode)
                            {
                                attributeName = WebUtility.HtmlEncode(attributeName);
                            }
                            formattedAttribute = string.Format("{0}: {1}", attributeName, HtmlHelper.FormatText(valueStr));
                            //we never encode multiline textbox input
                        }
                        else if (attribute.AttributeControlType == AttributeControlType.FileUpload)
                        {
                            //file upload
                            Guid downloadGuid;
                            Guid.TryParse(valueStr, out downloadGuid);
                            var download = await _downloadService.GetDownloadByGuid(downloadGuid);

                            if (download != null)
                            {
                                //TODO add a method for getting URL (use routing because it handles all SEO friendly URLs)
                                string attributeText = "";
                                var    fileName      = string.Format("{0}{1}",
                                                                     download.Filename ?? download.DownloadGuid.ToString(),
                                                                     download.Extension);
                                //encode (if required)
                                if (htmlEncode)
                                {
                                    fileName = WebUtility.HtmlEncode(fileName);
                                }
                                if (allowHyperlinks)
                                {
                                    //hyperlinks are allowed
                                    var downloadLink = string.Format("{0}download/getfileupload/?downloadId={1}", _webHelper.GetStoreLocation(false), download.DownloadGuid);
                                    attributeText = string.Format("<a href=\"{0}\" class=\"fileuploadattribute\">{1}</a>", downloadLink, fileName);
                                }
                                else
                                {
                                    //hyperlinks aren't allowed
                                    attributeText = fileName;
                                }
                                var attributeName = productAttribute.GetLocalized(a => a.Name, langId);
                                //encode (if required)
                                if (htmlEncode)
                                {
                                    attributeName = WebUtility.HtmlEncode(attributeName);
                                }
                                formattedAttribute = string.Format("{0}: {1}", attributeName, attributeText);
                            }
                        }
                        else
                        {
                            //other attributes (textbox, datepicker)
                            formattedAttribute = string.Format("{0}: {1}", productAttribute.GetLocalized(a => a.Name, langId), valueStr);
                            //encode (if required)
                            if (htmlEncode)
                            {
                                formattedAttribute = WebUtility.HtmlEncode(formattedAttribute);
                            }
                        }
                    }
                    else
                    {
                        //attributes with values
                        if (product.ProductAttributeMappings.Where(x => x.Id == attributes[i].Id).FirstOrDefault() != null)
                        {
                            var attributeValue = product.ProductAttributeMappings.Where(x => x.Id == attributes[i].Id).FirstOrDefault().ProductAttributeValues.Where(x => x.Id == valueStr).FirstOrDefault();
                            if (attributeValue != null)
                            {
                                formattedAttribute = string.Format("{0}: {1}", productAttribute.GetLocalized(a => a.Name, langId), attributeValue.GetLocalized(a => a.Name, langId));

                                if (renderPrices)
                                {
                                    decimal attributeValuePriceAdjustment = await _priceCalculationService.GetProductAttributeValuePriceAdjustment(attributeValue);

                                    var prices = await _taxService.GetProductPrice(product, attributeValuePriceAdjustment, _workContext.CurrentCustomer);

                                    decimal priceAdjustmentBase = prices.productprice;
                                    decimal taxRate             = prices.taxRate;
                                    decimal priceAdjustment     = await _currencyService.ConvertFromPrimaryStoreCurrency(priceAdjustmentBase, _workContext.WorkingCurrency);

                                    if (priceAdjustmentBase > 0)
                                    {
                                        string priceAdjustmentStr = _priceFormatter.FormatPrice(priceAdjustment, false, false);
                                        formattedAttribute += string.Format(" [+{0}]", priceAdjustmentStr);
                                    }
                                    else if (priceAdjustmentBase < decimal.Zero)
                                    {
                                        string priceAdjustmentStr = _priceFormatter.FormatPrice(-priceAdjustment, false, false);
                                        formattedAttribute += string.Format(" [-{0}]", priceAdjustmentStr);
                                    }
                                }

                                //display quantity
                                if (_shoppingCartSettings.RenderAssociatedAttributeValueQuantity &&
                                    attributeValue.AttributeValueType == AttributeValueType.AssociatedToProduct)
                                {
                                    //render only when more than 1
                                    if (attributeValue.Quantity > 1)
                                    {
                                        //TODO localize resource
                                        formattedAttribute += string.Format(" - qty {0}", attributeValue.Quantity);
                                    }
                                }
                            }
                            else
                            {
                                if (showInAdmin)
                                {
                                    formattedAttribute += string.Format("{0}: {1}", productAttribute.GetLocalized(a => a.Name, langId), "");
                                }
                            }

                            //encode (if required)
                            if (htmlEncode)
                            {
                                formattedAttribute = WebUtility.HtmlEncode(formattedAttribute);
                            }
                        }
                    }

                    if (!String.IsNullOrEmpty(formattedAttribute))
                    {
                        if (i != 0 || j != 0)
                        {
                            result.Append(serapator);
                        }
                        result.Append(formattedAttribute);
                    }
                }
            }
            return(result);
        }
        /// <summary>
        /// Formats attributes
        /// </summary>
        /// <param name="product">Product</param>
        /// <param name="attributesXml">Attributes in XML format</param>
        /// <param name="customer">Customer</param>
        /// <param name="serapator">Serapator</param>
        /// <param name="htmlEncode">A value indicating whether to encode (HTML) values</param>
        /// <param name="renderPrices">A value indicating whether to render prices</param>
        /// <param name="renderProductAttributes">A value indicating whether to render product attributes</param>
        /// <param name="renderGiftCardAttributes">A value indicating whether to render gift card attributes</param>
        /// <param name="allowHyperlinks">A value indicating whether to HTML hyperink tags could be rendered (if required)</param>
        /// <returns>Attributes</returns>
        public virtual string FormatAttributes(Product product, string attributesXml,
                                               Customer customer, string serapator = "<br />", bool htmlEncode           = true, bool renderPrices = true,
                                               bool renderProductAttributes        = true, bool renderGiftCardAttributes = true,
                                               bool allowHyperlinks = true)
        {
            var result = new StringBuilder();

            //attributes
            if (renderProductAttributes)
            {
                var attributes = _productAttributeParser.ParseProductAttributeMappings(product, attributesXml);
                for (int i = 0; i < attributes.Count; i++)
                {
                    var productAttribute = _productAttributeService.GetProductAttributeById(attributes[i].ProductAttributeId);
                    var attribute        = attributes[i];
                    var valuesStr        = _productAttributeParser.ParseValues(attributesXml, attribute.Id);
                    for (int j = 0; j < valuesStr.Count; j++)
                    {
                        string valueStr           = valuesStr[j];
                        string formattedAttribute = string.Empty;
                        if (!attribute.ShouldHaveValues())
                        {
                            //no values
                            if (attribute.AttributeControlType == AttributeControlType.MultilineTextbox)
                            {
                                //multiline textbox
                                var attributeName = productAttribute.GetLocalized(a => a.Name, _workContext.WorkingLanguage.Id);
                                //encode (if required)
                                if (htmlEncode)
                                {
                                    attributeName = HttpUtility.HtmlEncode(attributeName);
                                }
                                formattedAttribute = string.Format("{0}: {1}", attributeName, HtmlHelper.FormatText(valueStr, false, true, false, false, false, false));
                                //we never encode multiline textbox input
                            }
                            else if (attribute.AttributeControlType == AttributeControlType.FileUpload)
                            {
                                //file upload
                                Guid downloadGuid;
                                Guid.TryParse(valueStr, out downloadGuid);
                                var download = _downloadService.GetDownloadByGuid(downloadGuid);
                                if (download != null)
                                {
                                    //TODO add a method for getting URL (use routing because it handles all SEO friendly URLs)
                                    string attributeText = "";
                                    var    fileName      = string.Format("{0}{1}",
                                                                         download.Filename ?? download.DownloadGuid.ToString(),
                                                                         download.Extension);
                                    //encode (if required)
                                    if (htmlEncode)
                                    {
                                        fileName = HttpUtility.HtmlEncode(fileName);
                                    }
                                    if (allowHyperlinks)
                                    {
                                        //hyperlinks are allowed
                                        var downloadLink = string.Format("{0}download/getfileupload/?downloadId={1}", _webHelper.GetStoreLocation(false), download.DownloadGuid);
                                        attributeText = string.Format("<a href=\"{0}\" class=\"fileuploadattribute\">{1}</a>", downloadLink, fileName);
                                    }
                                    else
                                    {
                                        //hyperlinks aren't allowed
                                        attributeText = fileName;
                                    }
                                    var attributeName = productAttribute.GetLocalized(a => a.Name, _workContext.WorkingLanguage.Id);
                                    //encode (if required)
                                    if (htmlEncode)
                                    {
                                        attributeName = HttpUtility.HtmlEncode(attributeName);
                                    }
                                    formattedAttribute = string.Format("{0}: {1}", attributeName, attributeText);
                                }
                            }
                            else
                            {
                                //other attributes (textbox, datepicker)
                                formattedAttribute = string.Format("{0}: {1}", productAttribute.GetLocalized(a => a.Name, _workContext.WorkingLanguage.Id), valueStr);
                                //encode (if required)
                                if (htmlEncode)
                                {
                                    formattedAttribute = HttpUtility.HtmlEncode(formattedAttribute);
                                }
                            }
                        }
                        else
                        {
                            //attributes with values
                            int attributeValueId;
                            if (int.TryParse(valueStr, out attributeValueId))
                            {
                                if (product.ProductAttributeMappings.Where(x => x.ProductAttributeId == attributes[i].ProductAttributeId).FirstOrDefault() != null)
                                {
                                    var attributeValue = product.ProductAttributeMappings.Where(x => x.ProductAttributeId == attributes[i].ProductAttributeId).FirstOrDefault().ProductAttributeValues.Where(x => x.Id == attributeValueId).FirstOrDefault(); //_productAttributeService.GetProductAttributeValueById(attributeValueId);
                                    if (attributeValue != null)
                                    {
                                        formattedAttribute = string.Format("{0}: {1}", productAttribute.GetLocalized(a => a.Name, _workContext.WorkingLanguage.Id), attributeValue.GetLocalized(a => a.Name, _workContext.WorkingLanguage.Id));

                                        if (renderPrices)
                                        {
                                            decimal taxRate;
                                            decimal attributeValuePriceAdjustment = _priceCalculationService.GetProductAttributeValuePriceAdjustment(attributeValue);
                                            decimal priceAdjustmentBase           = _taxService.GetProductPrice(product, attributeValuePriceAdjustment, customer, out taxRate);
                                            decimal priceAdjustment = _currencyService.ConvertFromPrimaryStoreCurrency(priceAdjustmentBase, _workContext.WorkingCurrency);
                                            if (priceAdjustmentBase > 0)
                                            {
                                                string priceAdjustmentStr = _priceFormatter.FormatPrice(priceAdjustment, false, false);
                                                formattedAttribute += string.Format(" [+{0}]", priceAdjustmentStr);
                                            }
                                            else if (priceAdjustmentBase < decimal.Zero)
                                            {
                                                string priceAdjustmentStr = _priceFormatter.FormatPrice(-priceAdjustment, false, false);
                                                formattedAttribute += string.Format(" [-{0}]", priceAdjustmentStr);
                                            }
                                        }

                                        //display quantity
                                        if (_shoppingCartSettings.RenderAssociatedAttributeValueQuantity &&
                                            attributeValue.AttributeValueType == AttributeValueType.AssociatedToProduct)
                                        {
                                            //render only when more than 1
                                            if (attributeValue.Quantity > 1)
                                            {
                                                //TODO localize resource
                                                formattedAttribute += string.Format(" - qty {0}", attributeValue.Quantity);
                                            }
                                        }
                                    }
                                }

                                //encode (if required)
                                if (htmlEncode)
                                {
                                    formattedAttribute = HttpUtility.HtmlEncode(formattedAttribute);
                                }
                            }
                        }

                        if (!String.IsNullOrEmpty(formattedAttribute))
                        {
                            if (i != 0 || j != 0)
                            {
                                result.Append(serapator);
                            }
                            result.Append(formattedAttribute);
                        }
                    }
                }
            }

            //gift cards
            if (renderGiftCardAttributes)
            {
                if (product.IsGiftCard)
                {
                    string giftCardRecipientName;
                    string giftCardRecipientEmail;
                    string giftCardSenderName;
                    string giftCardSenderEmail;
                    string giftCardMessage;
                    _productAttributeParser.GetGiftCardAttribute(attributesXml, out giftCardRecipientName, out giftCardRecipientEmail,
                                                                 out giftCardSenderName, out giftCardSenderEmail, out giftCardMessage);

                    //sender
                    var giftCardFrom = product.GiftCardType == GiftCardType.Virtual ?
                                       string.Format(_localizationService.GetResource("GiftCardAttribute.From.Virtual"), giftCardSenderName, giftCardSenderEmail) :
                                       string.Format(_localizationService.GetResource("GiftCardAttribute.From.Physical"), giftCardSenderName);
                    //recipient
                    var giftCardFor = product.GiftCardType == GiftCardType.Virtual ?
                                      string.Format(_localizationService.GetResource("GiftCardAttribute.For.Virtual"), giftCardRecipientName, giftCardRecipientEmail) :
                                      string.Format(_localizationService.GetResource("GiftCardAttribute.For.Physical"), giftCardRecipientName);

                    //encode (if required)
                    if (htmlEncode)
                    {
                        giftCardFrom = HttpUtility.HtmlEncode(giftCardFrom);
                        giftCardFor  = HttpUtility.HtmlEncode(giftCardFor);
                    }

                    if (!String.IsNullOrEmpty(result.ToString()))
                    {
                        result.Append(serapator);
                    }
                    result.Append(giftCardFrom);
                    result.Append(serapator);
                    result.Append(giftCardFor);
                }
            }
            return(result.ToString());
        }
Пример #3
0
        /// <summary>
        /// Formats attributes
        /// </summary>
        /// <param name="product">Product</param>
        /// <param name="attributesXml">Attributes in XML format</param>
        /// <param name="customer">Customer</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>
        /// <param name="renderProductAttributes">A value indicating whether to render product attributes</param>
        /// <param name="renderGiftCardAttributes">A value indicating whether to render gift card attributes</param>
        /// <param name="allowHyperlinks">A value indicating whether to HTML hyperink tags could be rendered (if required)</param>
        /// <returns>Attributes</returns>
        public virtual string FormatAttributes(Product product, string attributesXml,
                                               Customer customer, string separator = "<br />", bool htmlEncode           = true, bool renderPrices = true,
                                               bool renderProductAttributes        = true, bool renderGiftCardAttributes = true,
                                               bool allowHyperlinks = true)
        {
            var result = new StringBuilder();

            //attributes
            if (renderProductAttributes)
            {
                foreach (var attribute in _productAttributeParser.ParseProductAttributeMappings(attributesXml))
                {
                    //attributes without values
                    if (!attribute.ShouldHaveValues())
                    {
                        foreach (var value in _productAttributeParser.ParseValues(attributesXml, attribute.Id))
                        {
                            var formattedAttribute = string.Empty;
                            if (attribute.AttributeControlType == AttributeControlType.MultilineTextbox)
                            {
                                //multiline textbox
                                var attributeName = _localizationService.GetLocalized(attribute.ProductAttribute, a => a.Name, _workContext.WorkingLanguage.Id);

                                //encode (if required)
                                if (htmlEncode)
                                {
                                    attributeName = WebUtility.HtmlEncode(attributeName);
                                }

                                //we never encode multiline textbox input
                                formattedAttribute = $"{attributeName}: {HtmlHelper.FormatText(value, false, true, false, false, false, false)}";
                            }
                            else if (attribute.AttributeControlType == AttributeControlType.FileUpload)
                            {
                                //file upload
                                Guid.TryParse(value, out var downloadGuid);
                                var download = _downloadService.GetDownloadByGuid(downloadGuid);
                                if (download != null)
                                {
                                    var fileName = $"{download.Filename ?? download.DownloadGuid.ToString()}{download.Extension}";

                                    //encode (if required)
                                    if (htmlEncode)
                                    {
                                        fileName = WebUtility.HtmlEncode(fileName);
                                    }

                                    //TODO add a method for getting URL (use routing because it handles all SEO friendly URLs)
                                    var attributeText = allowHyperlinks ? $"<a href=\"{_webHelper.GetStoreLocation(false)}download/getfileupload/?downloadId={download.DownloadGuid}\" class=\"fileuploadattribute\">{fileName}</a>"
                                        : fileName;

                                    var attributeName = _localizationService.GetLocalized(attribute.ProductAttribute, a => a.Name, _workContext.WorkingLanguage.Id);

                                    //encode (if required)
                                    if (htmlEncode)
                                    {
                                        attributeName = WebUtility.HtmlEncode(attributeName);
                                    }

                                    formattedAttribute = $"{attributeName}: {attributeText}";
                                }
                            }
                            else
                            {
                                //other attributes (textbox, datepicker)
                                formattedAttribute = $"{_localizationService.GetLocalized(attribute.ProductAttribute, a => a.Name, _workContext.WorkingLanguage.Id)}: {value}";

                                //encode (if required)
                                if (htmlEncode)
                                {
                                    formattedAttribute = WebUtility.HtmlEncode(formattedAttribute);
                                }
                            }

                            if (string.IsNullOrEmpty(formattedAttribute))
                            {
                                continue;
                            }

                            if (result.Length > 0)
                            {
                                result.Append(separator);
                            }
                            result.Append(formattedAttribute);
                        }
                    }
                    //product attribute values
                    else
                    {
                        foreach (var attributeValue in _productAttributeParser.ParseProductAttributeValues(attributesXml, attribute.Id))
                        {
                            var formattedAttribute = $"{_localizationService.GetLocalized(attribute.ProductAttribute, a => a.Name, _workContext.WorkingLanguage.Id)}: {_localizationService.GetLocalized(attributeValue, a => a.Name, _workContext.WorkingLanguage.Id)}";

                            if (renderPrices)
                            {
                                if (attributeValue.PriceAdjustmentUsePercentage)
                                {
                                    if (attributeValue.PriceAdjustment > decimal.Zero)
                                    {
                                        formattedAttribute += string.Format(
                                            _localizationService.GetResource("FormattedAttributes.PriceAdjustment"),
                                            "+", attributeValue.PriceAdjustment.ToString("G29"), "%");
                                    }
                                    else if (attributeValue.PriceAdjustment < decimal.Zero)
                                    {
                                        formattedAttribute += string.Format(
                                            _localizationService.GetResource("FormattedAttributes.PriceAdjustment"),
                                            string.Empty, attributeValue.PriceAdjustment.ToString("G29"), "%");
                                    }
                                }
                                else
                                {
                                    var attributeValuePriceAdjustment = _priceCalculationService.GetProductAttributeValuePriceAdjustment(attributeValue, customer);
                                    var priceAdjustmentBase           = _taxService.GetProductPrice(product, attributeValuePriceAdjustment, customer, out var _);
                                    var priceAdjustment = _currencyService.ConvertFromPrimaryStoreCurrency(priceAdjustmentBase, _workContext.WorkingCurrency);

                                    if (priceAdjustmentBase > decimal.Zero)
                                    {
                                        formattedAttribute += string.Format(
                                            _localizationService.GetResource("FormattedAttributes.PriceAdjustment"),
                                            "+", _priceFormatter.FormatPrice(priceAdjustment, false, false), string.Empty);
                                    }
                                    else if (priceAdjustmentBase < decimal.Zero)
                                    {
                                        formattedAttribute += string.Format(
                                            _localizationService.GetResource("FormattedAttributes.PriceAdjustment"),
                                            "-", _priceFormatter.FormatPrice(-priceAdjustment, false, false), string.Empty);
                                    }
                                }
                            }

                            //display quantity
                            if (_shoppingCartSettings.RenderAssociatedAttributeValueQuantity && attributeValue.AttributeValueType == AttributeValueType.AssociatedToProduct)
                            {
                                //render only when more than 1
                                if (attributeValue.Quantity > 1)
                                {
                                    formattedAttribute += string.Format(_localizationService.GetResource("ProductAttributes.Quantity"), attributeValue.Quantity);
                                }
                            }

                            //encode (if required)
                            if (htmlEncode)
                            {
                                formattedAttribute = WebUtility.HtmlEncode(formattedAttribute);
                            }

                            if (string.IsNullOrEmpty(formattedAttribute))
                            {
                                continue;
                            }

                            if (result.Length > 0)
                            {
                                result.Append(separator);
                            }
                            result.Append(formattedAttribute);
                        }
                    }
                }
            }

            //gift cards
            if (!renderGiftCardAttributes)
            {
                return(result.ToString());
            }

            if (!product.IsGiftCard)
            {
                return(result.ToString());
            }

            _productAttributeParser.GetGiftCardAttribute(attributesXml, out var giftCardRecipientName, out var giftCardRecipientEmail, out var giftCardSenderName, out var giftCardSenderEmail, out var _);

            //sender
            var giftCardFrom = product.GiftCardType == GiftCardType.Virtual ?
                               string.Format(_localizationService.GetResource("GiftCardAttribute.From.Virtual"), giftCardSenderName, giftCardSenderEmail) :
                               string.Format(_localizationService.GetResource("GiftCardAttribute.From.Physical"), giftCardSenderName);
            //recipient
            var giftCardFor = product.GiftCardType == GiftCardType.Virtual ?
                              string.Format(_localizationService.GetResource("GiftCardAttribute.For.Virtual"), giftCardRecipientName, giftCardRecipientEmail) :
                              string.Format(_localizationService.GetResource("GiftCardAttribute.For.Physical"), giftCardRecipientName);

            //encode (if required)
            if (htmlEncode)
            {
                giftCardFrom = WebUtility.HtmlEncode(giftCardFrom);
                giftCardFor  = WebUtility.HtmlEncode(giftCardFor);
            }

            if (!string.IsNullOrEmpty(result.ToString()))
            {
                result.Append(separator);
            }

            result.Append(giftCardFrom);
            result.Append(separator);
            result.Append(giftCardFor);

            return(result.ToString());
        }
        /// <inheritdoc />
        /// <summary>
        /// Function remains unmodifed and is present so custom code is called (PrepareProductAttributeModels)
        /// Prepare the product details model
        /// </summary>
        /// <param name="product">Product</param>
        /// <param name="updatecartitem">Updated shopping cart item</param>
        /// <param name="isAssociatedProduct">Whether the product is associated</param>
        /// <returns>Product details model</returns>
        //public override ProductDetailsModel PrepareProductDetailsModel(Product product,
        //    ShoppingCartItem updatecartitem = null, bool isAssociatedProduct = false)
        //{
        //    if (product == null)
        //        throw new ArgumentNullException(nameof(product));

        //    //standard properties
        //    var model = new ProductDetailsModel
        //    {
        //        Id = product.Id,
        //        Name = _localizationService.GetLocalized(product, x => x.Name),
        //        ShortDescription = _localizationService.GetLocalized(product, x => x.ShortDescription),
        //        FullDescription = _localizationService.GetLocalized(product, x => x.FullDescription),
        //        MetaKeywords = _localizationService.GetLocalized(product, x => x.MetaKeywords),
        //        MetaDescription = _localizationService.GetLocalized(product, x => x.MetaDescription),
        //        MetaTitle = _localizationService.GetLocalized(product, x => x.MetaTitle),
        //        SeName = _urlRecordService.GetSeName(product),
        //        ProductType = product.ProductType,
        //        ShowSku = _catalogSettings.ShowSkuOnProductDetailsPage,
        //        Sku = product.Sku,
        //        ShowManufacturerPartNumber = _catalogSettings.ShowManufacturerPartNumber,
        //        FreeShippingNotificationEnabled = _catalogSettings.ShowFreeShippingNotification,
        //        ManufacturerPartNumber = product.ManufacturerPartNumber,
        //        ShowGtin = _catalogSettings.ShowGtin,
        //        Gtin = product.Gtin,
        //        ManageInventoryMethod = product.ManageInventoryMethod,
        //        StockAvailability = _productService.FormatStockMessage(product, ""),
        //        HasSampleDownload = product.IsDownload && product.HasSampleDownload,
        //        DisplayDiscontinuedMessage = !product.Published && _catalogSettings.DisplayDiscontinuedMessageForUnpublishedProducts
        //    };

        //    //automatically generate product description?
        //    if (_seoSettings.GenerateProductMetaDescription && string.IsNullOrEmpty(model.MetaDescription))
        //    {
        //        //based on short description
        //        model.MetaDescription = model.ShortDescription;
        //    }

        //    //shipping info
        //    model.IsShipEnabled = product.IsShipEnabled;
        //    if (product.IsShipEnabled)
        //    {
        //        model.IsFreeShipping = product.IsFreeShipping;
        //        //delivery date
        //        var deliveryDate = _dateRangeService.GetDeliveryDateById(product.DeliveryDateId);
        //        if (deliveryDate != null)
        //        {
        //            model.DeliveryDate = _localizationService.GetLocalized(deliveryDate, dd => dd.Name);
        //        }
        //    }

        //    //email a friend
        //    model.EmailAFriendEnabled = _catalogSettings.EmailAFriendEnabled;
        //    //compare products
        //    model.CompareProductsEnabled = _catalogSettings.CompareProductsEnabled;
        //    //store name
        //    model.CurrentStoreName = _localizationService.GetLocalized(_storeContext.CurrentStore, x => x.Name);

        //    //vendor details
        //    if (_vendorSettings.ShowVendorOnProductDetailsPage)
        //    {
        //        var vendor = _vendorService.GetVendorById(product.VendorId);
        //        if (vendor != null && !vendor.Deleted && vendor.Active)
        //        {
        //            model.ShowVendor = true;

        //            model.VendorModel = new VendorBriefInfoModel
        //            {
        //                Id = vendor.Id,
        //                Name = _localizationService.GetLocalized(vendor, x => x.Name),
        //                SeName = _urlRecordService.GetSeName(vendor),
        //            };
        //        }
        //    }

        //    //page sharing
        //    if (_catalogSettings.ShowShareButton && !string.IsNullOrEmpty(_catalogSettings.PageShareCode))
        //    {
        //        var shareCode = _catalogSettings.PageShareCode;
        //        if (_webHelper.IsCurrentConnectionSecured())
        //        {
        //            //need to change the add this link to be https linked when the page is, so that the page doesn't ask about mixed mode when viewed in https...
        //            shareCode = shareCode.Replace("http://", "https://");
        //        }
        //        model.PageShareCode = shareCode;
        //    }

        //    //back in stock subscriptions
        //    if (product.ManageInventoryMethod == ManageInventoryMethod.ManageStock &&
        //        product.BackorderMode == BackorderMode.NoBackorders &&
        //        product.AllowBackInStockSubscriptions &&
        //        _productService.GetTotalStockQuantity(product) <= 0)
        //    {
        //        //out of stock
        //        model.DisplayBackInStockSubscription = true;
        //    }

        //    //breadcrumb
        //    //do not prepare this model for the associated products. anyway it's not used
        //    if (_catalogSettings.CategoryBreadcrumbEnabled && !isAssociatedProduct)
        //    {
        //        model.Breadcrumb = PrepareProductBreadcrumbModel(product);
        //    }

        //    //product tags
        //    //do not prepare this model for the associated products. anyway it's not used
        //    if (!isAssociatedProduct)
        //    {
        //        model.ProductTags = PrepareProductTagModels(product);
        //    }

        //    //pictures
        //    model.DefaultPictureZoomEnabled = _mediaSettings.DefaultPictureZoomEnabled;
        //    model.DefaultPictureModel = PrepareProductDetailsPictureModel(product, isAssociatedProduct, out IList<PictureModel> allPictureModels);
        //    model.PictureModels = allPictureModels;

        //    //price
        //    model.ProductPrice = PrepareProductPriceModel(product);

        //    //'Add to cart' model
        //    model.AddToCart = PrepareProductAddToCartModel(product, updatecartitem);

        //    //gift card
        //    if (product.IsGiftCard)
        //    {
        //        model.GiftCard.IsGiftCard = true;
        //        model.GiftCard.GiftCardType = product.GiftCardType;

        //        if (updatecartitem == null)
        //        {
        //            model.GiftCard.SenderName = _customerService.GetCustomerFullName(_workContext.CurrentCustomer);
        //            model.GiftCard.SenderEmail = _workContext.CurrentCustomer.Email;
        //        }
        //        else
        //        {
        //            _productAttributeParser.GetGiftCardAttribute(updatecartitem.AttributesXml,
        //                out string giftCardRecipientName, out string giftCardRecipientEmail,
        //                out string giftCardSenderName, out string giftCardSenderEmail, out string giftCardMessage);

        //            model.GiftCard.RecipientName = giftCardRecipientName;
        //            model.GiftCard.RecipientEmail = giftCardRecipientEmail;
        //            model.GiftCard.SenderName = giftCardSenderName;
        //            model.GiftCard.SenderEmail = giftCardSenderEmail;
        //            model.GiftCard.Message = giftCardMessage;
        //        }
        //    }

        //    //product attributes
        //    model.ProductAttributes = PrepareProductAttributeModels(product, updatecartitem);

        //    //product specifications
        //    //do not prepare this model for the associated products. anyway it's not used
        //    if (!isAssociatedProduct)
        //    {
        //        model.ProductSpecifications = PrepareProductSpecificationModel(product);
        //    }

        //    //product review overview
        //    model.ProductReviewOverview = PrepareProductReviewOverviewModel(product);

        //    //tier prices
        //    if (product.HasTierPrices && _permissionService.Authorize(StandardPermissionProvider.DisplayPrices))
        //    {
        //        model.TierPrices = PrepareProductTierPriceModels(product);
        //    }

        //    //manufacturers
        //    //do not prepare this model for the associated products. anyway it's not used
        //    if (!isAssociatedProduct)
        //    {
        //        model.ProductManufacturers = PrepareProductManufacturerModels(product);
        //    }

        //    //rental products
        //    if (product.IsRental)
        //    {
        //        model.IsRental = true;
        //        //set already entered dates attributes (if we're going to update the existing shopping cart item)
        //        if (updatecartitem != null)
        //        {
        //            model.RentalStartDate = updatecartitem.RentalStartDateUtc;
        //            model.RentalEndDate = updatecartitem.RentalEndDateUtc;
        //        }
        //    }

        //    //associated products
        //    if (product.ProductType == ProductType.GroupedProduct)
        //    {
        //        //ensure no circular references
        //        if (!isAssociatedProduct)
        //        {
        //            var associatedProducts = _productService.GetAssociatedProducts(product.Id, _storeContext.CurrentStore.Id);
        //            foreach (var associatedProduct in associatedProducts)
        //                model.AssociatedProducts.Add(PrepareProductDetailsModel(associatedProduct, null, true));
        //        }
        //    }

        //    return model;
        //}


        /// <inheritdoc />
        /// <summary>
        /// See custom code region, rest of function remains unaltered
        /// </summary>
        /// <param name="product">Product</param>
        /// <param name="updatecartitem">Updated shopping cart item</param>
        /// <returns>List of product attribute model</returns>
        protected override IList <ProductDetailsModel.ProductAttributeModel> PrepareProductAttributeModels(Product product, ShoppingCartItem updatecartitem)
        {
            if (product == null)
            {
                throw new ArgumentNullException(nameof(product));
            }

            var model = new List <ProductDetailsModel.ProductAttributeModel>();

            var productAttributeMapping = _productAttributeService.GetProductAttributeMappingsByProductId(product.Id);

            foreach (var attribute in productAttributeMapping)
            {
                var attributeModel = new ProductDetailsModel.ProductAttributeModel
                {
                    Id                   = attribute.Id,
                    ProductId            = product.Id,
                    ProductAttributeId   = attribute.ProductAttributeId,
                    Name                 = _localizationService.GetLocalized(attribute.ProductAttribute, x => x.Name),
                    Description          = _localizationService.GetLocalized(attribute.ProductAttribute, x => x.Description),
                    TextPrompt           = _localizationService.GetLocalized(attribute, x => x.TextPrompt),
                    IsRequired           = attribute.IsRequired,
                    AttributeControlType = attribute.AttributeControlType,
                    DefaultValue         = updatecartitem != null ? null : _localizationService.GetLocalized(attribute, x => x.DefaultValue),
                    HasCondition         = !string.IsNullOrEmpty(attribute.ConditionAttributeXml)
                };
                if (!string.IsNullOrEmpty(attribute.ValidationFileAllowedExtensions))
                {
                    attributeModel.AllowedFileExtensions = attribute.ValidationFileAllowedExtensions
                                                           .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                                                           .ToList();
                }

                if (attribute.ShouldHaveValues())
                {
                    //values
                    var attributeValues = _productAttributeService.GetProductAttributeValues(attribute.Id);

                    #region CUSTOM CODE
                    // Only custom code in function lives here!
                    FilterAttributes(product, ref attributeValues);
                    #endregion

                    foreach (var attributeValue in attributeValues)
                    {
                        var valueModel = new ProductDetailsModel.ProductAttributeValueModel
                        {
                            Id                = attributeValue.Id,
                            Name              = _localizationService.GetLocalized(attributeValue, x => x.Name),
                            ColorSquaresRgb   = attributeValue.ColorSquaresRgb, //used with "Color squares" attribute type
                            IsPreSelected     = attributeValue.IsPreSelected,
                            CustomerEntersQty = attributeValue.CustomerEntersQty,
                            Quantity          = attributeValue.Quantity
                        };
                        attributeModel.Values.Add(valueModel);

                        //display price if allowed
                        if (_permissionService.Authorize(StandardPermissionProvider.DisplayPrices))
                        {
                            var attributeValuePriceAdjustment = _priceCalculationService.GetProductAttributeValuePriceAdjustment(attributeValue, updatecartitem?.Customer ?? _workContext.CurrentCustomer);
                            var priceAdjustmentBase           = _taxService.GetProductPrice(product, attributeValuePriceAdjustment, out decimal _);
                            var priceAdjustment = _currencyService.ConvertFromPrimaryStoreCurrency(priceAdjustmentBase, _workContext.WorkingCurrency);

                            if (attributeValue.PriceAdjustmentUsePercentage)
                            {
                                var priceAdjustmentStr = attributeValue.PriceAdjustment.ToString("G29");
                                if (attributeValue.PriceAdjustment > decimal.Zero)
                                {
                                    valueModel.PriceAdjustment = "+";
                                }
                                valueModel.PriceAdjustment += priceAdjustmentStr + "%";
                            }
                            else
                            {
                                if (priceAdjustmentBase > decimal.Zero)
                                {
                                    valueModel.PriceAdjustment = "+" + _priceFormatter.FormatPrice(priceAdjustment, false, false);
                                }
                                else if (priceAdjustmentBase < decimal.Zero)
                                {
                                    valueModel.PriceAdjustment = "-" + _priceFormatter.FormatPrice(-priceAdjustment, false, false);
                                }
                            }

                            valueModel.PriceAdjustmentValue = priceAdjustment;
                        }

                        //"image square" picture (with with "image squares" attribute type only)
                        if (attributeValue.ImageSquaresPictureId > 0)
                        {
                            var productAttributeImageSquarePictureCacheKey = string.Format(NopModelCacheDefaults.ProductAttributeImageSquarePictureModelKey,
                                                                                           attributeValue.ImageSquaresPictureId,
                                                                                           _webHelper.IsCurrentConnectionSecured(),
                                                                                           _storeContext.CurrentStore.Id);
                            valueModel.ImageSquaresPictureModel = _cacheManager.Get(productAttributeImageSquarePictureCacheKey, () =>
                            {
                                var imageSquaresPicture = _pictureService.GetPictureById(attributeValue.ImageSquaresPictureId);
                                if (imageSquaresPicture != null)
                                {
                                    return(new PictureModel
                                    {
                                        FullSizeImageUrl = _pictureService.GetPictureUrl(imageSquaresPicture),
                                        ImageUrl = _pictureService.GetPictureUrl(imageSquaresPicture, _mediaSettings.ImageSquarePictureSize)
                                    });
                                }
                                return(new PictureModel());
                            });
                        }

                        //picture of a product attribute value
                        valueModel.PictureId = attributeValue.PictureId;
                    }
                }

                //set already selected attributes (if we're going to update the existing shopping cart item)
                if (updatecartitem != null)
                {
                    switch (attribute.AttributeControlType)
                    {
                    case AttributeControlType.DropdownList:
                    case AttributeControlType.RadioList:
                    case AttributeControlType.Checkboxes:
                    case AttributeControlType.ColorSquares:
                    case AttributeControlType.ImageSquares:
                    {
                        if (!string.IsNullOrEmpty(updatecartitem.AttributesXml))
                        {
                            //clear default selection
                            foreach (var item in attributeModel.Values)
                            {
                                item.IsPreSelected = false;
                            }

                            //select new values
                            var selectedValues = _productAttributeParser.ParseProductAttributeValues(updatecartitem.AttributesXml);
                            foreach (var attributeValue in selectedValues)
                            {
                                foreach (var item in attributeModel.Values)
                                {
                                    if (attributeValue.Id == item.Id)
                                    {
                                        item.IsPreSelected = true;

                                        //set customer entered quantity
                                        if (attributeValue.CustomerEntersQty)
                                        {
                                            item.Quantity = attributeValue.Quantity;
                                        }
                                    }
                                }
                            }
                        }
                    }
                    break;

                    case AttributeControlType.ReadonlyCheckboxes:
                    {
                        //values are already pre-set

                        //set customer entered quantity
                        if (!string.IsNullOrEmpty(updatecartitem.AttributesXml))
                        {
                            foreach (var attributeValue in _productAttributeParser.ParseProductAttributeValues(updatecartitem.AttributesXml)
                                     .Where(value => value.CustomerEntersQty))
                            {
                                var item = attributeModel.Values.FirstOrDefault(value => value.Id == attributeValue.Id);
                                if (item != null)
                                {
                                    item.Quantity = attributeValue.Quantity;
                                }
                            }
                        }
                    }
                    break;

                    case AttributeControlType.TextBox:
                    case AttributeControlType.MultilineTextbox:
                    {
                        if (!string.IsNullOrEmpty(updatecartitem.AttributesXml))
                        {
                            var enteredText = _productAttributeParser.ParseValues(updatecartitem.AttributesXml, attribute.Id);
                            if (enteredText.Any())
                            {
                                attributeModel.DefaultValue = enteredText[0];
                            }
                        }
                    }
                    break;

                    case AttributeControlType.Datepicker:
                    {
                        //keep in mind my that the code below works only in the current culture
                        var selectedDateStr = _productAttributeParser.ParseValues(updatecartitem.AttributesXml, attribute.Id);
                        if (selectedDateStr.Any())
                        {
                            if (DateTime.TryParseExact(selectedDateStr[0], "D", CultureInfo.CurrentCulture, DateTimeStyles.None, out DateTime selectedDate))
                            {
                                //successfully parsed
                                attributeModel.SelectedDay   = selectedDate.Day;
                                attributeModel.SelectedMonth = selectedDate.Month;
                                attributeModel.SelectedYear  = selectedDate.Year;
                            }
                        }
                    }
                    break;

                    case AttributeControlType.FileUpload:
                    {
                        if (!string.IsNullOrEmpty(updatecartitem.AttributesXml))
                        {
                            var downloadGuidStr = _productAttributeParser.ParseValues(updatecartitem.AttributesXml, attribute.Id).FirstOrDefault();
                            Guid.TryParse(downloadGuidStr, out Guid downloadGuid);
                            var download = _downloadService.GetDownloadByGuid(downloadGuid);
                            if (download != null)
                            {
                                attributeModel.DefaultValue = download.DownloadGuid.ToString();
                            }
                        }
                    }
                    break;

                    default:
                        break;
                    }
                }

                model.Add(attributeModel);
            }

            return(model);
        }