Error executing template "Designs/Swift/Paragraph/Swift_ProductDetailsGallery.cshtml"
System.ArgumentNullException: Value cannot be null.
Parameter name: source
   at System.Linq.Enumerable.Where[TSource](IEnumerable`1 source, Func`2 predicate)
   at CompiledRazorTemplates.Dynamic.RazorEngine_0f736e6a0c354ea8a4d0a26fccca7b4b.Execute() in D:\dynamicweb.net\Solutions\scanoffice.cloud.dynamicweb-cms.com\Files\Templates\Designs\Swift\Paragraph\Swift_ProductDetailsGallery.cshtml:line 18
   at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader)
   at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer)
   at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template)
   at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template)
   at Dynamicweb.Rendering.Template.RenderRazorTemplate()

1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 @using Dynamicweb.Frontend 4 5 @{ 6 ProductViewModel product = new ProductViewModel(); 7 8 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 9 { 10 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 11 } 12 13 @* Collect the images *@ 14 var selectedImageCategories = Model.Item.GetList("ImageAssets").SelectedValues; 15 bool includeImagePatternImages = Model.Item.GetBoolean("ImagePatternImages"); 16 17 @* Needed image data collection to support both DefaultImage, ImagePatterns and Image Assets *@ 18 IEnumerable<MediaViewModel> assetsImages = product.AssetCategories.Where(x => selectedImageCategories.Contains(x.SystemName)).SelectMany(x => x.Assets); 19 IEnumerable<MediaViewModel> images = new MediaViewModel[]{}; 20 images = includeImagePatternImages && assetsImages.Count() == 0 ? images.Append(product.DefaultImage) : images; 21 images = images.Union(assetsImages); 22 images = includeImagePatternImages ? images.Union(product.ImagePatternImages) : images; 23 24 bool defaultImageFallback = Model.Item.GetBoolean("DefaultImageFallback"); 25 26 int totalImages = 0; 27 foreach (MediaViewModel asset in images) { 28 var assetName = asset.Value.ToLower(); 29 if (assetName.Contains(".jpg") || assetName.Contains(".webp") || assetName.Contains(".png") || assetName.Contains(".gif")) { 30 totalImages++; 31 } 32 } 33 34 if (totalImages == 0) 35 { 36 if (defaultImageFallback) { 37 images = new List<MediaViewModel>(){ product.DefaultImage }; 38 totalImages = 1; 39 } else { 40 images = new List<MediaViewModel>(){ }; 41 totalImages = 0; 42 } 43 } 44 45 @* Layout settings *@ 46 string spacing = Model.Item.GetRawValueString("Spacing", "4"); 47 spacing = spacing == "none" ? "gap-0" : spacing; 48 spacing = spacing == "small" ? "gap-3" : spacing; 49 spacing = spacing == "large" ? "gap-4" : spacing; 50 51 string layout = Model.Item.GetRawValueString("Layout", "grid"); 52 } 53 54 @* Get images from selected categories or get all images *@ 55 @if (totalImages != 0 && images.Count() != 0) 56 { 57 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 58 59 if (layout != "custom-slider") 60 { 61 62 @* Show the gallery on large screens *@ 63 <div class="d-none d-lg-block h-100 @theme"> 64 <div class="grid @spacing"> 65 @{ 66 int imageNumber = 0; 67 68 foreach (MediaViewModel asset in images) 69 { 70 var assetName = asset.Value.ToLower(); 71 72 if (assetName.Contains(".jpg") || assetName.Contains(".webp") || assetName.Contains(".png") || assetName.Contains(".gif")) 73 { 74 string colClass = totalImages > 1 ? "g-col-lg-6" : "g-col-12"; 75 colClass = layout == "full-first" && imageNumber == 0 ? "g-col-12" : colClass; 76 colClass = layout == "full-last" && imageNumber == (totalImages - 1) ? "g-col-12" : colClass; 77 colClass = layout == "advanced-grid" && imageNumber > 1 ? "g-col-4" : colClass; 78 79 colClass = layout == "advanced-grid" && totalImages == 1 ? "g-col-12" : colClass; 80 colClass = layout == "advanced-grid" && totalImages == 3 && imageNumber == 2 ? "g-col-12" : colClass; 81 colClass = layout == "advanced-grid" && totalImages == 4 && imageNumber == 2 ? "g-col-6" : colClass; 82 colClass = layout == "advanced-grid" && totalImages == 4 && imageNumber == 3 ? "g-col-6" : colClass; 83 colClass = layout == "advanced-grid" && totalImages == 6 && imageNumber == 5 ? "g-col-12" : colClass; 84 colClass = layout == "advanced-grid" && totalImages == 7 && imageNumber == 5 ? "g-col-6" : colClass; 85 colClass = layout == "advanced-grid" && totalImages == 7 && imageNumber == 6 ? "g-col-6" : colClass; 86 colClass = layout == "advanced-grid" && totalImages == 9 && imageNumber == 8 ? "g-col-12" : colClass; 87 88 <div class="@colClass"> 89 @RenderImage(asset, product, imageNumber) 90 </div> 91 92 imageNumber++; 93 } 94 } 95 } 96 </div> 97 </div> 98 99 } 100 101 @* Modal *@ 102 <div class="modal fade" id="modal_@Model.ID" tabindex="-1" aria-labelledby="productDetailsGalleryModalTitle" aria-hidden="true"> 103 <div class="modal-dialog modal-dialog-centered modal-xl"> 104 <div class="modal-content"> 105 <div class="modal-header visually-hidden"> 106 <h5 class="modal-title" id="productDetailsGalleryModalTitle">@Translate("Full image")</h5> 107 </div> 108 <div class="modal-body"> 109 <div id="FullImages_@Model.ID" class="d-flex align-items-center justify-content-center"> 110 @foreach (MediaViewModel asset in images) 111 { 112 var assetName = asset.Value.ToLower(); 113 if (assetName.Contains(".jpg") || assetName.Contains(".webp") || assetName.Contains(".png") || assetName.Contains(".gif")) 114 { 115 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 116 var parms = new Dictionary<string, object>(); 117 parms.Add("columns", Model.GridRowColumnCount); 118 parms.Add("cssClass", "mw-100 mh-100"); 119 <span class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 120 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 121 </span> 122 } 123 } 124 </div> 125 <script type="module"> 126 var sliderFull_@(Model.ID) = tns({ 127 container: '#FullImages_@Model.ID', 128 items: 1, 129 gutter: 16, 130 mouseDrag: true, 131 touch: true, 132 arrowKeys: true, 133 nav: false, 134 loop: false, 135 speed: 10, 136 controls: false, 137 swipeAngle: 30, 138 preventScrollOnTouch: 'auto' 139 }); 140 141 if (document.getElementById('modal_@Model.ID')) { 142 document.getElementById('modal_@Model.ID').addEventListener('show.bs.modal', function (event) { 143 var slideNumber = event.relatedTarget.getAttribute("data-image-number"); 144 sliderFull_@(Model.ID).goTo(slideNumber); 145 }); 146 } 147 </script> 148 </div> 149 </div> 150 </div> 151 </div> 152 153 154 155 @* Show the thumbs on small screens *@ 156 string defaultSliderClass = "d-block d-lg-none mini-product-image-slider mx-lg-0" + theme; 157 string customSliderClass = "d-block d-lg-flex flex-lg-row mini-product-image-slider mx-lg-0" + theme; 158 string sliderClass = layout == "custom-slider" ? @customSliderClass : @defaultSliderClass; 159 <div class="@sliderClass"> 160 <div class="custom-slider d-lg-flex flex-lg-column flex-lg-fill border me-lg-2"> 161 <div id="SmallScreenImages_@Model.ID"> 162 @{ 163 var smallImageNumber = 0; 164 165 foreach (MediaViewModel asset in images) 166 { 167 var assetName = asset.Value.ToLower(); 168 169 if (assetName.Contains(".jpg") || assetName.Contains(".webp") || assetName.Contains(".png") || assetName.Contains(".gif")) 170 { 171 @RenderImage(asset, product, smallImageNumber, "small") 172 smallImageNumber++; 173 } 174 } 175 } 176 </div> 177 </div> 178 179 180 @{ 181 if (images.Count() > 1) 182 { 183 <div id="SmallScreenImagesThumbnails_@Model.ID" class="custom-thumbnails tns-nav-thumbnails grid gap-2 my-3 m-lg-0 d-lg-flex flex-lg-column h-100"> 184 @{ 185 foreach (MediaViewModel asset in images) { 186 var assetName = asset.Value.ToLower(); 187 if (assetName.Contains(".jpg") || assetName.Contains(".webp") || assetName.Contains(".png") || assetName.Contains(".gif")) { 188 189 string imagePath = Dynamicweb.Context.Current.Server.UrlEncode(assetName); 190 string imagePathThumb = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=160&format=webp"; 191 192 <div class="tns-nav-thumbnail-item ratio ratio-4x3 border outline-none" style="width:80px;"><img src="@imagePathThumb" class="p-2" style="object-fit: cover;"></div> 193 } 194 } 195 } 196 </div> 197 } 198 } 199 200 @* Tiny slider *@ 201 <script type="module"> 202 var slider_@Model.ID = tns({ 203 container: '#SmallScreenImages_@Model.ID', 204 items: 1, 205 gutter: 16, 206 mouseDrag: true, 207 touch: true, 208 arrowKeys: true, 209 nav: true, 210 navContainer: "#SmallScreenImagesThumbnails_@Model.ID", 211 navAsThumbnails: true, 212 loop: false, 213 controls: false, 214 swipeAngle: 30, 215 preventScrollOnTouch: 'auto', 216 autoHeight: false, 217 }); 218 </script> 219 </div> 220 } 221 222 @helper RenderImage(MediaViewModel asset, ProductViewModel product, int number, string screenSize = "large") { 223 string layout = Model.Item.GetRawValueString("Layout", "grid"); 224 string padding = ""; 225 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 226 227 if (theme != "") 228 { 229 padding = "p-3"; 230 } 231 232 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 233 ratio = ratio != "0" ? ratio : ""; 234 string ratioCssClass = ratio != "" && ratio != "fill" ? " ratio" : ""; 235 string ratioVariable = ratio != "" && ratio != "fill" ? "--bs-aspect-ratio: " + ratio : ""; 236 ratioCssClass = ratio != "" && ratio == "fill" && screenSize == "small" ? " ratio" : ratioCssClass; 237 ratioVariable = ratio != "" && ratio == "fill" && screenSize == "small" ? "--bs-aspect-ratio: 66%" : ratioVariable; 238 string fillClass = ratio == "fill" ? " h-100" : ""; 239 240 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 241 string imageLinkPath = Dynamicweb.Context.Current.Server.UrlEncode(imagePath); 242 string productName = product.Name; 243 244 var parms = new Dictionary<string, object>(); 245 parms.Add("alt", product.Name); 246 parms.Add("itemprop", "image"); 247 parms.Add("columns", Model.GridRowColumnCount); 248 249 if (ratio == "fill" && layout != "grid") { 250 parms.Add("cssClass", "w-100 h-100 image-zoom-lg"); 251 } 252 else 253 { 254 parms.Add("cssClass", "mw-100 mh-100"); 255 } 256 257 258 <div class="h-100 @(padding)@(theme)"> 259 <a href="@imageLinkPath" class="d-block h-100 @(ratioCssClass)@(fillClass)" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID" data-image-number="@number"> 260 <span class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 261 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 262 </span> 263 </a> 264 </div> 265 } 266
Error executing template "Designs/Swift/Paragraph/Swift_ProductDetailsInfo.cshtml"
System.ArgumentNullException: Value cannot be null.
Parameter name: source
   at System.Linq.Enumerable.Where[TSource](IEnumerable`1 source, Func`2 predicate)
   at CompiledRazorTemplates.Dynamic.RazorEngine_1ecbe748485d47ea8fb5ec3fdd273fb6.Execute() in D:\dynamicweb.net\Solutions\scanoffice.cloud.dynamicweb-cms.com\Files\Templates\Designs\Swift\Paragraph\Swift_ProductDetailsInfo.cshtml:line 18
   at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader)
   at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer)
   at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template)
   at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template)
   at Dynamicweb.Rendering.Template.RenderRazorTemplate()

1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 @using Dynamicweb.Ecommerce.Products.FieldDisplayGroups 4 @using Dynamicweb.Frontend 5 @using Dynamicweb.Ecommerce.Products 6 @using ScanOffice.CustomModules 7 @{ 8 bool isVisualEditor = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("VisualEdit")) ? Convert.ToBoolean(Dynamicweb.Context.Current.Request.QueryString.Get("VisualEdit")) : false; 9 bool userIsLoggedIn = Pageview.User != null && Pageview.User.ID > 0 ? true : false; 10 ProductViewModel product = new ProductViewModel(); 11 12 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 13 { 14 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 15 } 16 string ItemAvailabilityIdentifier = string.Join(".", product.Id, Dynamicweb.Ecommerce.Common.Context.LanguageID, "itemAvailableDate"); 17 18 var _customerServiceETA = product.ProductFields.Where(x => x.Key == "CustomerServiceETA").FirstOrDefault().Value.Value; 19 bool customerServiceETA = _customerServiceETA == null ? false : Convert.ToBoolean(_customerServiceETA); 20 21 //SQSCOFECOM-250 - disable add to cart button with custom text message 22 string DisableAddToCartButton = product.ProductFields.Where(x => x.Key == "DisableAddToCartButton").FirstOrDefault().Value.Value.ToString(); 23 24 var _expectedDeliveryDate = product.ProductFields.Where(x => x.Key == "ExpectedDeliveryDate").FirstOrDefault().Value.Value; 25 DateTime expectedDeliveryDate = Convert.ToDateTime(_expectedDeliveryDate); 26 27 var bundleIncludedProducts = product.ProductFields.Single(x => x.Key == "BundleIncludedProducts"); 28 string bipValue = bundleIncludedProducts.Value.Value.ToString(); 29 bool bipViewProduct = string.IsNullOrWhiteSpace(bipValue) || bipValue == " " ? false : true; 30 31 var hideproductfieldValue = product.ProductFields.Single(x => x.Key == "HideProduct").Value.Value.ToString(); 32 bool productNotAvailable = Convert.ToBoolean(hideproductfieldValue); 33 34 string[] variantId = product.VariantId.Split('.'); 35 string disableAddToCart = string.IsNullOrEmpty(product.VariantId) && product.VariantInfo.VariantInfo != null ? "disabled" : ""; 36 string hideStockState = string.IsNullOrEmpty(product.VariantId) && product.VariantInfo.VariantInfo != null ? "d-none" : ""; 37 38 string url = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("CartService")); 39 if (!url.Contains("LayoutTemplate")) 40 { 41 url += url.Contains("?") ? "&LayoutTemplate=Swift_MiniCart.cshtml" : "?LayoutTemplate=Swift_MiniCart.cshtml"; 42 } 43 44 IEnumerable<string> selectedDisplayGroups = Model.Item.GetList("MainFeatures").SelectedValues; 45 List<CategoryFieldViewModel> mainFeatures = new List<CategoryFieldViewModel>(); 46 47 foreach (var selection in selectedDisplayGroups) 48 { 49 50 foreach (CategoryFieldViewModel group in product.FieldDisplayGroups.Values) 51 { 52 53 if (selection == group.Id) 54 { 55 mainFeatures.Add(group); 56 } 57 } 58 } 59 60 if (DisableAddToCartButton == "True") 61 { 62 disableAddToCart = "disabled"; 63 } 64 65 66 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 67 68 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "display-6"); 69 70 string contentPadding = Model.Item.GetRawValueString("ContentPadding", ""); 71 contentPadding = contentPadding == "small" ? "p-2 p-md-3" : contentPadding; 72 contentPadding = contentPadding == "large" ? "p-4 p-md-5" : contentPadding; 73 74 string NoutovarastoTRE = product.ProductFields.Where(x => x.Key == "NoutovarastoTRE").FirstOrDefault().Value.Value.ToString(); 75 } 76 77 <div class="h-100 @(contentPadding) @(theme)"> 78 <div class="d-flex flex-column js-product"> 79 <div> 80 <h4 class="lato-h4" itemprop="name">@product.Name</h4> 81 @if (!Model.Item.GetBoolean("HideProductNumber")) 82 { 83 <div class="open-sans-caption text-color-medium">@product.Number</div> 84 } 85 </div> 86 87 @if (!string.IsNullOrEmpty(product.ShortDescription)) 88 { 89 <div class="mt-3 open-sans-body-one" itemprop="disambiguatingDescription"> 90 @product.ShortDescription 91 </div> 92 } 93 94 @{ 95 bool showWarehouseLocation = false; 96 97 if (Pageview.User != null) 98 { 99 if (Pageview.User.Groups != null) 100 { 101 showWarehouseLocation = Pageview.User.Groups.Any(g => 102 g?.CustomFieldValues?.Any(cfv => 103 cfv?.CustomField?.SystemName == "AccessUser_ShowWarehouseLocation" && cfv?.Value?.ToString().ToLower() == "true" 104 ) == true 105 ); 106 } 107 108 if (!showWarehouseLocation && Pageview.User.CustomFieldValues != null) 109 { 110 showWarehouseLocation = Pageview.User.CustomFieldValues.Any(cfv => 111 cfv?.CustomField?.SystemName == "AccessUser_ShowWarehouseLocation" && cfv?.Value?.ToString().ToLower() == "true" 112 ); 113 } 114 } 115 116 if (showWarehouseLocation) 117 { 118 <div class="lato-h5" itemprop="warehouseLocation"> 119 @NoutovarastoTRE 120 </div> 121 } 122 } 123 124 @if (userIsLoggedIn && !bipViewProduct && !Helpers.IsHiddedPrice()) 125 { 126 <div> 127 <span class="open-sans-caption text-color-medium">@Translate("Price")</span> 128 <div class="h4 mb-0" itemprop="offers" itemscope itemtype="https://schema.org/Offer"> 129 <span itemprop="priceCurrency" content="@product.Price.CurrencyCode" class="d-none"></span> 130 <span itemprop="price" content="@product.Price.Price" class="d-none"></span> 131 <span class="text-price lato-h5 text-color-primary">@product.Price.PriceFormatted</span> 132 </div> 133 @if (product.Price.Price != product.PriceBeforeDiscount.Price) 134 { 135 <div class="text-decoration-line-through opacity-85"> 136 @product.PriceBeforeDiscount.PriceFormatted 137 </div> 138 } 139 </div>} 140 141 @if (mainFeatures.Count > 0) 142 { 143 <div class="grid gap-0"> 144 @foreach (CategoryFieldViewModel mainFeatureGroup in mainFeatures) 145 { 146 foreach (var field in mainFeatureGroup.Fields) 147 { 148 @RenderField(field.Value) 149 } 150 } 151 </div> 152 } 153 154 @if (product.VariantInfo.VariantInfo != null) 155 { 156 int groupNumber = 1; 157 158 <div class="mb-3 js-variant-selector" data-combinations="@string.Join(",", product.VariantCombinations())"> 159 @foreach (var variantGroup in product.VariantGroups()) 160 { 161 VariantGroupViewModel group = variantGroup; 162 163 <h3 class="h6">@group.Name</h3> 164 <div class="mb-3 js-variant-group" data-group-id="@groupNumber"> 165 @foreach (var option in group.Options) 166 { 167 string active = variantId.Contains(option.Id) ? "active" : ""; 168 169 if (!string.IsNullOrEmpty(option.Color)) 170 { 171 <button type="button" class="btn colorbox rounded-circle me-1 mb-2 d-inline-block variant-option js-variant-option @active" style="background-color: @option.Color" onclick="swift.VariantSelector.OptionClick(event)" data-variant-id="@option.Id"></button> 172 } 173 else if (!string.IsNullOrEmpty(option.Color) && !string.IsNullOrEmpty(option.Image.Value)) 174 { 175 <button type="button" class="btn p-0 d-inline-block mb-2 variant-option js-variant-option @active" onclick="swift.VariantSelector.OptionClick(event)" data-variant-id="@option.Id"> 176 <img src="/Admin/Public/GetImage.ashx?image=@(option.Image.Value)&width=42&Format=WebP&Quality=70" /> 177 </button> 178 } 179 else 180 { 181 <button type="button" class="btn btn-secondary d-inline-block mb-2 variant-option js-variant-option @active" onclick="swift.VariantSelector.OptionClick(event)" data-variant-id="@option.Id"> 182 @option.Name 183 </button> 184 } 185 } 186 </div> 187 188 groupNumber++; 189 } 190 </div> 191 } 192 @if (DisableAddToCartButton == "True" && !bipViewProduct) 193 { 194 string DisableAddToCartText = Helpers.GetAreaItemValue("DisableAddToCart"); 195 <div class="d-flex align-items-center"> 196 <small class="text-danger"><span class="open-sans-body-one"><i class="fas fa-circle instock"></i> @DisableAddToCartText</span></small> 197 </div> 198 } 199 else if (!Model.Item.GetBoolean("HideStockState") && userIsLoggedIn && !bipViewProduct) 200 { 201 var ItemStatus = Helpers.ItemAvailabilityStatus(ItemAvailabilityIdentifier, customerServiceETA, expectedDeliveryDate, DisableAddToCartButton); 202 <div class="mt-2 js-stock-state @hideStockState"> 203 <small class="@ItemStatus.Status"><span class="open-sans-body-one me-2"><i class="fas fa-circle instock"></i> @Translate(ItemStatus.statusText)</span></small> 204 </div> 205 } 206 @if (userIsLoggedIn && !bipViewProduct) 207 { 208 if (!productNotAvailable) 209 { 210 <form method="post" action="@url" class="mt-3"> 211 <div class="d-flex flex-column"> 212 <input type="hidden" name="redirect" value="false" /> 213 <input type="hidden" name="ProductId" value="@product.Id" /> 214 <input type="hidden" name="cartcmd" value="add" /> 215 216 @if (!string.IsNullOrEmpty(product.VariantId)) 217 { 218 <input type="hidden" name="VariantId" value="@product.VariantId" /> 219 } 220 221 @if (!Model.Item.GetBoolean("QuantitySelector")) 222 { 223 <div class="flex-fill"> 224 <input id="Quantity_@product.Id" name="Quantity" value="1" type="hidden"> 225 <button type="button" onclick="swift.Cart.Update(event)" class="btn btn-primary js-add-to-cart-button w-100 @disableAddToCart" title="@Translate("Add to cart")" id="AddToCartButton@(product.Id)">@Translate("Add to cart")</button> 226 </div> 227 } 228 else 229 { 230 <div class="flex-fill input-primary-button-group d-flex flex-row mb-2"> 231 <span class="btn btn-primary me-1 @disableAddToCart" onclick="swift.ProductList.UpdateProductQuantity('minus', 'Quantity_@product.Id.Replace("/",@"\\/")', '@product.PurchaseQuantityStep')"><i class="fas fa-minus fa-sm"></i></span> 232 @if (product.PurchaseMinimumQuantity == 0) 233 { 234 <input id="Quantity_@product.Id" name="Quantity" value="1" class="form-control no-arrows mx-2 text-center" type="number" min="1"> 235 } 236 else 237 { 238 <input id="Quantity_@product.Id" name="Quantity" value="@product.PurchaseMinimumQuantity" class="form-control no-arrows mx-2 text-center" type="number" min="@product.PurchaseMinimumQuantity"> 239 240 } 241 <span class="btn btn-primary ms-1 @disableAddToCart" onclick="swift.ProductList.UpdateProductQuantity('plus', 'Quantity_@product.Id.Replace("/",@"\\/")', '@product.PurchaseQuantityStep')"><i class="fas fa-plus fa-sm"></i></span> 242 </div> 243 244 <div class="flex-fill input-group input-primary-button-group d-flex flex-row"> 245 <button type="button" onclick="swift.Cart.Update(event)" class="btn btn-primary flex-fill js-add-to-cart-button @disableAddToCart" title="@Translate("Add to cart")" id="AddToCartButton@(product.Id)">@Translate("Add to cart")</button> 246 </div> 247 } 248 </div> 249 </form> 250 } 251 } 252 </div> 253 </div> 254 255 @helper RenderField(FieldValueViewModel field) 256 { 257 string fieldValue = field?.Value != null ? field.Value.ToString() : ""; 258 bool noValues = false; 259 260 if (!string.IsNullOrEmpty(fieldValue)) 261 { 262 if (field.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 263 { 264 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 265 noValues = values.Count > 0 ? false : true; 266 } 267 } 268 269 if (!string.IsNullOrEmpty(fieldValue) && noValues == false) 270 { 271 <dt class="g-col-12 g-col-sm-4 g-col-lg-12 fw-bold m-0">@field.Name</dt> 272 <dd class="g-col-12 g-col-sm-8 g-col-lg-12 mb-3"> 273 @RenderFieldValue(field) 274 </dd> 275 } 276 } 277 278 @helper RenderFieldValue(FieldValueViewModel field) 279 { 280 string fieldValue = field?.Value != null ? field.Value.ToString() : ""; 281 282 fieldValue = fieldValue == "False" ? Translate("No") : fieldValue; 283 fieldValue = fieldValue == "True" ? Translate("Yes") : fieldValue; 284 285 bool isColor = false; 286 287 if (field.Value.GetType() == typeof(System.Collections.Generic.List<Dynamicweb.Ecommerce.ProductCatalog.FieldOptionValueViewModel>)) 288 { 289 int valueCount = 0; 290 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 291 int totalValues = values.Count; 292 293 foreach (FieldOptionValueViewModel option in values) 294 { 295 if (option.Value.Substring(0, 1) == "#") 296 { 297 isColor = true; 298 } 299 300 if (!isColor) 301 { 302 @option.Name 303 } 304 else 305 { 306 <span class="colorbox-sm" style="background-color: @option.Value" title="@option.Value"></span> 307 } 308 309 if (valueCount != totalValues && valueCount < (totalValues - 1)) 310 { 311 if (isColor) 312 { 313 <text> </text> 314 } 315 else 316 { 317 <text>, </text> 318 } 319 } 320 valueCount++; 321 } 322 } 323 else 324 { 325 if (fieldValue.Substring(0, 1) == "#") 326 { 327 isColor = true; 328 } 329 330 if (!isColor) 331 { 332 @fieldValue 333 } 334 else 335 { 336 <span class="colorbox-sm" style="background-color: @fieldValue" title="@fieldValue"></span> 337 } 338 } 339 } 340 341 @if (product.VariantInfo.VariantInfo != null) 342 { 343 <script> 344 //Initialize the variant selector 345 document.addEventListener('load', function (event) { 346 swift.VariantSelector.init(); 347 }); 348 </script> 349 } 350 351 <script> 352 window.addEventListener('keydown', function (e) { 353 if (e.keyIdentifier == 'U+000A' || e.keyIdentifier == 'Enter' || e.keyCode == 13) { 354 if (e.target.nodeName == 'INPUT' && e.target.getAttribute("type") != "search") { 355 e.preventDefault(); 356 return false; 357 } 358 } 359 }, true); 360 </script> 361
Error executing template "Designs/Swift/Paragraph/Solteq_ProductBundle.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_d432c46e60354b0b9aa99463a788514c.Execute() in D:\dynamicweb.net\Solutions\scanoffice.cloud.dynamicweb-cms.com\Files\Templates\Designs\Swift\Paragraph\Solteq_ProductBundle.cshtml:line 21
   at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader)
   at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer)
   at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template)
   at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template)
   at Dynamicweb.Rendering.Template.RenderRazorTemplate()

