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_fb12c6d11a4d40bbbb299ca71b6d84fb.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_a53efd015383437497b9721803d77176.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() ?? string.Empty; 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 @if (mainFeatures.Count > 0) 95 { 96 <div class="grid gap-0"> 97 @foreach (CategoryFieldViewModel mainFeatureGroup in mainFeatures) 98 { 99 foreach (var field in mainFeatureGroup.Fields) 100 { 101 @RenderField(field.Value) 102 } 103 } 104 </div> 105 } 106 107 @if (product.VariantInfo.VariantInfo != null) 108 { 109 int groupNumber = 1; 110 111 <div class="mb-3 js-variant-selector" data-combinations="@string.Join(",", product.VariantCombinations())"> 112 @foreach (var variantGroup in product.VariantGroups()) 113 { 114 VariantGroupViewModel group = variantGroup; 115 116 <h3 class="h6">@group.Name</h3> 117 <div class="mb-3 js-variant-group" data-group-id="@groupNumber"> 118 @foreach (var option in group.Options) 119 { 120 string active = variantId.Contains(option.Id) ? "active" : ""; 121 122 if (!string.IsNullOrEmpty(option.Color)) 123 { 124 <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> 125 } 126 else if (!string.IsNullOrEmpty(option.Color) && !string.IsNullOrEmpty(option.Image.Value)) 127 { 128 <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"> 129 <img src="/Admin/Public/GetImage.ashx?image=@(option.Image.Value)&width=42&Format=WebP&Quality=70" /> 130 </button> 131 } 132 else 133 { 134 <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"> 135 @option.Name 136 </button> 137 } 138 } 139 </div> 140 141 groupNumber++; 142 } 143 </div> 144 } 145 @if (DisableAddToCartButton == "True" && !bipViewProduct) 146 { 147 string DisableAddToCartText = Helpers.GetAreaItemValue("DisableAddToCart"); 148 <div class="mt-2 js-stock-state open-sans-body-one row"> 149 <div class="col-12 col-sm-auto d-flex align-items-center pr-0"> 150 <i class="fas fa-circle me-1 instock text-danger"></i> 151 <span class="fw-bold me-1 text-danger">Verkkokauppa:</span> 152 </div> 153 <div class="col-12 col-sm pl-xxl-0"> 154 <span>@DisableAddToCartText</span> 155 </div> 156 </div> 157 } 158 else if (!Model.Item.GetBoolean("HideStockState") && userIsLoggedIn && !bipViewProduct) 159 { 160 var ItemStatus = Helpers.ItemAvailabilityStatus(ItemAvailabilityIdentifier, customerServiceETA, expectedDeliveryDate, DisableAddToCartButton); 161 <div class="mt-2 js-stock-state open-sans-body-one @hideStockState"> 162 <i class="fas fa-circle me-1 instock @ItemStatus.Status"></i><span class="fw-bold me-1 @ItemStatus.Status">Verkkokauppa:</span><span>@Translate(ItemStatus.statusText)</span> 163 </div> 164 } 165 166 @{ 167 bool showWarehouseLocation = false; 168 169 if (Pageview.User != null) 170 { 171 if (Pageview.User.Groups != null) 172 { 173 showWarehouseLocation = Pageview.User.Groups.Any(g => 174 g?.CustomFieldValues?.Any(cfv => 175 cfv?.CustomField?.SystemName == "AccessUser_ShowWarehouseLocation" && cfv?.Value?.ToString().ToLower() == "true" 176 ) == true 177 ); 178 } 179 180 if (!showWarehouseLocation && Pageview.User.CustomFieldValues != null) 181 { 182 showWarehouseLocation = Pageview.User.CustomFieldValues.Any(cfv => 183 cfv?.CustomField?.SystemName == "AccessUser_ShowWarehouseLocation" && cfv?.Value?.ToString().ToLower() == "true" 184 ); 185 } 186 } 187 188 if (showWarehouseLocation) 189 { 190 @*<div class="lato-h5" itemprop="warehouseLocation"> 191 @NoutovarastoTRE 192 </div>*@ 193 string NOUTOTRE = product.ProductFields.Where(x => x.Key == "NOUTOTRE").FirstOrDefault().Value.Value.ToString(); 194 string noutotreLabel = "Noutovarasto Tampere:"; 195 string noutotreStyle = ""; 196 string noutotreMessage = ""; 197 var noutotreParsed = 0; 198 199 if (string.IsNullOrEmpty(NoutovarastoTRE)) 200 { 201 noutotreLabel = $@"Noutovarasto Tampere:"; 202 noutotreMessage = Translate("Not in catalogue"); 203 noutotreStyle = "font-grey"; 204 } 205 else if (!string.IsNullOrEmpty(NOUTOTRE)) 206 { 207 noutotreParsed = int.Parse(NOUTOTRE); 208 noutotreMessage = noutotreParsed <= 0 ? Translate("Out of Stock") : $@"{Translate("In Shelf")} {noutotreParsed} {Translate("pcs")} </br> {NoutovarastoTRE} "; 209 noutotreStyle = noutotreParsed <= 0 ? "text-danger" : "text-success"; 210 } 211 212 <div class="mt-2 js-stock-state open-sans-body-one d-flex flex-nowrap align-items-start @hideStockState"> 213 <i class="fas fa-circle me-1 mt-1 instock @noutotreStyle"></i> 214 <div class="d-flex @(noutotreParsed > 0 ? " flex-column" : "")"> 215 <span class="fw-bold me-1 @noutotreStyle">@noutotreLabel</span><span class="@(string.IsNullOrEmpty(NoutovarastoTRE) ? noutotreStyle : "")">@noutotreMessage</span> 216 </div> 217 </div> 218 } 219 } 220 221 @if (userIsLoggedIn && !bipViewProduct && !Helpers.IsHiddedPrice()) 222 { 223 <div> 224 <span class="open-sans-caption text-color-medium">@Translate("Price")</span> 225 <div class="h4 mb-0" itemprop="offers" itemscope itemtype="https://schema.org/Offer"> 226 <span itemprop="priceCurrency" content="@product.Price.CurrencyCode" class="d-none"></span> 227 <span itemprop="price" content="@product.Price.Price" class="d-none"></span> 228 <span class="text-price lato-h5 text-color-primary">@product.Price.PriceFormatted</span> 229 </div> 230 @if (product.Price.Price != product.PriceBeforeDiscount.Price) 231 { 232 <div class="text-decoration-line-through opacity-85"> 233 @product.PriceBeforeDiscount.PriceFormatted 234 </div> 235 } 236 </div> 237 } 238 239 @if (userIsLoggedIn && !bipViewProduct) 240 { 241 if (!productNotAvailable) 242 { 243 <form method="post" action="@url" class="mt-3"> 244 <div class="d-flex flex-column"> 245 <input type="hidden" name="redirect" value="false" /> 246 <input type="hidden" name="ProductId" value="@product.Id" /> 247 <input type="hidden" name="cartcmd" value="add" /> 248 249 @if (!string.IsNullOrEmpty(product.VariantId)) 250 { 251 <input type="hidden" name="VariantId" value="@product.VariantId" /> 252 } 253 254 @if (!Model.Item.GetBoolean("QuantitySelector")) 255 { 256 <div class="flex-fill"> 257 <input id="Quantity_@product.Id" name="Quantity" value="1" type="hidden"> 258 <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> 259 </div> 260 } 261 else 262 { 263 <div class="flex-fill input-primary-button-group d-flex flex-row mb-2"> 264 <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> 265 @if (product.PurchaseMinimumQuantity == 0) 266 { 267 <input id="Quantity_@product.Id" name="Quantity" value="1" class="form-control no-arrows mx-2 text-center" type="number" min="1"> 268 } 269 else 270 { 271 <input id="Quantity_@product.Id" name="Quantity" value="@product.PurchaseMinimumQuantity" class="form-control no-arrows mx-2 text-center" type="number" min="@product.PurchaseMinimumQuantity"> 272 273 } 274 <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> 275 </div> 276 277 <div class="flex-fill input-group input-primary-button-group d-flex flex-row"> 278 <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> 279 </div> 280 } 281 </div> 282 </form> 283 } 284 } 285 </div> 286 </div> 287 288 @helper RenderField(FieldValueViewModel field) 289 { 290 string fieldValue = field?.Value != null ? field.Value.ToString() : ""; 291 bool noValues = false; 292 293 if (!string.IsNullOrEmpty(fieldValue)) 294 { 295 if (field.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 296 { 297 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 298 noValues = values.Count > 0 ? false : true; 299 } 300 } 301 302 if (!string.IsNullOrEmpty(fieldValue) && noValues == false) 303 { 304 <dt class="g-col-12 g-col-sm-4 g-col-lg-12 fw-bold m-0">@field.Name</dt> 305 <dd class="g-col-12 g-col-sm-8 g-col-lg-12 mb-3"> 306 @RenderFieldValue(field) 307 </dd> 308 } 309 } 310 311 @helper RenderFieldValue(FieldValueViewModel field) 312 { 313 string fieldValue = field?.Value != null ? field.Value.ToString() : ""; 314 315 fieldValue = fieldValue == "False" ? Translate("No") : fieldValue; 316 fieldValue = fieldValue == "True" ? Translate("Yes") : fieldValue; 317 318 bool isColor = false; 319 320 if (field.Value.GetType() == typeof(System.Collections.Generic.List<Dynamicweb.Ecommerce.ProductCatalog.FieldOptionValueViewModel>)) 321 { 322 int valueCount = 0; 323 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 324 int totalValues = values.Count; 325 326 foreach (FieldOptionValueViewModel option in values) 327 { 328 if (option.Value.Substring(0, 1) == "#") 329 { 330 isColor = true; 331 } 332 333 if (!isColor) 334 { 335 @option.Name 336 } 337 else 338 { 339 <span class="colorbox-sm" style="background-color: @option.Value" title="@option.Value"></span> 340 } 341 342 if (valueCount != totalValues && valueCount < (totalValues - 1)) 343 { 344 if (isColor) 345 { 346 <text> </text> 347 } 348 else 349 { 350 <text>, </text> 351 } 352 } 353 valueCount++; 354 } 355 } 356 else 357 { 358 if (fieldValue.Substring(0, 1) == "#") 359 { 360 isColor = true; 361 } 362 363 if (!isColor) 364 { 365 @fieldValue 366 } 367 else 368 { 369 <span class="colorbox-sm" style="background-color: @fieldValue" title="@fieldValue"></span> 370 } 371 } 372 } 373 374 @if (product.VariantInfo.VariantInfo != null) 375 { 376 <script> 377 //Initialize the variant selector 378 document.addEventListener('load', function (event) { 379 swift.VariantSelector.init(); 380 }); 381 </script> 382 } 383 384 <script> 385 window.addEventListener('keydown', function (e) { 386 if (e.keyIdentifier == 'U+000A' || e.keyIdentifier == 'Enter' || e.keyCode == 13) { 387 if (e.target.nodeName == 'INPUT' && e.target.getAttribute("type") != "search") { 388 e.preventDefault(); 389 return false; 390 } 391 } 392 }, true); 393 </script> 394
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_18cd452866c240169fb37393bf1d515e.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_e71bb98c10db4b5397a0a1d4b45d674b.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_fb12c6d11a4d40bbbb299ca71b6d84fb.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_RelatedProducts.cshtml"
System.ArgumentNullException: Value cannot be null.
Parameter name: source
   at System.Linq.Enumerable.SelectMany[TSource,TResult](IEnumerable`1 source, Func`2 selector)
   at Dynamicweb.Ecommerce.ProductCatalog.ProductViewModelExtensions.GetRelatedProducts(ProductViewModel product)
   at CompiledRazorTemplates.Dynamic.RazorEngine_3d2d2714881b4b3885728ee6592b1f6e.Execute() in D:\dynamicweb.net\Solutions\scanoffice.cloud.dynamicweb-cms.com\Files\Templates\Designs\Swift\Paragraph\Swift_RelatedProducts.cshtml:line 87
   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 4 @{ 5 bool isVisualEditor = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("VisualEdit")) ? Convert.ToBoolean(Dynamicweb.Context.Current.Request.QueryString.Get("VisualEdit")) : false; 6 7 bool productViewModelFound = false; 8 ProductViewModel product = new ProductViewModel(); 9 10 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 11 { 12 productViewModelFound = true; 13 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 14 } 15 16 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "h3"); 17 string subtitleFontSize = Model.Item.GetRawValueString("SubtitleFontSize", "fs-5"); 18 19 string buttonStyle = Model.Item.GetRawValueString("ButtonStyle", ""); 20 buttonStyle = buttonStyle == "primary" ? " btn-primary" : buttonStyle; 21 buttonStyle = buttonStyle == "secondary" ? " btn-secondary" : buttonStyle; 22 23 string generalTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("GeneralTheme")) ? " theme " + Model.Item.GetRawValueString("GeneralTheme").Replace(" ", "").Trim().ToLower() : ""; 24 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 25 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 26 27 string detailPageId = Model.Item.GetLink("ProductDetailsPage") != null ? Model.Item.GetLink("ProductDetailsPage").PageId.ToString() : ""; 28 string productPageByNavigationTag = GetPageIdByNavigationTag("ProductDetailPage") != 0 ? GetPageIdByNavigationTag("ProductDetailPage").ToString() : ""; 29 detailPageId = detailPageId == "" ? productPageByNavigationTag : detailPageId; 30 31 string pageId = Model.Item.GetLink("ProductSliderServicePage") != null ? Model.Item.GetLink("ProductSliderServicePage").PageId.ToString() : ""; 32 string servicePageByNavigationTag = GetPageIdByNavigationTag("ProductSliderService") != 0 ? GetPageIdByNavigationTag("ProductSliderService").ToString() : ""; 33 pageId = pageId == "" ? servicePageByNavigationTag : pageId; 34 35 string url = "/Default.aspx?ID=" + pageId; 36 if (!url.Contains("LayoutTemplate")) { 37 url += url.Contains("?") ? "&LayoutTemplate=Designs/Swift/Swift_PageClean.cshtml" : "?LayoutTemplate=Designs/Swift/Swift_PageClean.cshtml"; 38 } 39 if (isVisualEditor) { 40 url += "&VisualEdit=True"; 41 } 42 43 string relationType = Model.Item.GetRawValueString("RelationType", "trending"); 44 45 //If products is added through the trending groups selector 46 IList<ProductGroupViewModel> groupsToRelateToTrending = Model.Item.GetValue<IList<ProductGroupViewModel>>("GroupsToRelateToTrending"); 47 IList<ProductGroupViewModel> groupsToRelateToMostSold = Model.Item.GetValue<IList<ProductGroupViewModel>>("GroupsToRelateToMostSold"); 48 IList<string> relateFromGroupIds = new List<string>{}; 49 50 if (groupsToRelateToTrending != null && relationType == "trending") { 51 foreach (var fromGroup in groupsToRelateToTrending) 52 { 53 relateFromGroupIds.Add(fromGroup.Id); 54 } 55 } 56 57 if (groupsToRelateToMostSold != null && relationType == "most-sold") { 58 foreach (var fromGroup in groupsToRelateToMostSold) 59 { 60 relateFromGroupIds.Add(fromGroup.Id); 61 } 62 } 63 64 //If products is added through the selector 65 ProductListViewModel productsToRelateTo = Model.Item.GetValue<ProductListViewModel>("ProductsToRelateTo"); 66 IList<string> relateFromProductIds = new List<string>{}; 67 68 if (productsToRelateTo != null) { 69 foreach (var fromProduct in productsToRelateTo.Products) 70 { 71 relateFromProductIds.Add(fromProduct.Id); 72 } 73 } 74 75 ProductListViewModel products = Model.Item.GetValue<ProductListViewModel>("Products"); 76 IList<string> selectedProductIds = new List<string>{}; 77 78 if (products != null) { 79 foreach (var productSelection in products.Products) 80 { 81 selectedProductIds.Add(productSelection.Id); 82 } 83 } 84 85 if (relationType == "selected" && products == null) 86 { 87 foreach(var pro in product.GetRelatedProducts()) 88 { 89 selectedProductIds.Add(pro.Id); 90 } 91 } 92 93 string groupIds = productViewModelFound ? product.PrimaryOrDefaultGroup.Id : string.Join(",", relateFromGroupIds); 94 string productIds = productViewModelFound ? product.Id : string.Join(",", relateFromProductIds); 95 string title = Model?.Item?.GetString("Title") != null ? Model.Item.GetString("Title") : Translate("Products"); 96 97 string linkParameters = ""; 98 linkParameters += "&GroupId=" + groupIds; 99 linkParameters += !string.IsNullOrEmpty(productIds) ? "&MainProductId=" + productIds : ""; 100 linkParameters += selectedProductIds.Count > 0 ? "&MainProductId=" + string.Join(",", selectedProductIds) : ""; 101 string productListPageId = Model.Item.GetLink("ProductListPage") != null ? Model.Item.GetLink("ProductListPage").PageId.ToString() : ""; 102 string productListPageByNavigationTag = GetPageIdByNavigationTag("Shop") != 0 ? GetPageIdByNavigationTag("Shop").ToString() : ""; 103 productListPageId = productListPageId == "" ? productListPageByNavigationTag : productListPageId; 104 string link = "/Default.aspx?ID=" + productListPageId + linkParameters; 105 } 106 107 <form method="post" action="@url" data-response-target-element="RelatedProducts_@Model.ID" data-preloader="inline" data-update-url="false" class="w-100 h-100 js-product-list"> 108 <input type="hidden" name="HideTitle" value="@Model.Item.GetString("HideTitle")" /> 109 @if (Model.Item.GetInt32("ProductsCount") != 0) 110 { 111 <input type="hidden" name="PageSize" value="@Model.Item.GetInt32("ProductsCount")" /> 112 } 113 @if (detailPageId != "") 114 { 115 <input type="hidden" name="ProductDetailsPage" value="@detailPageId" /> 116 } 117 @if (!Model.Item.GetBoolean("HideTitle")) 118 { 119 <input type="hidden" name="HeadingTitle" value="@title" /> 120 } 121 @if (!string.IsNullOrEmpty(Model.Item.GetString("Subtitle"))) 122 { 123 <input type="hidden" name="Subtitle" value="@Model.Item.GetString("Subtitle")" /> 124 } 125 126 <input type="hidden" name="Link" value="@link" /> 127 128 @if (!string.IsNullOrEmpty(Model.Item.GetString("LinkText"))) 129 { 130 <input type="hidden" name="LinkText" value="@Model.Item.GetString("LinkText")" /> 131 } 132 @if (!string.IsNullOrEmpty(Model.Item.GetString("ImageAspectRatio"))) 133 { 134 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 135 ratio = ratio != "0" ? ratio : ""; 136 <input type="hidden" name="ImageAspectRatio" value="@ratio" /> 137 } 138 @if (!string.IsNullOrEmpty(Model.Item.GetString("Layout"))) 139 { 140 <input type="hidden" name="Layout" value="@Model.Item.GetRawValueString("Layout")" /> 141 } 142 @if (titleFontSize != "") 143 { 144 <input type="hidden" name="TitleFontSize" value="@titleFontSize" /> 145 } 146 @if (subtitleFontSize != "") 147 { 148 <input type="hidden" name="SubtitleFontSize" value="@subtitleFontSize" /> 149 } 150 @if (buttonStyle != "") 151 { 152 <input type="hidden" name="ButtonStyle" value="@buttonStyle" /> 153 } 154 @if (generalTheme != "") 155 { 156 <input type="hidden" name="GeneralTheme" value="@generalTheme" /> 157 } 158 @if (theme != "") 159 { 160 <input type="hidden" name="Theme" value="@theme" /> 161 } 162 @if (imageTheme != "") 163 { 164 <input type="hidden" name="ImageTheme" value="@imageTheme" /> 165 } 166 @if (!string.IsNullOrEmpty(Model.Item.GetString("ContentPadding"))) 167 { 168 string contentPadding = Model.Item.GetRawValueString("ContentPadding"); 169 <input type="hidden" name="ContentPadding" value="@contentPadding" /> 170 } 171 <input type="hidden" name="HideNavigationBar" value="@Model.Item.GetString("HideNavigationBar").ToLower()" /> 172 173 @* Types *@ 174 @if (relationType == "trending" || relationType == "most-sold" || 175 (relationType == "selected" && products == null && !product.GetRelatedProducts().Any())) 176 { 177 if (!string.IsNullOrEmpty(groupIds)) 178 { 179 <input type="hidden" name="GroupId" value="@groupIds" /> 180 <input type="hidden" name="SortBy" value="OrderCountGrowth" /> 181 } 182 } 183 @if (relationType == "most-sold") 184 { 185 <input type="hidden" name="SortBy" value="OrderCount" /> 186 } 187 @if (relationType == "frequently") 188 { 189 <input type="hidden" name="BoughtWithProductIds" value="[@productIds]" /> 190 } 191 @if (relationType == "selected") 192 { 193 productIds = string.Join(",", selectedProductIds); 194 <input type="hidden" name="MainProductID" value="@productIds" /> 195 } 196 197 <div id="RelatedProducts_@Model.ID" class="h-100"></div> 198 </form> 199 200 <script type="module"> 201 swift.ProductList.init(); 202 </script> 203