1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 @using Dynamicweb.Ecommerce.Products.FieldDisplayGroups 4 @using Dynamicweb.Frontend 5 @using ScanOffice.CustomModules 6 7 @{ 8 bool isVisualEditor = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("VisualEdit")) ? Convert.ToBoolean(Dynamicweb.Context.Current.Request.QueryString.Get("VisualEdit")) : false; 9 bool userIsLoggedIn = Pageview.User != null && Pageview.User.ID > 0 ? true : false; 10 string signInPage = "/Default.aspx?Id=" + GetPageIdByNavigationTag("SignInPage").ToString(); 11 12 string detailsPageLink = Dynamicweb.Context.Current.Items["DetailsPageLink"] != null ? Dynamicweb.Context.Current.Items["DetailsPageLink"].ToString() : ""; 13 ProductViewModel product = new ProductViewModel(); 14 15 string link = "/Default.aspx?ID=" + PageView.Current().ID; 16 17 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 18 { 19 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 20 } 21 string BundleIncludedProducts = !string.IsNullOrEmpty(product.ProductFields.Values.SingleOrDefault(x => x.SystemName == "BundleIncludedProducts").Value.ToString()) 22 ? product.ProductFields.Values.SingleOrDefault(x => x.SystemName == "BundleIncludedProducts").Value.ToString() : ""; 23 string BundleSelectableProducts = !string.IsNullOrEmpty(product.ProductFields.Values.SingleOrDefault(x => x.SystemName == "BundleSelectableProducts").Value.ToString()) 24 ? product.ProductFields.Values.SingleOrDefault(x => x.SystemName == "BundleSelectableProducts").Value.ToString() : ""; 25 26 string BundleSelectableProductsQty = !string.IsNullOrEmpty(product.ProductFields.Values.SingleOrDefault(x => x.SystemName == "BundleSelectableProductsQty").Value.ToString()) 27 ? product.ProductFields.Values.SingleOrDefault(x => x.SystemName == "BundleSelectableProductsQty").Value.ToString() : ""; 28 29 string BundleRecommendedProducts = !string.IsNullOrEmpty(product.ProductFields.Values.SingleOrDefault(x => x.SystemName == "BundleRecommendedProducts").Value.ToString()) 30 ? product.ProductFields.Values.SingleOrDefault(x => x.SystemName == "BundleRecommendedProducts").Value.ToString() : ""; 31 string BundleUsefulProducts = !string.IsNullOrEmpty(product.ProductFields.Values.SingleOrDefault(x => x.SystemName == "BundleUsefulProducts").Value.ToString()) 32 ? product.ProductFields.Values.SingleOrDefault(x => x.SystemName == "BundleUsefulProducts").Value.ToString() : ""; 33 string Title = Translate("Bundle Product"); 34 var guid = Guid.NewGuid().ToString("N").Substring(0,20); 35 //SQSCOFECOM-250 - disable add to cart button with custom text message 36 string DisableAddToCartButton = product.ProductFields.Where(x => x.Key == "DisableAddToCartButton").FirstOrDefault().Value.Value.ToString(); 37 string DisableAddToCartText = Helpers.GetAreaItemValue("DisableAddToCart"); 38 } 39 40 @if (!string.IsNullOrEmpty(BundleIncludedProducts)) { 41 <script id="mainproductdata" type="application/json"> 42 { 43 "ProductID": "@product.Id", 44 "ProductName": "@product.Name", 45 "ProductPrice": "@product.Price.PriceFormatted", 46 "Title" : "@Translate("Bundle Product")", 47 "IsUser": "@userIsLoggedIn", 48 "IsHiddedPrice" : "@Helpers.IsHiddedPrice()", 49 "SignInMessage" : "@Translate("Click here to Sign in")", 50 "AddToCartText": "@Translate("Add to cart")", 51 "CartId":"@GetPageIdByNavigationTag("CartService")", 52 "SignInButtonText": "@Translate("Sign in")", 53 "SignInLink": "@signInPage", 54 "ProductIdentifier": "@guid", 55 "DetailsPageLink": "@link", 56 "BundleIncludedProducts": "@BundleIncludedProducts", 57 "BundleSelectableProducts": "@BundleSelectableProducts", 58 "BundleSelectableProductsQty": "@BundleSelectableProductsQty", 59 "BundleRecommendedProducts": "@BundleRecommendedProducts", 60 "BundleUsefulProducts": "@BundleUsefulProducts", 61 "DisableAddToCartButton": "@DisableAddToCartButton", 62 "DisableAddToCartText": "@DisableAddToCartText" 63 } 64 </script> 65 66 <Product-Bundle></Product-Bundle> 67 } 68
Error executing template "Designs/Swift/Paragraph/Solteq_ProductDataTabs.cshtml"
System.ArgumentNullException: Value cannot be null.
Parameter name: source
   at System.Linq.Enumerable.Where[TSource](IEnumerable`1 source, Func`2 predicate)
   at CompiledRazorTemplates.Dynamic.RazorEngine_abe47ea5035145fb8d6008cc2dfcae7d.Execute() in D:\dynamicweb.net\Solutions\scanoffice.cloud.dynamicweb-cms.com\Files\Templates\Designs\Swift\Paragraph\Solteq_ProductDataTabs.cshtml:line 17
   at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader)
   at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer)
   at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template)
   at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template)
   at Dynamicweb.Rendering.Template.RenderRazorTemplate()

1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 @using Dynamicweb.Frontend 4 @using System.IO 5 @using System.Web 6 @using ScanOffice.CustomModules 7 @{ 8 9 ProductViewModel product = new ProductViewModel(); 10 11 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 12 { 13 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 14 } 15 16 int TabCounter = 1; 17 IEnumerable<MediaViewModel> Documents = product.AssetCategories.Where(x => x.Name == "Documents").SelectMany(x => x.Assets); 18 IEnumerable<MediaViewModel> Videos = product.AssetCategories.Where(x => x.Name == "Videos").SelectMany(x => x.Assets); 19 20 ListViewModel mainTabs = Model.Item.GetList("MainTabs"); 21 IEnumerable<string> selectedDisplayGroupIds = Model.Item.GetList("MainTabs").SelectedValues; 22 List<CategoryFieldViewModel> DataTabs = new List<CategoryFieldViewModel>(); 23 24 foreach (var selection in selectedDisplayGroupIds) 25 { 26 foreach (CategoryFieldViewModel group in product.FieldDisplayGroups.Values) 27 { 28 if (selection == group.Id) 29 { 30 31 bool hasvalue = group.Fields.Any(x => !string.IsNullOrEmpty(x.Value.Value.ToString())); 32 if (group.Name == "Documents") 33 { 34 hasvalue = Documents.Any(x => !string.IsNullOrEmpty(x.Value.ToString())) ? true : false; 35 } 36 else if (group.Name == "Video") 37 { 38 hasvalue = Videos.Any(x => !string.IsNullOrEmpty(x.Value.ToString())) ? true : false; 39 } 40 if (hasvalue) 41 { 42 DataTabs.Add(group); 43 } 44 45 } 46 } 47 } 48 } 49 50 @if (DataTabs.Count > 0) 51 { 52 <!-- Nav tabs --> 53 <ul class="nav nav-pills" id="datatabs" role="tablist"> 54 55 @foreach (var tab in DataTabs) 56 { 57 string tabId = tab.Id.ToLower(); 58 string tabTarget = "#" + tab.Id.ToLower() + "-content"; 59 string tabControl = tab.Id.ToLower(); 60 string tabActive = TabCounter == 1 ? "active" : ""; 61 62 <li class="nav-item" role="presentation"> 63 <button class="nav-link @tabActive lato-h6" id="@tabId" data-bs-toggle="tab" data-bs-target="@tabTarget" type="button" role="tab" aria-controls="@tabControl" aria-selected="true">@Translate(tab.Id.ToString(), tab.Name.ToString())</button> 64 </li> 65 66 if (TabCounter == DataTabs.Count) 67 { 68 TabCounter = 1; 69 } 70 else 71 { 72 TabCounter++; 73 } 74 75 } 76 77 </ul> 78 79 <!-- Tab panes --> 80 <div class="tab-content"> 81 @foreach (var tab in DataTabs) 82 { 83 string tabId = tab.Id.ToLower() + "-content"; 84 string tabTarget = tab.Id.ToLower(); 85 string tabActive = TabCounter == 1 ? "show active" : ""; 86 87 <div class="tab-pane fade @tabActive" id="@tabId" role="tabpanel" aria-labelledby="@tabTarget"> 88 @switch (tab.Name) 89 { 90 case "Description": 91 @renderDescription(product) 92 break; 93 case "Technical Data": 94 @renderTechnicalData(product) 95 break; 96 case "Documents": 97 @renderDocuments(Documents); 98 break; 99 case "Video": 100 @renderVideo(Videos); 101 break; 102 default: 103 break; 104 } 105 </div> 106 107 if (TabCounter == DataTabs.Count) 108 { 109 TabCounter = 1; 110 } 111 else 112 { 113 TabCounter++; 114 } 115 } 116 </div> 117 } 118 119 @helper renderDescription(ProductViewModel product) 120 { 121 <div class="description-tab"> 122 @product.LongDescription 123 </div> 124 } 125 126 @helper renderTechnicalData(ProductViewModel product) 127 { 128 var technical_data = Helpers.GetTechnicalData(product); 129 130 <table id="technical_data_table"> 131 @foreach (var group in technical_data) 132 { 133 var c = group.LongCount() + 1; 134 <tbody> 135 136 <th rowspan="@c">@Translate(group.Key)</th> 137 138 @foreach (var field in group) 139 { 140 <tr> 141 <td>@field.Name</td> 142 <td>@field.Unit</td> 143 <td>@field.Value</td> 144 </tr> 145 } 146 </tbody> 147 } 148 </table> 149 150 } 151 @helper renderDocuments(IEnumerable<MediaViewModel> Documents) 152 { 153 154 bool userIsLoggedIn = Pageview.User != null && Pageview.User.ID > 0 ? true : false; 155 <ul> 156 @foreach (var file in Documents) 157 { 158 string filePath = HttpContext.Current.Server.MapPath(file.Value); 159 if (File.Exists(filePath)) 160 { 161 FileInfo fileInfo = new FileInfo(file.Value); 162 if (userIsLoggedIn && file.DisplayName.ToLower() == "installer") 163 { 164 <li> 165 <a href="/Admin/Public/DWSDownload.aspx?File=@file.Value" alt="@fileInfo.Name"> 166 <i class="fas fa-file-pdf"></i> 167 @fileInfo.Name 168 </a> 169 </li> 170 } 171 else if (file.DisplayName.ToLower() != "installer") 172 { 173 <li> 174 <a href="/Admin/Public/DWSDownload.aspx?File=@file.Value" alt="@fileInfo.Name"> 175 <i class="fas fa-file-pdf"></i> 176 @fileInfo.Name 177 </a> 178 </li> 179 } 180 181 } 182 } 183 </ul> 184 } 185 186 @helper renderVideo(IEnumerable<MediaViewModel> Videos) 187 { 188 <div class="video_div"> 189 @foreach (var i in Videos) 190 { 191 192 if (i.Keywords == "youtube") 193 { 194 var yvsrc = "https://www.youtube.com/embed/" + i.Value.Replace("/Files/", ""); 195 <iframe class="iframe-youtube" src="@yvsrc" title="@i.Name" frameborder="0" allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> 196 } 197 if (i.Keywords == "vimeo") 198 { 199 var vvsrc = "https://player.vimeo.com/video/" + i.Value.Replace("/Files/", ""); 200 <iframe class="iframe-vimeo" src="@vvsrc" frameborder="0" allow="accelerometer; clipboard-write; fullscreen; picture-in-picture" allowfullscreen></iframe> 201 } 202 203 } 204 </div> 205 } 206 207 <style> 208 @@media(max-width: 600px) { 209 #technical_data_table table, thead, tbody, th, td, tr { 210 display: block; 211 } 212 213 #technical_data_table tr { 214 border-bottom: 1px solid #ccc; 215 } 216 217 #technical_data_table td { 218 border: none; 219 border-bottom: 0px solid #eee; 220 position: relative; 221 padding-left: 50%; 222 font-size: small; 223 } 224 225 #technical_data_table td:before { 226 position: absolute; 227 top: 6px; 228 left: 6px; 229 width: 45%; 230 padding-right: 10px; 231 white-space: nowrap; 232 } 233 } 234 235 .iframe-youtube { 236 top: 0; 237 left: 0; 238 bottom: 0; 239 right: 0; 240 height: 230px; 241 margin: 1em; 242 min-width: 400px; 243 } 244 245 .iframe-vimeo { 246 top: 0; 247 left: 0; 248 bottom: 0; 249 right: 0; 250 height: 230px; 251 margin: 1em; 252 min-width: 400px; 253 } 254 255 .video_div { 256 width: 100%; 257 height: 100%; 258 align-items: center; 259 justify-content: center; 260 padding-left: 2em; 261 } 262 263 @@media(max-width: 600px) { 264 .iframe-youtube { 265 top: 0; 266 left: 0; 267 bottom: 0; 268 right: 0; 269 width: 100%; 270 height: 100%; 271 margin: 0em; 272 min-width: 0; 273 } 274 275 .iframe-vimeo { 276 top: 0; 277 left: 0; 278 bottom: 0; 279 right: 0; 280 width: 100%; 281 height: 100%; 282 margin: 0em; 283 min-width: 0; 284 } 285 286 .video_div { 287 width: 100%; 288 height: 100%; 289 align-items: center; 290 justify-content: center; 291 margin: 0; 292 padding-left: 0; 293 } 294 } 295 </style> 296
Error executing template "Designs/Swift/Paragraph/Swift_ProductDetailsGallery.cshtml"
System.ArgumentNullException: Value cannot be null.
Parameter name: source
   at System.Linq.Enumerable.Where[TSource](IEnumerable`1 source, Func`2 predicate)
   at CompiledRazorTemplates.Dynamic.RazorEngine_0f736e6a0c354ea8a4d0a26fccca7b4b.Execute() in D:\dynamicweb.net\Solutions\scanoffice.cloud.dynamicweb-cms.com\Files\Templates\Designs\Swift\Paragraph\Swift_ProductDetailsGallery.cshtml:line 18
   at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader)
   at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer)
   at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template)
   at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template)
   at Dynamicweb.Rendering.Template.RenderRazorTemplate()

1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 @using Dynamicweb.Frontend 4 5 @{ 6 ProductViewModel product = new ProductViewModel(); 7 8 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 9 { 10 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 11 } 12 13 @* Collect the images *@ 14 var selectedImageCategories = Model.Item.GetList("ImageAssets").SelectedValues; 15 bool includeImagePatternImages = Model.Item.GetBoolean("ImagePatternImages"); 16 17 @* Needed image data collection to support both DefaultImage, ImagePatterns and Image Assets *@ 18 IEnumerable<MediaViewModel> assetsImages = product.AssetCategories.Where(x => selectedImageCategories.Contains(x.SystemName)).SelectMany(x => x.Assets); 19 IEnumerable<MediaViewModel> images = new MediaViewModel[]{}; 20 images = includeImagePatternImages && assetsImages.Count() == 0 ? images.Append(product.DefaultImage) : images; 21 images = images.Union(assetsImages); 22 images = includeImagePatternImages ? images.Union(product.ImagePatternImages) : images; 23 24 bool defaultImageFallback = Model.Item.GetBoolean("DefaultImageFallback"); 25 26 int totalImages = 0; 27 foreach (MediaViewModel asset in images) { 28 var assetName = asset.Value.ToLower(); 29 if (assetName.Contains(".jpg") || assetName.Contains(".webp") || assetName.Contains(".png") || assetName.Contains(".gif")) { 30 totalImages++; 31 } 32 } 33 34 if (totalImages == 0) 35 { 36 if (defaultImageFallback) { 37 images = new List<MediaViewModel>(){ product.DefaultImage }; 38 totalImages = 1; 39 } else { 40 images = new List<MediaViewModel>(){ }; 41 totalImages = 0; 42 } 43 } 44 45 @* Layout settings *@ 46 string spacing = Model.Item.GetRawValueString("Spacing", "4"); 47 spacing = spacing == "none" ? "gap-0" : spacing; 48 spacing = spacing == "small" ? "gap-3" : spacing; 49 spacing = spacing == "large" ? "gap-4" : spacing; 50 51 string layout = Model.Item.GetRawValueString("Layout", "grid"); 52 } 53 54 @* Get images from selected categories or get all images *@ 55 @if (totalImages != 0 && images.Count() != 0) 56 { 57 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 58 59 if (layout != "custom-slider") 60 { 61 62 @* Show the gallery on large screens *@ 63 <div class="d-none d-lg-block h-100 @theme"> 64 <div class="grid @spacing"> 65 @{ 66 int imageNumber = 0; 67 68 foreach (MediaViewModel asset in images) 69 { 70 var assetName = asset.Value.ToLower(); 71 72 if (assetName.Contains(".jpg") || assetName.Contains(".webp") || assetName.Contains(".png") || assetName.Contains(".gif")) 73 { 74 string colClass = totalImages > 1 ? "g-col-lg-6" : "g-col-12"; 75 colClass = layout == "full-first" && imageNumber == 0 ? "g-col-12" : colClass; 76 colClass = layout == "full-last" && imageNumber == (totalImages - 1) ? "g-col-12" : colClass; 77 colClass = layout == "advanced-grid" && imageNumber > 1 ? "g-col-4" : colClass; 78 79 colClass = layout == "advanced-grid" && totalImages == 1 ? "g-col-12" : colClass; 80 colClass = layout == "advanced-grid" && totalImages == 3 && imageNumber == 2 ? "g-col-12" : colClass; 81 colClass = layout == "advanced-grid" && totalImages == 4 && imageNumber == 2 ? "g-col-6" : colClass; 82 colClass = layout == "advanced-grid" && totalImages == 4 && imageNumber == 3 ? "g-col-6" : colClass; 83 colClass = layout == "advanced-grid" && totalImages == 6 && imageNumber == 5 ? "g-col-12" : colClass; 84 colClass = layout == "advanced-grid" && totalImages == 7 && imageNumber == 5 ? "g-col-6" : colClass; 85 colClass = layout == "advanced-grid" && totalImages == 7 && imageNumber == 6 ? "g-col-6" : colClass; 86 colClass = layout == "advanced-grid" && totalImages == 9 && imageNumber == 8 ? "g-col-12" : colClass; 87 88 <div class="@colClass"> 89 @RenderImage(asset, product, imageNumber) 90 </div> 91 92 imageNumber++; 93 } 94 } 95 } 96 </div> 97 </div> 98 99 } 100 101 @* Modal *@ 102 <div class="modal fade" id="modal_@Model.ID" tabindex="-1" aria-labelledby="productDetailsGalleryModalTitle" aria-hidden="true"> 103 <div class="modal-dialog modal-dialog-centered modal-xl"> 104 <div class="modal-content"> 105 <div class="modal-header visually-hidden"> 106 <h5 class="modal-title" id="productDetailsGalleryModalTitle">@Translate("Full image")</h5> 107 </div> 108 <div class="modal-body"> 109 <div id="FullImages_@Model.ID" class="d-flex align-items-center justify-content-center"> 110 @foreach (MediaViewModel asset in images) 111 { 112 var assetName = asset.Value.ToLower(); 113 if (assetName.Contains(".jpg") || assetName.Contains(".webp") || assetName.Contains(".png") || assetName.Contains(".gif")) 114 { 115 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 116 var parms = new Dictionary<string, object>(); 117 parms.Add("columns", Model.GridRowColumnCount); 118 parms.Add("cssClass", "mw-100 mh-100"); 119 <span class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 120 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 121 </span> 122 } 123 } 124 </div> 125 <script type="module"> 126 var sliderFull_@(Model.ID) = tns({ 127 container: '#FullImages_@Model.ID', 128 items: 1, 129 gutter: 16, 130 mouseDrag: true, 131 touch: true, 132 arrowKeys: true, 133 nav: false, 134 loop: false, 135 speed: 10, 136 controls: false, 137 swipeAngle: 30, 138 preventScrollOnTouch: 'auto' 139 }); 140 141 if (document.getElementById('modal_@Model.ID')) { 142 document.getElementById('modal_@Model.ID').addEventListener('show.bs.modal', function (event) { 143 var slideNumber = event.relatedTarget.getAttribute("data-image-number"); 144 sliderFull_@(Model.ID).goTo(slideNumber); 145 }); 146 } 147 </script> 148 </div> 149 </div> 150 </div> 151 </div> 152 153 154 155 @* Show the thumbs on small screens *@ 156 string defaultSliderClass = "d-block d-lg-none mini-product-image-slider mx-lg-0" + theme; 157 string customSliderClass = "d-block d-lg-flex flex-lg-row mini-product-image-slider mx-lg-0" + theme; 158 string sliderClass = layout == "custom-slider" ? @customSliderClass : @defaultSliderClass; 159 <div class="@sliderClass"> 160 <div class="custom-slider d-lg-flex flex-lg-column flex-lg-fill border me-lg-2"> 161 <div id="SmallScreenImages_@Model.ID"> 162 @{ 163 var smallImageNumber = 0; 164 165 foreach (MediaViewModel asset in images) 166 { 167 var assetName = asset.Value.ToLower(); 168 169 if (assetName.Contains(".jpg") || assetName.Contains(".webp") || assetName.Contains(".png") || assetName.Contains(".gif")) 170 { 171 @RenderImage(asset, product, smallImageNumber, "small") 172 smallImageNumber++; 173 } 174 } 175 } 176 </div> 177 </div> 178 179 180 @{ 181 if (images.Count() > 1) 182 { 183 <div id="SmallScreenImagesThumbnails_@Model.ID" class="custom-thumbnails tns-nav-thumbnails grid gap-2 my-3 m-lg-0 d-lg-flex flex-lg-column h-100"> 184 @{ 185 foreach (MediaViewModel asset in images) { 186 var assetName = asset.Value.ToLower(); 187 if (assetName.Contains(".jpg") || assetName.Contains(".webp") || assetName.Contains(".png") || assetName.Contains(".gif")) { 188 189 string imagePath = Dynamicweb.Context.Current.Server.UrlEncode(assetName); 190 string imagePathThumb = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=160&format=webp"; 191 192 <div class="tns-nav-thumbnail-item ratio ratio-4x3 border outline-none" style="width:80px;"><img src="@imagePathThumb" class="p-2" style="object-fit: cover;"></div> 193 } 194 } 195 } 196 </div> 197 } 198 } 199 200 @* Tiny slider *@ 201 <script type="module"> 202 var slider_@Model.ID = tns({ 203 container: '#SmallScreenImages_@Model.ID', 204 items: 1, 205 gutter: 16, 206 mouseDrag: true, 207 touch: true, 208 arrowKeys: true, 209 nav: true, 210 navContainer: "#SmallScreenImagesThumbnails_@Model.ID", 211 navAsThumbnails: true, 212 loop: false, 213 controls: false, 214 swipeAngle: 30, 215 preventScrollOnTouch: 'auto', 216 autoHeight: false, 217 }); 218 </script> 219 </div> 220 } 221 222 @helper RenderImage(MediaViewModel asset, ProductViewModel product, int number, string screenSize = "large") { 223 string layout = Model.Item.GetRawValueString("Layout", "grid"); 224 string padding = ""; 225 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 226 227 if (theme != "") 228 { 229 padding = "p-3"; 230 } 231 232 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 233 ratio = ratio != "0" ? ratio : ""; 234 string ratioCssClass = ratio != "" && ratio != "fill" ? " ratio" : ""; 235 string ratioVariable = ratio != "" && ratio != "fill" ? "--bs-aspect-ratio: " + ratio : ""; 236 ratioCssClass = ratio != "" && ratio == "fill" && screenSize == "small" ? " ratio" : ratioCssClass; 237 ratioVariable = ratio != "" && ratio == "fill" && screenSize == "small" ? "--bs-aspect-ratio: 66%" : ratioVariable; 238 string fillClass = ratio == "fill" ? " h-100" : ""; 239 240 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 241 string imageLinkPath = Dynamicweb.Context.Current.Server.UrlEncode(imagePath); 242 string productName = product.Name; 243 244 var parms = new Dictionary<string, object>(); 245 parms.Add("alt", product.Name); 246 parms.Add("itemprop", "image"); 247 parms.Add("columns", Model.GridRowColumnCount); 248 249 if (ratio == "fill" && layout != "grid") { 250 parms.Add("cssClass", "w-100 h-100 image-zoom-lg"); 251 } 252 else 253 { 254 parms.Add("cssClass", "mw-100 mh-100"); 255 } 256 257 258 <div class="h-100 @(padding)@(theme)"> 259 <a href="@imageLinkPath" class="d-block h-100 @(ratioCssClass)@(fillClass)" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID" data-image-number="@number"> 260 <span class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 261 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 262 </span> 263 </a> 264 </div> 265 } 266