An error occurred while attaching module (Dynamicweb.Frontend.Content)

System.IndexOutOfRangeException: Index was outside the bounds of the array.
   at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
   at Dynamicweb.Ecommerce.FieldTypeProviders.ConversionUnitFieldTypeProviderBase.GetLanguageUnits(Dictionary`2 settings)
   at Dynamicweb.Ecommerce.FieldTypeProviders.ConversionUnitFieldTypeProviderBase.GetValue(Object value, String languageId, Dictionary`2 settings)
   at Dynamicweb.Ecommerce.Products.ProductFieldTypeProvider.GetProductValue(Product product, FieldType fieldType, Object fieldValue)
   at Dynamicweb.Ecommerce.Products.ProductFieldValueCollection.RefreshProviderFields(Product product)
   at Dynamicweb.Ecommerce.Products.ProductRepository.ExtractProduct(IDataReader dataReader, Nullable`1& groupProductRelationSortingExists)
   at Dynamicweb.Ecommerce.Products.ProductRepository.GetProductById(String productId, String productVariantId, String productLanguageId)
   at Dynamicweb.Ecommerce.Products.ProductService.FetchMissingProductsInternal(IProductRepository repo, IEnumerable`1 keys)
   at Dynamicweb.Caching.ServiceCache`2.GetCache(IEnumerable`1 keys)
   at Dynamicweb.Caching.ServiceCache`2.GetCache(TKey key)
   at Dynamicweb.Ecommerce.Products.ProductService.GetProductById(String productId, String productVariantId, String productLanguageId, User user, Boolean showUntranslated)
   at Dynamicweb.Ecommerce.Products.ProductService.GetProductById(String productId, String productVariantId, String productLanguageId, Boolean useAssortments)
   at Dynamicweb.Ecommerce.ProductCatalog.ProductCatalogFrontend.RenderProduct(String productId, String variantId, String groupId, ProductCatalogSettings settings)
   at Dynamicweb.Ecommerce.ProductCatalog.ProductCatalogFrontend.GetContent()
   at Dynamicweb.Frontend.Content.GetModuleOutput(Paragraph paragraph, PageView pageview)

Error executing template "Designs/Swift/Paragraph/Swift_ProductDetailsImage.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_70eadbef4a5742adade70737ff3d9553.Execute() in D:\dynamicweb.net\Solutions\Nextech\superhome.cloud.dynamicweb-cms.com\Files\Templates\Designs\Swift\Paragraph\Swift_ProductDetailsImage.cshtml:line 139
   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.Text.RegularExpressions; 6 7 @functions { 8 public ProductViewModel product { get; set; } = new ProductViewModel(); 9 public string galleryLayout { get; set; } 10 public string[] supportedImageFormats { get; set; } 11 public string[] supportedVideoFormats { get; set; } 12 public string[] supportedDocumentFormats { get; set; } 13 public string[] allSupportedFormats { get; set; } 14 15 public class RatioSettings 16 { 17 public string Ratio { get; set; } 18 public string CssClass { get; set; } 19 public string CssVariable { get; set; } 20 public string Fill { get; set; } 21 } 22 23 public RatioSettings GetRatioSettings(string size = "desktop") 24 { 25 var ratioSettings = new RatioSettings(); 26 27 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 28 ratio = ratio != "0" ? ratio : ""; 29 string cssClass = ratio != "" && ratio != "fill" ? " ratio" : ""; 30 string cssVariable = ratio != "" && ratio != "fill" ? "--bs-aspect-ratio: " + ratio : ""; 31 cssClass = ratio == "fill" && size == "mobile" ? " ratio" : cssClass; 32 cssVariable = ratio == "fill" && size == "mobile" ? "--bs-aspect-ratio: 66%" : cssVariable; 33 34 ratioSettings.Ratio = ratio; 35 ratioSettings.CssClass = cssClass; 36 ratioSettings.CssVariable = cssVariable; 37 ratioSettings.Fill = ratio == "fill" ? " h-100" : ""; 38 39 return ratioSettings; 40 } 41 42 public string GetArrowsColor() 43 { 44 var invertColor = Model.Item.GetBoolean("InvertModalArrowsColor"); 45 var arrowsColor = invertColor ? " carousel-dark" : string.Empty; 46 return arrowsColor; 47 } 48 49 public string GetThumbnailPlacement() 50 { 51 return Model.Item.GetRawValueString("ThumbnailPlacement", "bottom"); 52 } 53 54 public string GetThumbnailRowSettingCss() 55 { 56 switch (GetThumbnailPlacement()) 57 { 58 case "bottom": 59 return "d-flex flex-wrap"; 60 case "left": 61 return "d-flex flex-column order-first"; 62 case "right": 63 return "d-flex flex-column order-last"; 64 default: 65 return "d-flex flex-wrap"; 66 } 67 } 68 69 public Dictionary<string, object> GetVideoParams(MediaViewModel asset, string size) 70 { 71 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Name; 72 string type = GetVideoType(asset.Value); 73 bool openInModal = Model.Item.GetString("OpenVideoInModal") == "true" ? true : false; 74 bool autoPlay = Model.Item.GetBoolean("VideoAutoPlay"); 75 76 var videoParams = new Dictionary<string, object>(); 77 videoParams.Add("AssetName", asset.Name); 78 videoParams.Add("AssetVideoType", type); 79 videoParams.Add("AssetDisplayName", asset.DisplayName); 80 videoParams.Add("OpenVideoInModal", openInModal); 81 videoParams.Add("VideoAutoPlay", autoPlay); 82 videoParams.Add("Size", size); 83 videoParams.Add("Id", Model.ID); 84 return videoParams; 85 86 } 87 88 public string GetVideoType(string assetValue) 89 { 90 string type = assetValue.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "youtube" : string.Empty; 91 type = assetValue.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "vimeo" : type; 92 type = string.IsNullOrEmpty(type) ? "selfhosted" : type; 93 94 return type; 95 } 96 97 public string GetYoutubeScreenDump(string assetValue, string quality) 98 { 99 var regex = new Regex(@"(?:youtube\.com\/.*[\?&]v=|youtu\.be\/)([\w-]+)"); 100 Match match = regex.Match(assetValue); 101 string videoId = match.Success ? match.Groups[1].Value : string.Empty; 102 string youtubeThumbnail = $"https://img.youtube.com/vi/{videoId}/{quality}.jpg"; 103 return youtubeThumbnail; 104 } 105 } 106 107 @{ 108 ProductViewModel product = null; 109 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 110 { 111 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 112 } 113 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 114 { 115 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 116 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 117 118 if (productList?.Products is object) 119 { 120 product = productList.Products[0]; 121 } 122 } 123 } 124 125 @if (product is object) 126 { 127 @* Supported formats *@ 128 supportedImageFormats = new string[] { ".jpg", ".jpeg", ".webp", ".png", ".gif", ".bmp", ".tiff" }; 129 supportedVideoFormats = new string[] { "youtu.be", "youtube", "vimeo", ".mp4", ".webm" }; 130 supportedDocumentFormats = new string[] { ".pdf", ".docx", ".xlsx", ".ppt", "pptx" }; 131 allSupportedFormats = supportedImageFormats.Concat(supportedVideoFormats).Concat(supportedDocumentFormats).ToArray(); 132 133 @* Collect the assets *@ 134 var selectedAssetCategories = Model.Item.GetList("ImageAssets")?.GetRawValue().OfType<string>(); 135 bool includeImagePatternImages = Model.Item.GetBoolean("ImagePatternImages"); 136 137 @* Needed image data collection to support both DefaultImage, ImagePatterns and Image Assets *@ 138 string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : ""; 139 IEnumerable<MediaViewModel> assetsImages = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets); 140 assetsImages = assetsImages.OrderByDescending(x => x.Value.Equals(defaultImage)); 141 IEnumerable<MediaViewModel> assetsList = new MediaViewModel[] { }; 142 assetsList = assetsList.Union(assetsImages); 143 assetsList = includeImagePatternImages ? assetsList.Union(product.ImagePatternImages) : assetsList; 144 //CS NT assetsList = includeImagePatternImages && assetsList.Count() == 0 ? assetsList.Append(product.DefaultImage) : assetsList; 145 assetsList = includeImagePatternImages && assetsList.Count() == 0 ? assetsList.Append(product.DefaultImage) : assetsList.Prepend(product.DefaultImage);//CS NT Show primary Image 146 147 148 //assetsList = assetsList.Reverse(); 149 assetsList = assetsList.OrderBy(x => x.DisplayName); 150 151 152 bool defaultImageFallback = Model.Item.GetBoolean("DefaultImageFallback"); 153 bool showOnlyPrimaryImage = Model.Item.GetBoolean("ShowOnlyPrimaryImage"); 154 155 int totalAssets = 0; 156 if (showOnlyPrimaryImage == false) 157 { 158 foreach (MediaViewModel asset in assetsList) 159 { 160 var assetValue = asset.Value; 161 foreach (string format in allSupportedFormats) 162 { 163 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 164 { 165 totalAssets++; 166 } 167 } 168 } 169 } 170 171 if ((totalAssets == 0 && product.DefaultImage != null && selectedAssetCategories.Count() == 0) || (showOnlyPrimaryImage == true && product.DefaultImage != null) || totalAssets == 0 && defaultImageFallback) 172 { 173 assetsList = new List<MediaViewModel>() { product.DefaultImage }; 174 totalAssets = 1; 175 } 176 177 @* Theme settings *@ 178 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 179 180 var badgeParms = new Dictionary<string, object>(); 181 badgeParms.Add("size", "h5"); 182 badgeParms.Add("saleBadgeType", Model.Item.GetRawValue("SaleBadgeType")); 183 badgeParms.Add("saleBadgeCssClassName", Model.Item.GetRawValue("SaleBadgeDesign")); 184 badgeParms.Add("newBadgeCssClassName", Model.Item.GetRawValue("NewBadgeDesign")); 185 badgeParms.Add("newPublicationDays", Model.Item.GetInt32("NewPublicationDays")); 186 badgeParms.Add("campaignBadgesValues", Model.Item.GetList("CampaignBadges")?.GetRawValue().OfType<string>().ToList()); 187 188 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("SaleBadgeDesign")) && Model.Item.GetRawValueString("SaleBadgeDesign") != "none" ? true : false; 189 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("NewBadgeDesign")) && Model.Item.GetRawValueString("NewBadgeDesign") != "none" ? true : false; 190 DateTime createdDate = product.Created.Value; 191 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false; 192 showBadges = (newBadgeEnabled && Model.Item.GetInt32("NewPublicationDays") == 0) || (newBadgeEnabled && (createdDate.AddDays(Model.Item.GetInt32("NewPublicationDays")) > DateTime.Now)) ? true : showBadges; 193 showBadges = !string.IsNullOrEmpty(Model.Item.GetRawValueString("CampaignBadges")) ? true : showBadges; 194 195 196 197 //CS NT..Price only and sale sticker.. 198 var PS = new Dynamicweb.Ecommerce.Products.ProductService(); 199 var productObj = PS.GetProductById(product.Id, "", Pageview.Area.EcomLanguageId.ToString()); 200 string discountPercentage = ""; 201 string originalPrice = ""; 202 string onOffer = ""; 203 string customSticker = ""; 204 foreach (var prodCustomField in productObj.ProductFieldValues) 205 { 206 if (prodCustomField.ProductField.SystemName == "DiscountPercentage") 207 { 208 discountPercentage = prodCustomField.Value.ToString(); 209 } 210 if (prodCustomField.ProductField.SystemName == "OriginalPrice") 211 { 212 originalPrice = prodCustomField.Value.ToString(); 213 } 214 if (prodCustomField.ProductField.SystemName == "OnOffer") 215 { 216 onOffer = prodCustomField.Value.ToString(); 217 } 218 if (prodCustomField.ProductField.SystemName == "CustomSticker" && !string.IsNullOrWhiteSpace(prodCustomField.Value.ToString())) 219 { 220 customSticker = prodCustomField.Value.ToString(); 221 } 222 } 223 originalPrice = (originalPrice == "0") ? "" : ( onOffer == "Yes" ? "€" + originalPrice : ""); 224 bool onlyMono = string.IsNullOrWhiteSpace(originalPrice) && !string.IsNullOrWhiteSpace(discountPercentage) && discountPercentage.Contains("Super") ? true : false; 225 226 //..CS NT..Price only and sale sticker 227 228 bool isDesktop = Pageview.Device.ToString() == "Desktop"; 229 230 @* Get assets from selected categories or get all assets *@ 231 if (totalAssets != 0) 232 { 233 int assetNumber = 0; 234 int thumbnailNumber = 0; 235 int modalAssetNumber = 0; 236 string thumbnailAxisCss = GetThumbnailPlacement() == "bottom" ? "flex-column" : string.Empty; 237 238 string touchActionAuto_NT = !isDesktop ? "touchActionAuto" : "";//used to zoom for mobile and tablet 239 240 <div class="grid d-flex gap-1 h-100 @(thumbnailAxisCss) @(theme) item_@Model.Item.SystemName.ToLower()"> 241 242 243 244 245 246 247 <div id="SmallScreenImages_@Model.ID" class="@touchActionAuto_NT carousel@(GetArrowsColor()) col position-relative g-col " data-col-size="5" data-bs-ride="carousel"> 248 <div class="carousel-inner h-100"> 249 @foreach (MediaViewModel asset in assetsList) 250 { 251 var assetValue = asset.Value; 252 foreach (string format in allSupportedFormats) 253 { 254 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 255 { 256 string activeSlide = assetNumber == 0 ? "active" : ""; 257 258 <div class="carousel-item @activeSlide" data-bs-interval="99999"> 259 @{ 260 string size = "mobile"; 261 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 262 263 264 <div class="h-100 @(imageTheme)" id="ImgGallery_NT_@Model.ID"> 265 @foreach (string imageFormat in supportedImageFormats) 266 { //Images 267 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 268 { 269 if (product is object) 270 { 271 string productName = product.Name; 272 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 273 string imageLinkPath = Dynamicweb.Context.Current.Server.UrlEncode(imagePath); 274 275 RatioSettings ratioSettings = GetRatioSettings(size); 276 277 var parms = new Dictionary<string, object>(); 278 parms.Add("alt", productName + asset.Keywords); 279 parms.Add("itemprop", "image"); 280 parms.Add("columns", Model.GridRowColumnCount); 281 parms.Add("eagerLoadNewImages", Model.Item.GetBoolean("DisableLazyLoading")); 282 parms.Add("doNotUseGetimage", Model.Item.GetBoolean("DisableGetImage")); 283 if (!string.IsNullOrEmpty(asset.DisplayName)) 284 { 285 parms.Add("title", asset.DisplayName); 286 } 287 288 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") 289 { 290 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 291 } 292 else 293 { 294 parms.Add("cssClass", "mw-100 mh-100"); 295 } 296 297 parms.Add("id","Image_" + product.Id + "_" + assetNumber.ToString());//CS NT 298 string divId = "Image_" + product.Id + "_" + assetNumber.ToString() + "_div"; 299 300 //CS NT Do not show pop up modal on mobile 20260405 301 if(isDesktop) 302 { 303 304 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 305 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@assetNumber"> 306 307 @if(Pageview.Device.ToString() == "Desktop") 308 { 309 parms.Add("ModelID", Model.ID); 310 311 @RenderPartial("Components/ImageWithZoomNT.cshtml", new FileViewModel { Path = imagePath }, parms) 312 } 313 else 314 { 315 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 316 } 317 </div> 318 </a> 319 }else 320 { 321 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 322 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 323 </div> 324 } 325 } 326 } 327 } 328 @foreach (string videoFormat in supportedVideoFormats) 329 { //Videos 330 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 331 { 332 if (Model.Item.GetString("OpenVideoInModal") == "true") 333 { 334 if (product is object) 335 { 336 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 337 338 string productName = product.Name; 339 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 340 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 341 342 RatioSettings ratioSettings = GetRatioSettings(size); 343 344 string type = GetVideoType(asset.Value); 345 346 string videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value, "maxresdefault") : string.Empty; 347 videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeDataString(asset.Value) : videoScreendumpPath; 348 string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : ""; 349 350 351 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable); cursor: pointer" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 352 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@assetNumber"> 353 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 354 @if (type != "selfhosted") 355 { 356 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@productName" @assetTitle class="@videoJsClass mw-100 mh-100" data-asset-value="@asset.Value" style="object-fit: cover;"> 357 } 358 else 359 { 360 string videoType = Path.GetExtension(asset.Value).ToLower(); 361 362 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 363 <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> 364 </video> 365 } 366 </div> 367 </div> 368 369 } 370 } 371 else 372 { 373 if (product is object) 374 { 375 var videoParams = GetVideoParams(asset, size); 376 @RenderPartial("Components/VideoPlayer.cshtml", new FileViewModel { Path = asset.Value}, videoParams); 377 378 } 379 } 380 } 381 } 382 @foreach (string documentFormat in supportedDocumentFormats) 383 { //Documents 384 if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) 385 { 386 if (product is object) 387 { 388 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 389 390 string productName = product.Name; 391 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 392 string imageLinkPath = imagePath; 393 394 RatioSettings ratioSettings = GetRatioSettings(size); 395 396 var parms = new Dictionary<string, object>(); 397 parms.Add("alt", productName + asset.Keywords); 398 parms.Add("itemprop", "image"); 399 parms.Add("fullwidth", true); 400 parms.Add("columns", Model.GridRowColumnCount); 401 if (!string.IsNullOrEmpty(asset.DisplayName)) 402 { 403 parms.Add("title", asset.DisplayName); 404 } 405 406 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") 407 { 408 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 409 } 410 else 411 { 412 parms.Add("cssClass", "mw-100 mh-100"); 413 } 414 415 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download alt="@Translate("Download")"> 416 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 417 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 418 @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) 419 { 420 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 421 } 422 </div> 423 </a> 424 } 425 426 } 427 } 428 </div> 429 } 430 431 432 </div> 433 assetNumber++; 434 } 435 } 436 } 437 </div> 438 @if (showBadges) 439 { 440 <div class="position-absolute top-0 left-0 p-2 p-lg-3"> 441 @{@RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms)} 442 </div> 443 } 444 445 446 @if(!string.IsNullOrWhiteSpace(customSticker)) 447 { 448 if(customSticker.StartsWith("/")) 449 { 450 <div class="position-absolute customStickerDivNT"> 451 <img style="width: 90px;" src="/Admin/Public/GetImage.ashx?Width=100&Height=90&Crop=5&DoNotUpscale=True&FillCanvas=True&Image=@customSticker&AlternativeImage=/Images/missing_image.jpg" alt="Custom Sticker" title="" data-image="@customSticker"> 452 </div> 453 } 454 else 455 { 456 <div class="position-absolute customStickerDivNT"> 457 <div class="stickers-container__tag--sale">@customSticker</div> 458 </div> 459 } 460 } 461 462 @*CS NT Hide from here and place under price 463 @if(onlyMono) 464 { 465 466 <div class="position-absolute monoStickerDivNT"> 467 <img style="width: 60px;" src="/Admin/Public/GetImage.ashx?Width=100&Height=90&Crop=5&DoNotUpscale=True&FillCanvas=True&Image=/Files/Images/General/SUPER LABEL ΜΟΝΟ.png&AlternativeImage=/Images/missing_image.jpg" alt="Only Mono Price" title="" data-image="/Files/Images/SUPER LABEL ΜΟΝΟ.png"> 468 </div> 469 }else if(!string.IsNullOrWhiteSpace(discountPercentage)) 470 { 471 <div class="position-absolute top-0 left-0 stickers-container stickers-container--bottom-right dw-mod"> 472 <div class="stickers-container__tag--sale_ProdPage">@discountPercentage</div> 473 </div> 474 } 475 *@ 476 </div> 477 478 479 @if (totalAssets > 1) 480 { 481 <div class="@(GetThumbnailRowSettingCss()) gap-3" id="SmallScreenImagesThumbnails_@Model.ID"> 482 @foreach (MediaViewModel asset in assetsList) 483 { 484 var assetValue = asset.Value; 485 string assetName = asset.Name; 486 assetName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 487 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : null; 488 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 489 490 string imagePath = Dynamicweb.Context.Current.Server.UrlEncode(assetValue); 491 imagePath = assetValue.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "https://img.youtube.com/vi/" + assetValue.Substring(assetValue.LastIndexOf('/') + 1) + "/mqdefault.jpg" : imagePath; 492 string imagePathThumb = assetValue.StartsWith("/Files/", StringComparison.OrdinalIgnoreCase) ? imagePath.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) < 0 && imagePath.IndexOf(".mp4", StringComparison.OrdinalIgnoreCase) < 0 ? $"/Admin/Public/GetImage.ashx?image={imagePath}&width=180&format=webp" : imagePath : assetValue; 493 494 RatioSettings ratioSettings = GetRatioSettings("desktop"); 495 496 <div class="border outline-none @(ratioSettings.CssClass) thumbImageNT" data-assetNumber="@thumbnailNumber" style="@(ratioSettings.CssVariable); cursor: pointer; min-width: 5rem; max-width: 5rem;" data-bs-target="#SmallScreenImages_@Model.ID" data-bs-slide-to="@thumbnailNumber"> 497 @foreach (string imageFormat in supportedImageFormats) 498 { //Images 499 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 500 { 501 <img src="@imagePathThumb" alt="@assetName" @assetTitle class="p-0 p-lg-1 w-100 h-100" style="object-fit: contain;"> 502 503 thumbnailNumber++; 504 } 505 } 506 507 @foreach (string videoFormat in supportedVideoFormats) 508 { //Images 509 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 510 { 511 512 string type = GetVideoType(asset.Value); 513 514 string videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value, "mqdefault") : ""; 515 videoScreendumpPath = type == "vimeo" ? string.Empty : videoScreendumpPath; 516 videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeDataString(asset.Value) : videoScreendumpPath; 517 string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : string.Empty; 518 519 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 520 521 if (type != "selfhosted") 522 { 523 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@assetTitle" @assetTitle class="@videoJsClass mw-100 mh-100" data-asset-value="@asset.Value" style="object-fit: cover;" /> 524 } 525 else 526 { 527 string videoType = Path.GetExtension(asset.Value).ToLower(); 528 529 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 530 <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> 531 </video> 532 } 533 534 thumbnailNumber++; 535 } 536 } 537 538 @foreach (string documentFormat in supportedDocumentFormats) 539 { //Images 540 if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) 541 { 542 <a href="@assetValue" class="ratio ratio-4x3 border outline-none" style="cursor: pointer; min-width: 7rem; max-width: 8rem;" download title="@asset.Value"> 543 @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) 544 { 545 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 546 <div class="icon-3 position-absolute text-light" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 547 </div> 548 <img src="@imagePathThumb" alt="@assetName" @assetTitle class="p-0 p-lg-1 mw-100 mh-100" style="object-fit: cover;"> 549 } 550 else 551 { 552 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 553 <div class="icon-3 position-absolute" style="z-index: 1">@ReadFile(iconPath + "file-text.svg")</div> 554 </div> 555 } 556 </a> 557 558 thumbnailNumber++; 559 } 560 } 561 562 </div> 563 } 564 </div> 565 } 566 </div> 567 568 @* Modal with slides *@ 569 <div class="modal fade swift_products-details-images-modal" id="modal_@Model.ID" tabindex="-1" aria-labelledby="productDetailsGalleryModalTitle_@Model.ID" aria-hidden="true"> 570 <div class="modal-dialog modal-dialog-centered modal-xl"> 571 <div class="modal-content"> 572 <div class="modal-header visually-hidden"> 573 <h1 class="modal-title" id="productDetailsGalleryModalTitle_@Model.ID">@product.Title</h1> 574 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 575 </div> 576 <div class="modal-body p-2 p-lg-3 h-100"> 577 <div id="ModalCarousel_@Model.ID" class="carousel@(GetArrowsColor()) h-100" data-bs-ride="carousel"> 578 <div class="carousel-inner h-100 @theme"> 579 @foreach (MediaViewModel asset in assetsList) 580 { 581 var assetValue = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 582 foreach (string supportedFormat in supportedImageFormats.Concat(supportedVideoFormats).ToArray()) 583 { 584 if (assetValue.IndexOf(supportedFormat, StringComparison.OrdinalIgnoreCase) >= 0) 585 { 586 string imagePath = assetValue; 587 string activeSlide = modalAssetNumber == 0 ? "active" : ""; 588 589 var parms = new Dictionary<string, object>(); 590 parms.Add("cssClass", "d-block mw-100 mh-100 m-auto"); 591 parms.Add("fullwidth", true); 592 parms.Add("columns", Model.GridRowColumnCount); 593 594 <div class="carousel-item @activeSlide h-100" data-bs-interval="99999"> 595 @foreach (string imageFormat in supportedImageFormats) 596 { //Images 597 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 598 { 599 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 600 } 601 } 602 603 @foreach (string videoFormat in supportedVideoFormats) 604 { //Videos 605 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 606 { 607 if (product is object) 608 { 609 var videoParams = GetVideoParams(asset, "modal"); 610 @RenderPartial("Components/VideoPlayer.cshtml", new FileViewModel { Path = asset.Value }, videoParams) 611 } 612 } 613 } 614 </div> 615 modalAssetNumber++; 616 } 617 } 618 } 619 <button class="carousel-control-prev carousel-control-area" type="button" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide="prev"> 620 <span class="carousel-control-prev-icon" aria-hidden="true"></span> 621 <span class="visually-hidden">@Translate("Previous")</span> 622 </button> 623 <button class="carousel-control-next carousel-control-area" type="button" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide="next"> 624 <span class="carousel-control-next-icon" aria-hidden="true"></span> 625 <span class="visually-hidden">@Translate("Next")</span> 626 </button> 627 </div> 628 </div> 629 </div> 630 </div> 631 </div> 632 </div> 633 } 634 else if (Pageview.IsVisualEditorMode) 635 { 636 RatioSettings ratioSettings = GetRatioSettings("desktop"); 637 638 <div class="h-100 @theme"> 639 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> 640 <img src="/Files/Images/missing_image.jpg" loading="lazy" decoding="async" class="mh-100 mw-100" style="object-fit: cover;"> 641 </div> 642 </div> 643 } 644 645 646 647 if(Pageview.Device.ToString() == "Desktop") 648 { 649 <script> 650 651 652 $(document).ready(function () { 653 var $carousel = $(".item_swift_productdetailsimage"); 654 655 function showSpinner($img) { 656 // Ensure parent wrapper is positioned 657 if (!$img.parent().hasClass("img-zoom-wrapper")) { 658 $img.wrap('<div class="img-zoom-wrapper"></div>'); 659 } 660 var $wrapper = $img.parent(".img-zoom-wrapper"); 661 662 // Add spinner if not already present 663 if ($wrapper.find(".zoom-spinner").length === 0) { 664 $wrapper.append('<div class="zoom-spinner"></div>'); 665 } 666 } 667 668 function hideSpinner($img) { 669 $img.parent(".img-zoom-wrapper").find(".zoom-spinner").remove(); 670 } 671 672 function initZoom($img) { 673 if (!$img.length) return; 674 675 // Clean up previous zoom 676 var ez = $img.data('ezPlus'); 677 if (ez && typeof ez.destroy === 'function') { 678 ez.destroy(); 679 } 680 $('.zoomContainer').remove(); 681 $img.removeData('ezPlus'); 682 683 var zoomSrc = $img.data('zoom-image'); 684 if (!zoomSrc) return; 685 686 // Show spinner while loading zoom image 687 showSpinner($img); 688 689 $img.one("load", function () { 690 var zoomImg = new Image(); 691 zoomImg.onload = function () { 692 var imgW = $img.width(); 693 var imgH = $img.height(); 694 695 if (this.width <= imgW || this.height <= imgH) { 696 $img.ezPlus({ 697 zoomType: "lens", 698 lensShape: "round", 699 lensSize: 150, 700 scrollZoom: true, 701 zoomLevel: 1.5 702 }); 703 } else { 704 var winW = Math.min(this.width, Math.floor($(window).width() * 0.4)); 705 var winH = Math.min(this.height, Math.floor($(window).height() * 0.6)); 706 707 //if(winW < 400) winW = 400; 708 //if(winH < 400) winH = 400; 709 710 711 $img.ezPlus({ 712 zoomType: "window", 713 cursor: "crosshair", 714 zoomWindowWidth: winW, 715 zoomWindowHeight: winH, 716 zoomWindowOffsetX: 20, 717 borderSize: 1, 718 borderColour: "#888", 719 scrollZoom: true, 720 lensSize: 200 721 }); 722 } 723 724 // Remove spinner when zoom ready 725 hideSpinner($img); 726 }; 727 zoomImg.src = zoomSrc; 728 }).each(function () { 729 if (this.complete) $(this).trigger("load"); 730 }); 731 } 732 733 // Initialize on first active image 734 initZoom($carousel.find(".carousel-item.active img")); 735 736 // Re-init after carousel slide 737 $carousel.on("slid.bs.carousel", function () { 738 var $newImg = $carousel.find(".carousel-item.active img"); 739 setTimeout(function () { 740 initZoom($newImg); 741 }, 100); 742 }); 743 744 // Re-init on resize 745 $(window).on("resize", function () { 746 var $activeImg = $carousel.find(".carousel-item.active img"); 747 initZoom($activeImg); 748 }); 749 }); 750 751 752 753 754 /* Works fine without spinner 755 $(document).ready(function () { 756 var $carousel = $(".item_swift_productdetailsimage"); 757 758 function initZoom($img) { 759 if (!$img.length) return; 760 761 // Clean up previous zoom 762 var ez = $img.data('ezPlus'); 763 if (ez && typeof ez.destroy === 'function') { 764 ez.destroy(); 765 } 766 $('.zoomContainer').remove(); 767 $img.removeData('ezPlus'); 768 769 // Get zoom image 770 var zoomSrc = $img.data('zoom-image'); 771 if (!zoomSrc) return; 772 773 // Make sure image is fully loaded before attaching zoom 774 $img.one("load", function () { 775 var zoomImg = new Image(); 776 zoomImg.onload = function () { 777 var imgW = $img.width(); 778 var imgH = $img.height(); 779 780 if (this.width <= imgW || this.height <= imgH) { 781 // Fallback to lens zoom 782 $img.ezPlus({ 783 zoomType: "lens", 784 lensShape: "round", 785 lensSize: 150, // smaller lens → stronger magnification 786 scrollZoom: true, 787 zoomLevel: 1.5 788 }); 789 } else { 790 // Use window zoom, scaled responsively 791 var winW = Math.min(this.width, Math.floor($(window).width() * 0.4)); 792 var winH = Math.min(this.height, Math.floor($(window).height() * 0.6)); 793 794 $img.ezPlus({ 795 zoomType: "window", 796 cursor: "crosshair", 797 zoomWindowWidth: winW, 798 zoomWindowHeight: winH, 799 zoomWindowOffsetX: 20, 800 borderSize: 1, 801 borderColour: "#888", 802 scrollZoom: true, 803 lensSize: 200 804 }); 805 } 806 }; 807 zoomImg.src = zoomSrc; 808 }).each(function () { 809 if (this.complete) $(this).trigger("load"); 810 }); 811 } 812 813 // Initialize first image 814 initZoom($carousel.find(".carousel-item.active img")); 815 816 // Re-init zoom when carousel finishes sliding 817 $carousel.on("slid.bs.carousel", function () { 818 var $newImg = $carousel.find(".carousel-item.active img"); 819 820 // small timeout to let Bootstrap finish DOM changes 821 setTimeout(function () { 822 initZoom($newImg); 823 }, 100); 824 }); 825 826 // Re-init on window resize 827 $(window).on("resize", function () { 828 var $activeImg = $carousel.find(".carousel-item.active img"); 829 initZoom($activeImg); 830 }); 831 }); 832 */ 833 834 835 </script> 836 } 837 838 if(false && Pageview.Device.ToString() == "Desktop") 839 { 840 <script> 841 $(document).ready(function(){ 842 843 function enableHoverZoom() { 844 // $(".carousel-item.active ").hover( 845 $(".item_swift_productdetailsimage .carousel-item.active .img-zoom-container").hover( 846 847 function(){ 848 $("#zoomImageResult").css("visibility","visible"); 849 $(".img-zoom-lens").css("visibility","visible"); 850 }, function(){ 851 $("#zoomImageResult").css("visibility","hidden"); 852 $(".img-zoom-lens").css("visibility","hidden"); 853 } 854 ); 855 }; 856 enableHoverZoom(); 857 858 imageZoom("Image_@product.Id" + "_0", "zoomImageResult"); 859 $(".img-zoom-lens").css("visibility","hidden"); 860 861 862 $(".thumbImageNT").on('click', function(event){ 863 864 imageZoom("Image_@product.Id" + "_" + $(this).attr("data-assetnumber"), "zoomImageResult"); 865 enableHoverZoom(); 866 867 }); 868 869 870 });// end of ready 871 872 window.onload = function() { 873 }; 874 </script> 875 } 876 877 878 } 879 else if (Pageview.IsVisualEditorMode) 880 { 881 <div class="alert alert-dark m-0">@Translate("No products available")</div> 882 } 883 884 885 886

Error executing template "Designs/Swift/Paragraph/Swift_ProductPrice.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_2be720bd3f6c45559309d5e163c2932b.Execute() in D:\dynamicweb.net\Solutions\Nextech\superhome.cloud.dynamicweb-cms.com\Files\Templates\Designs\Swift\Paragraph\Swift_ProductPrice.cshtml:line 38
   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 NextechDWAddIn.Common 4 5 @{ 6 ProductViewModel product = null; 7 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 8 { 9 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 10 } 11 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 12 { 13 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 14 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 15 16 if (productList?.Products is object) 17 { 18 product = productList.Products[0]; 19 } 20 } 21 22 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", ""); 23 bool anonymousUser = Pageview.User == null; 24 bool isErpConnectionDown = !Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsWebServiceConnectionAvailable"]); 25 bool hidePrice = anonymousUsersLimitations.Contains("price") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHidePrices") && isErpConnectionDown; 26 27 bool productIsDiscontinued = product is object && product.Discontinued; 28 bool doNotShowPriceIfProductIsDiscontinued = Model.Item.GetBoolean("DoNotShowPriceIfProductIsDiscontinued"); 29 var isDiscontinued = productIsDiscontinued && doNotShowPriceIfProductIsDiscontinued; 30 31 //CS NT.. 32 string originalPrice = ""; 33 string comparisonPrice = ""; 34 string discountPercentage = ""; 35 36 if(product != null) 37 { 38 foreach (var pf in product.ProductFields) 39 { 40 if (!string.IsNullOrWhiteSpace(pf.Key) && pf.Value != null && pf.Value.Value != null && !string.IsNullOrWhiteSpace(pf.Value.Value.ToString())) 41 { 42 if (pf.Key == "OriginalPrice") 43 { 44 originalPrice = pf.Value.Value.ToString(); 45 } else if (pf.Key == "ComparisonPrice") 46 { 47 comparisonPrice = CommonFunctions.HandleComparisonPriceAccordingToLaw(pf.Value.Value.ToString()); 48 } else if (pf.Key == "DiscountPercentage") 49 { 50 discountPercentage = pf.Value.Value.ToString(); 51 } 52 } 53 } 54 } 55 //..CS NT 56 57 58 59 bool isProductDetailsPage = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("ProductID")) ? true : false; 60 string originalPriceHeight = isProductDetailsPage ? "height:35px;" : "height:25px;"; 61 62 //CS NT Show monoOnly under Price 63 64 bool onlyMono = (string.IsNullOrWhiteSpace(originalPrice) || originalPrice == "0") && !string.IsNullOrWhiteSpace(discountPercentage) && discountPercentage.Contains("Super") ? true : false; 65 66 } 67 68 @if (product is object && !hidePrice && !isDiscontinued) { 69 bool showInformativePrice = Model.Item.GetBoolean("ShowInformativePrice"); 70 string unitId = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("UnitId")) ? Dynamicweb.Context.Current.Request.Form.Get("UnitId") : string.Empty; 71 72 string priceFontSize = Model.Item.GetRawValueString("PriceSize", "fs-2"); 73 string horizontalAlign = Model.Item.GetRawValueString("HorizontalAlignment", ""); 74 string layout = Model.Item.GetRawValueString("Layout", "horizontal"); 75 string textAlign = horizontalAlign == "center" ? "text-center" : string.Empty; 76 textAlign = horizontalAlign == "end" ? "text-end" : textAlign; 77 78 horizontalAlign = horizontalAlign == "center" && layout == "horizontal" ? "justify-content-center" : horizontalAlign; 79 horizontalAlign = horizontalAlign == "end" && layout == "horizontal" ? "justify-content-end" : horizontalAlign; 80 horizontalAlign = horizontalAlign == "center" && layout == "vertical" ? "align-items-center" : horizontalAlign; 81 horizontalAlign = horizontalAlign == "end" && layout == "vertical" ? "align-items-end" : horizontalAlign; 82 83 string flexDirection = layout == "horizontal" ? string.Empty : "flex-column"; 84 string flexGap = layout == "horizontal" ? "gap-3" : string.Empty; 85 string order = layout == "horizontal" ? string.Empty : "order-2"; 86 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? "theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 87 theme = GetViewParameter("theme") != null ? GetViewParameterString("theme") : theme; 88 89 string contentPadding = Model.Item.GetRawValueString("ContentPadding", ""); 90 contentPadding = contentPadding == "none" ? "p-0" : contentPadding; 91 contentPadding = contentPadding == "small" ? "p-1 px-md-2 py-md-1" : contentPadding; 92 contentPadding = contentPadding == "large" ? "p-2 px-md-3 py-md-2" : contentPadding; 93 94 string showPricesWithVat = Pageview.Area.EcomPricesWithVat.ToLower(); 95 bool neverShowVat = string.IsNullOrEmpty(showPricesWithVat); 96 97 string priceMin = ""; 98 string priceMax = ""; 99 100 string liveInfoClass = ""; 101 string productInfoFeed = ""; 102 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]); 103 if (isLazyLoadingForProductInfoEnabled) 104 { 105 if (Dynamicweb.Context.Current.Items.Contains("ProductInfoFeed")) 106 { 107 productInfoFeed = Dynamicweb.Context.Current.Items["ProductInfoFeed"]?.ToString(); 108 if (!string.IsNullOrEmpty(productInfoFeed)) 109 { 110 productInfoFeed = $"data-product-info-feed=\"{productInfoFeed}\""; 111 } 112 } 113 liveInfoClass = "js-live-info"; 114 } 115 116 <div class="@textAlign @liveInfoClass item_@Model.Item.SystemName.ToLower()" data-product-id="@product.Id" data-variant-id="@product.VariantId" @productInfoFeed> 117 @if (showInformativePrice && product.PriceInformative.Price != 0) 118 { 119 <div class="opacity-50"> 120 <span>@Translate("RRP") </span> 121 <span class="text-decoration-line-through text-price">@product.PriceInformative.PriceFormatted</span> 122 </div> 123 } 124 <!--<div class="@priceFontSize m-0 d-flex flex-wrap @flexDirection @flexGap @horizontalAlign" style="row-gap: 0 !important" itemprop="offers" itemscope itemtype="https://schema.org/Offer">--> 125 <div class="@priceFontSize m-0 d-flex flex-wrap @flexDirection @flexGap @horizontalAlign" style="row-gap: 0 !important" itemprop="offers" itemscope> 126 <span itemprop="priceCurrency" content="@product.Price.CurrencyCode" class="d-none"></span> 127 128 129 @if (showPricesWithVat == "false" && !neverShowVat) 130 { 131 if (isLazyLoadingForProductInfoEnabled && !Pageview.IsVisualEditorMode) 132 { 133 <span itemprop="price" content="" class="d-none"></span> 134 <span class="text-decoration-line-through js-text-decoration-line-through opacity-75 me-3 text-price js-text-price d-none" data-show-if="LiveProductInfo.product.Price.Price != LiveProductInfo.product.PriceBeforeDiscount.Price"></span> 135 } 136 else 137 { 138 string beforePrice = !string.IsNullOrEmpty(unitId) ? product.GetPrice(unitId).PriceBeforeDiscount.PriceWithoutVatFormatted : product.PriceBeforeDiscount.PriceWithoutVatFormatted; 139 140 <span itemprop="price" content="@product.Price.PriceWithoutVat" class="d-none"></span> 141 if (product.Price.Price != product.PriceBeforeDiscount.Price) 142 { 143 <span class="text-decoration-line-through opacity-75 @order">@beforePrice</span> 144 } 145 } 146 } 147 else 148 { 149 if (isLazyLoadingForProductInfoEnabled && !Pageview.IsVisualEditorMode) 150 { 151 <span itemprop="price" content="" class="d-none"></span> 152 <span class="text-decoration-line-through js-text-decoration-line-through opacity-75 me-3 text-price js-text-price d-none" data-show-if="LiveProductInfo.product.Price.Price != LiveProductInfo.product.PriceBeforeDiscount.Price"></span> 153 } 154 else 155 { 156 string beforePrice = !string.IsNullOrEmpty(unitId) ? product.GetPrice(unitId).PriceBeforeDiscount.PriceFormatted : product.PriceBeforeDiscount.PriceFormatted; 157 <span itemprop="price" content="@product.Price.Price" class="d-none"></span> 158 if (product.Price.Price != product.PriceBeforeDiscount.Price) 159 { 160 <span class="text-decoration-line-through opacity-75 @order"> 161 <span class="text-price">@beforePrice</span> 162 </span> 163 } 164 } 165 } 166 167 @if (showPricesWithVat == "false" && !neverShowVat) 168 { 169 if (isLazyLoadingForProductInfoEnabled && !Pageview.IsVisualEditorMode) 170 { 171 <span class="text-price js-text-price"> 172 <span class="spinner-border" role="status"></span> 173 </span> 174 } 175 else 176 { 177 string price = !string.IsNullOrEmpty(unitId) ? product.GetPrice(unitId).Price.PriceWithoutVatFormatted : product.Price.PriceWithoutVatFormatted; 178 179 if (product?.VariantInfo?.VariantInfo != null) 180 { 181 priceMin = product?.VariantInfo?.PriceMin?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithoutVatFormatted : ""; 182 priceMax = product?.VariantInfo?.PriceMax?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithoutVatFormatted : ""; 183 } 184 if (priceMin != priceMax) 185 { 186 price = priceMin + " - " + priceMax; 187 } 188 <span class="@theme @contentPadding"> 189 <span class="text-price">@price</span> 190 </span> 191 } 192 } 193 else 194 { 195 196 @*CS NT OriginalPrice.. *@ 197 <!--<div class="@priceFontSize m-0 d-flex @flexDirection @flexGap @horizontalAlign" style="@originalPriceHeight width:100%" itemprop="offers" itemscope itemtype="https://schema.org/Offer">--> 198 <div class="@priceFontSize m-0 d-flex @flexDirection @flexGap @horizontalAlign" style="@originalPriceHeight width:100%" itemprop="offers" itemscope> 199 @if(!string.IsNullOrWhiteSpace(originalPrice) && originalPrice != "0") 200 { 201 <span class="" style="/*line-height: 1.7;*/"> 202 <span class="text-decoration-line-through-nt opacity-75 original-price-nt" style="">&nbsp; €@originalPrice &nbsp;</span> 203 </span> 204 } 205 </div> 206 207 @*..CS NT OriginalPrice *@ 208 209 210 211 if (isLazyLoadingForProductInfoEnabled && !Pageview.IsVisualEditorMode) 212 { 213 <span class="text-price js-text-price"> 214 <span class="spinner-border" role="status"></span> 215 </span> 216 } 217 else 218 { 219 string price = !string.IsNullOrEmpty(unitId) ? product.GetPrice(unitId).Price.PriceFormatted : product.Price.PriceFormatted; 220 price=price.Replace("EUR","€"); 221 222 if (product?.VariantInfo?.VariantInfo != null) 223 { 224 priceMin = product?.VariantInfo?.PriceMin?.PriceFormatted != null ? product.VariantInfo.PriceMin.PriceFormatted : ""; 225 priceMax = product?.VariantInfo?.PriceMax?.PriceFormatted != null ? product.VariantInfo.PriceMax.PriceFormatted : ""; 226 } 227 if (priceMin != priceMax) 228 { 229 price = priceMin + " - " + priceMax; 230 } 231 <span class="@theme @contentPadding"> 232 <span class="text-price">@price</span> 233 234 @if(!string.IsNullOrWhiteSpace(comparisonPrice) && comparisonPrice != "0") 235 { 236 <span class="opacity-75" style="font-size:12px;line-height: 2;color:black;">€@comparisonPrice</span> 237 } 238 </span> 239 240 } 241 } 242 243 244 245 @if(isProductDetailsPage) 246 { 247 if(onlyMono) 248 { 249 250 <div class="monoStickerDivNT"> 251 <img style="width: 60px;" src="/Admin/Public/GetImage.ashx?Width=100&Height=90&Crop=5&DoNotUpscale=True&FillCanvas=True&Image=/Files/Images/General/SUPER LABEL ΜΟΝΟ.png&AlternativeImage=/Images/missing_image.jpg" alt="Only Mono Price" title="" data-image="/Files/Images/SUPER LABEL ΜΟΝΟ.png"> 252 </div> 253 }else if(!string.IsNullOrWhiteSpace(discountPercentage)) 254 { 255 <div class=" top-0 left-0 stickers-container stickers-container--bottom-right dw-mod"> 256 <div class="stickers-container__tag--sale_ProdPage">@discountPercentage</div> 257 </div> 258 } 259 } 260 261 262 @* Stock state for Schema.org, start *@ 263 @{ 264 Uri url = Dynamicweb.Context.Current.Request.Url; 265 } 266 267 <link itemprop="url" href="@url"> 268 269 @{ 270 bool IsNeverOutOfStock = product.NeverOutOfstock; 271 } 272 273 @if (IsNeverOutOfStock) 274 { 275 <span itemprop="availability" class="d-none">@Translate("Available in stock")</span> 276 } 277 else 278 { 279 if (product.StockLevel > 0) 280 { 281 <span itemprop="availability" class="d-none">InStock</span> 282 } 283 else 284 { 285 <span itemprop="availability" class="d-none">OutOfStock</span> 286 } 287 } 288 @* Stock state for Schema.org, stop *@ 289 290 291 292 293 294 </div> 295 296 @if (showPricesWithVat == "false" && !neverShowVat) 297 { 298 if (isLazyLoadingForProductInfoEnabled && !Pageview.IsVisualEditorMode) 299 { 300 <small class="opacity-85 fst-normal js-text-price-with-vat d-none" data-suffix="@Translate("Incl. VAT")"></small> 301 } 302 else 303 { 304 string price = !string.IsNullOrEmpty(unitId) ? product.GetPrice(unitId).Price.PriceWithVatFormatted : product.Price.PriceWithVatFormatted; 305 306 if (product?.VariantInfo?.VariantInfo != null) 307 { 308 priceMin = product?.VariantInfo?.PriceMin?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithVatFormatted : ""; 309 priceMax = product?.VariantInfo?.PriceMax?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithVatFormatted : ""; 310 } 311 if (priceMin != priceMax) 312 { 313 price = priceMin + " - " + priceMax; 314 } 315 <small class="opacity-85 fst-normal">@price @Translate("Incl. VAT")</small> 316 } 317 } 318 319 320 321 322 </div> 323 } 324 else if (Pageview.IsVisualEditorMode) 325 { 326 <div class="alert alert-dark m-0" role="alert"> 327 <span>@Translate("No products available")</span> 328 </div> 329 } 330
Error executing template "Designs/Swift/Paragraph/Swift_ProductNumber.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_9b041f5e2fdf4cfbbf7828c77883086a.Execute() in D:\dynamicweb.net\Solutions\Nextech\superhome.cloud.dynamicweb-cms.com\Files\Templates\Designs\Swift\Paragraph\Swift_ProductNumber.cshtml:line 32
   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 ProductViewModel product = null; 6 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 7 { 8 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 9 } 10 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 11 { 12 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 13 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 14 15 if (productList?.Products is object) 16 { 17 product = productList.Products[0]; 18 } 19 } 20 21 22 bool isProductDetailsPage = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("ProductID")) ? true : false; 23 } 24 25 26 27 @*CS NT Share button 20251118*@ 28 @if(isProductDetailsPage) 29 { 30 var PS = new Dynamicweb.Ecommerce.Products.ProductService(); 31 var productObjectNT = PS.GetProductById(product.Id, "", Pageview.Area.EcomLanguageId.ToString()); 32 string imageLinkPath_NT = !string.IsNullOrEmpty(product.DefaultImage.Value) ? Uri.EscapeDataString(product.DefaultImage.Value) : ""; 33 //CS NT This does not work, moved to Swift_Master -> Pageview.Meta.AddTag($"<meta property=\"og:image\" content=\"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{imageLinkPath_NT}\">"); 34 string productNumberNT = product.Name.Replace("\"","\\\""); 35 36 if(product.Price.PriceWithVat >= 100) 37 { 38 string FleksyBuyNowPayLater_pageId = GetPageIdByNavigationTag("FleksyBuyNowPayLater") != 0 ? "/Default.aspx?ID=" + GetPageIdByNavigationTag("FleksyBuyNowPayLater").ToString() : ""; 39 40 <div class=" theme superhomeredfonts item_swift_1columncomponentedit"> 41 <div class="py-0" data-col-size="12"> 42 <div class="mt-2" > 43 <div class="h-100 item_swift_feature"> 44 <a href="@FleksyBuyNowPayLater_pageId" class="text-decoration-none d-block h-100"> 45 <div class="d-flex flex-row gap-3 h-100 text-end align-items-center"> 46 <div class="d-flex flex-column flex-grow-1"> 47 <h3 class="h6 buyNowPayLaterText m-0" >@Translate("Buy now Pay later")</h3> 48 </div> 49 <div class="flex-grow-0"> 50 <div class="icon-auto text-inherit opacity-100" style="width: 80px;">@ReadFile("/Files/icons/Fleksylogo.svg")</div> 51 </div> 52 </div> 53 </a> 54 </div> 55 </div> 56 </div> 57 </div> 58 } 59 60 <div class=" theme superhomeredfonts item_swift_1columncomponentedit"> 61 <div class="py-0" data-col-size="12"> 62 <div class="mt-2" style="float:right;"> 63 <div class="h-100 item_swift_feature"> 64 <button id="shareBtn_@product.Number" class="productShareButton"> 65 <div class="icon-3" style="z-index: 1"> 66 @ReadFile("Files/Icons/" + "share-red.svg") 67 </div> 68 @Translate("Share") 69 </button> 70 71 <p id="shareResult_@product.Number"></p> 72 </div> 73 </div> 74 </div> 75 </div> 76 77 78 79 <script> 80 81 document.addEventListener("DOMContentLoaded", () => { 82 83 const btnShare = document.getElementById("shareBtn_@product.Number"); 84 85 if (!btnShare) return; 86 87 btnShare.addEventListener("click", async () => { 88 89 const productNameNT = 90 @System.Text.Json.JsonSerializer.Serialize(product.Name); 91 92 const shareData = { 93 title: productNameNT, 94 text: productNameNT, 95 url: window.location.href 96 }; 97 98 try { 99 100 if (navigator.share) { 101 102 await navigator.share(shareData); 103 104 } else { 105 106 await navigator.clipboard.writeText(window.location.href); 107 108 alert("Link copied to clipboard"); 109 } 110 111 } catch (err) { 112 113 console.error("Share failed:", err); 114 115 try { 116 await navigator.clipboard.writeText(window.location.href); 117 alert("Link copied to clipboard"); 118 } catch (clipboardErr) { 119 console.error("Clipboard failed:", clipboardErr); 120 } 121 } 122 }); 123 124 }); 125 126 </script> 127 128 @*CS NT... 129 <div class="mt-2" style="float:right;"> 130 <button id="shareBtn_@product.Number" class="productShareButton"><div class="icon-2" style="z-index: 1">@ReadFile("Files/Icons/" + "share-red.svg")</div> @Translate("Share")</button> 131 <p id="shareResult_@product.Number"></p> 132 </div> 133 134 <script> 135 136 const btn_share = document.getElementById("shareBtn_@product.Number"); 137 //const resultPara = document.getElementById("shareResult_@product.Number"); 138 139 140 141 btn_share.addEventListener("click", async () => { 142 try { 143 //const blob = await fetch('@imageLinkPath_NT').then(r => r.blob()); 144 //const file = new File([blob], 'image.jpg', { type: blob.type }); 145 146 // Safe JS string using Json.Encode 147 const productNameNT = @System.Text.Json.JsonSerializer.Serialize(product.Name); 148 149 await navigator.share({ 150 //files: [file], 151 title: productNameNT, 152 text: productNameNT, 153 url: window.location.href 154 }); 155 156 } catch (err) { 157 console.error(err); 158 } 159 }); 160 161 /* 162 163 // Share must be triggered by "user activation" 164 btn_share.addEventListener("click", async () => { 165 try { 166 167 const blob = await fetch('@imageLinkPath_NT').then(r=>r.blob()) 168 169 const shareData = { 170 files: [ 171 new File([blob], 'image.jpg', { 172 type: blob.type, 173 }), 174 ], 175 title: "@product.Name", 176 text: `@product.Name\n${window.location.href}`, 177 url: window.location.href, 178 }; 179 180 181 await navigator.share(shareData); 182 //resultPara.textContent = "Product shared successfully"; 183 184 185 } catch (err) { 186 //resultPara.textContent = 'Error: ${err}'; 187 } 188 }); 189 */ 190 191 </script> 192 ...*@ 193 } 194 195 196 197 @if (product is object && !Model.Item.GetBoolean("HideProductNumber")) 198 { 199 string horizontalAlign = Model.Item.GetRawValueString("HorizontalAlignment", ""); 200 horizontalAlign = horizontalAlign == "center" ? "text-center" : horizontalAlign; 201 horizontalAlign = horizontalAlign == "end" ? "text-end" : horizontalAlign; 202 203 <div class="fs-7 opacity-85 @horizontalAlign item_@Model.Item.SystemName.ToLower()" itemprop="sku">@product.Number</div> 204 } 205 else if (Pageview.IsVisualEditorMode) 206 { 207 <div class="alert alert-warning">@Translate("No products available")</div> 208 } 209
Error executing template "Designs/Swift/Paragraph/DiscountExplanation.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_dc48095f9e474e59bce3fbfb6268dfa5.Execute() in D:\dynamicweb.net\Solutions\Nextech\superhome.cloud.dynamicweb-cms.com\Files\Templates\Designs\Swift\Paragraph\DiscountExplanation.cshtml:line 29
   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 ProductViewModel product = null; 6 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 7 { 8 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 9 } 10 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 11 { 12 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 13 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 14 15 if (productList?.Products is object) 16 { 17 product = productList.Products[0]; 18 } 19 } 20 21 22 23 //CS NT.. 24 25 string DiscountExplanation_Text = "";//"Buy 30 euros worth of products and get this for"; 26 string DiscountExplanation_percentage = "";//"€ 5.25"; 27 28 string DiscountExplanation = ""; 29 foreach (var p in product.ProductFields) 30 { 31 if (p.Key == "DiscountExplanation" && p.Value != null && !string.IsNullOrWhiteSpace(p.Value.ToString())) 32 { 33 DiscountExplanation = p.Value.ToString(); 34 DiscountExplanation = DiscountExplanation.Trim().Replace("±","€").Replace("�","€"); 35 // split based on $ character DiscountExplanation_Text 36 var DiscountExplanation_Unsplitted = DiscountExplanation.Split("$".ToCharArray()); 37 if (DiscountExplanation_Unsplitted.Length == 2) 38 { 39 if (!string.IsNullOrWhiteSpace(DiscountExplanation_Unsplitted[0])) 40 { 41 DiscountExplanation_Text = DiscountExplanation_Unsplitted[0]; 42 } 43 if (!string.IsNullOrWhiteSpace(DiscountExplanation_Unsplitted[1])) 44 { 45 DiscountExplanation_percentage = DiscountExplanation_Unsplitted[1]; 46 } 47 DiscountExplanation_Text = Pageview.Area.CultureInfo.TwoLetterISOLanguageName == "el" ? Translate(DiscountExplanation_Text.Replace("+"," ")) : DiscountExplanation_Text; 48 DiscountExplanation_Text += " <span class='discountExplanationPercentage_Span'>€" + DiscountExplanation_percentage + "</span>"; 49 } 50 else 51 { 52 //DiscountExplanation_Text = DiscountExplanation; 53 DiscountExplanation_Text = Pageview.Area.CultureInfo.TwoLetterISOLanguageName == "el" ? Translate(DiscountExplanation.Replace("+"," ")) : DiscountExplanation; 54 } 55 break; 56 } 57 } 58 59 60 61 bool isProductDetailsPage = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("ProductID")) ? true : false; 62 //bool isProductDetailsPage = Dynamicweb.Context.Current.Request.QueryString.AllKeys.Contains("ProductID"); 63 } 64 65 66 @if (product is object) 67 { 68 string fontsize_style = "font-size:" + Model.Item.GetRawValueString("Font_Size", "12px") + ";"; 69 70 if(isProductDetailsPage) 71 { 72 if(!string.IsNullOrWhiteSpace(DiscountExplanation_Text)) 73 { 74 <div class="DiscountExplanation_Container_productPage"> 75 <div style="width:100%" class="DiscountExplanation" style="@fontsize_style">@DiscountExplanation_Text </div> 76 </div> 77 } 78 } 79 else 80 { 81 <div class="DiscountExplanation_Container_grid"> 82 @if(!string.IsNullOrWhiteSpace(DiscountExplanation_Text)) 83 { 84 <div style="width:100%" class="DiscountExplanation" style="@fontsize_style">@DiscountExplanation_Text </div> 85 } 86 </div> 87 } 88 89 } 90 else if (Pageview.IsVisualEditorMode) 91 { 92 <div class="alert alert-dark m-0" role="alert"> 93 <span>@Translate("No products available")</span> 94 </div> 95 }
Error executing template "Designs/Swift/Paragraph/ProductBrand.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_a3709293a14c4d4c986882296938a80b.Execute() in D:\dynamicweb.net\Solutions\Nextech\superhome.cloud.dynamicweb-cms.com\Files\Templates\Designs\Swift\Paragraph\ProductBrand.cshtml:line 22
   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 ProductViewModel product = null; 6 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 7 { 8 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 9 } 10 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 11 { 12 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 13 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 14 15 if (productList?.Products is object) 16 { 17 product = productList.Products[0]; 18 } 19 } 20 21 string brand = ""; 22 foreach (var p in product.ProductFields) 23 { 24 if (p.Key == "Brand" && p.Value != null) 25 { 26 brand = p.Value.ToString(); 27 break; 28 } 29 } 30 31 32 //CS NT 33 string productsUrl = "/Default.aspx?ID=" + GetPageIdByNavigationTag("Shop"); 34 35 36 bool isProductDetailsPage = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("ProductID")) ? true : false; 37 //bool isProductDetailsPage = Dynamicweb.Context.Current.Request.QueryString.AllKeys.Contains("ProductID"); 38 39 } 40 41 @if (product is object) { 42 43 string fontsize = Model.Item.GetRawValueString("Font_Size", "1.25rem"); 44 string brandFontSize = Model.Item.GetRawValueString("BrandSize", "fs-2"); 45 string horizontalAlign = Model.Item.GetRawValueString("HorizontalAlignment", ""); 46 string layout = Model.Item.GetRawValueString("Layout", "horizontal"); 47 string textAlign = horizontalAlign == "center" ? "text-center" : string.Empty; 48 textAlign = horizontalAlign == "end" ? "text-end" : textAlign; 49 50 51 horizontalAlign = horizontalAlign == "center" && layout == "horizontal" ? "justify-content-center" : horizontalAlign; 52 horizontalAlign = horizontalAlign == "end" && layout == "horizontal" ? "justify-content-end" : horizontalAlign; 53 horizontalAlign = horizontalAlign == "center" && layout == "vertical" ? "align-items-center" : horizontalAlign; 54 horizontalAlign = horizontalAlign == "end" && layout == "vertical" ? "align-items-end" : horizontalAlign; 55 56 string flexDirection = layout == "horizontal" ? string.Empty : "flex-column"; 57 string flexGap = layout == "horizontal" ? "gap-3" : string.Empty; 58 string order = layout == "horizontal" ? string.Empty : "order-2"; 59 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? "theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 60 theme = GetViewParameter("theme") != null ? GetViewParameterString("theme") : theme; 61 62 string contentPadding = Model.Item.GetRawValueString("ContentPadding", ""); 63 contentPadding = contentPadding == "none" ? "p-0" : contentPadding; 64 contentPadding = contentPadding == "small" ? "p-1 px-md-2 py-md-1" : contentPadding; 65 contentPadding = contentPadding == "large" ? "p-2 px-md-3 py-md-2" : contentPadding; 66 67 string productInfoFeed = ""; 68 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]); 69 if (isLazyLoadingForProductInfoEnabled) 70 { 71 if (Dynamicweb.Context.Current.Items.Contains("ProductInfoFeed")) 72 { 73 productInfoFeed = Dynamicweb.Context.Current.Items["ProductInfoFeed"]?.ToString(); 74 if (!string.IsNullOrEmpty(productInfoFeed)) 75 { 76 productInfoFeed = $"data-product-info-feed=\"{productInfoFeed}\""; 77 } 78 } 79 } 80 if (!string.IsNullOrEmpty(brand)) { 81 string brandUrl = productsUrl + "&Brand=[" + brand.Replace("&eacute;","-eacute").Replace("&","%26").Replace(" ","+").Replace("'","%27") + "]"; 82 <div class="@textAlign item_@Model.Item.SystemName.ToLower() brandHeightNT" data-product-id="@product.Id" data-variant-id="@product.VariantId" @productInfoFeed> 83 84 <!--<div class="@brandFontSize m-0 d-flex flex-wrap @flexDirection @flexGap @horizontalAlign" style="row-gap: 0 !important" itemprop="offers" itemscope itemtype="https://schema.org/Offer">--> 85 <div class="@brandFontSize m-0 d-flex flex-wrap @flexDirection @flexGap @horizontalAlign" style="row-gap: 0 !important" itemprop="offers" itemscope> 86 <!--<a href="/english/products?Brand=@brand" class="product-brand">--> 87 <a href="@brandUrl" class="product-brand"> 88 <span class="product-brand" style="font-size:@fontsize !important">@brand</span> 89 </a> 90 </div> 91 </div> 92 } else if(!isProductDetailsPage) 93 { 94 <div class="@textAlign item_@Model.Item.SystemName.ToLower() brandHeightNT" data-product-id="@product.Id" data-variant-id="@product.VariantId" @productInfoFeed> 95 96 <!--<div class="m-0 d-flex flex-wrap @flexDirection @flexGap @horizontalAlign" style="font-size:@fontsize !important row-gap: 0 !important" itemprop="offers" itemscope itemtype="https://schema.org/Offer">--> 97 <div class="m-0 d-flex flex-wrap @flexDirection @flexGap @horizontalAlign" style="font-size:@fontsize !important row-gap: 0 !important" itemprop="offers" itemscope> 98 <span class="product-brand"></span> 99 </div> 100 </div> 101 } 102 103 } 104 else if (Pageview.IsVisualEditorMode) 105 { 106 <div class="alert alert-dark m-0" role="alert"> 107 <span>@Translate("No products available")</span> 108 </div> 109 } 110
Error executing template "Designs/Swift/Paragraph/Swift_ProductAddToCart.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_bb57d2eb35fa49a09327307287f51e65.Execute() in D:\dynamicweb.net\Solutions\Nextech\superhome.cloud.dynamicweb-cms.com\Files\Templates\Designs\Swift\Paragraph\Swift_ProductAddToCart.cshtml:line 107
   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.Core.Encoders 4 @using NextechDWAddIn.Common 5 6 7 @{ 8 ProductViewModel product = null; 9 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 10 { 11 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 12 } 13 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 14 { 15 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 16 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 17 18 if (productList?.Products is object) 19 { 20 product = productList.Products[0]; 21 } 22 } 23 24 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", ""); 25 bool anonymousUser = Pageview.User == null; 26 bool isErpConnectionDown = !Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsWebServiceConnectionAvailable"]); 27 bool hideAddToCart = anonymousUsersLimitations.Contains("cart") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHideAddToCart") && isErpConnectionDown; 28 hideAddToCart = Pageview.IsVisualEditorMode ? false : hideAddToCart; 29 30 //CS NT PhoneOrder 31 bool isPhoneOrder = false; 32 if (product.ProductFields != null && product.ProductFields.Count > 0) 33 { 34 foreach (var pf in product.ProductFields) 35 { 36 if (pf.Key == "PhoneOrders" && pf.Value != null && !string.IsNullOrWhiteSpace(pf.Value.ToString())) 37 { 38 isPhoneOrder = pf.Value.ToString() == "1" ? true : false; 39 } 40 } 41 } 42 } 43 44 @if(product is object && isPhoneOrder) 45 { 46 47 string productId = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("ProductID")) ? Dynamicweb.Context.Current.Request.QueryString.Get("ProductID") : ""; 48 bool isProductDetailsPage = !string.IsNullOrEmpty(productId); 49 string divStyle = isProductDetailsPage ? "" : "text-align: center"; 50 <div class="w-100" style="@divStyle"> 51 <button id="PhoneOrderBtn" class="PhoneOrderBtnNT"><a href="tel:+77777545"> @Translate("Phone Order") <i class="fas fa-phone"></i></a></button> 52 </div> 53 } 54 else if (product is object && !hideAddToCart) { 55 string horizontalAlign = Model.Item.GetRawValueString("HorizontalAlignment", ""); 56 horizontalAlign = horizontalAlign == "center" ? "justify-content-center" : horizontalAlign; 57 horizontalAlign = horizontalAlign == "end" ? "justify-content-end" : horizontalAlign; 58 horizontalAlign = horizontalAlign == "full" ? "" : horizontalAlign; 59 60 bool favoritesSelector = !string.IsNullOrEmpty(Model.Item.GetString("ShowAddToFavorites")) ? Model.Item.GetBoolean("ShowAddToFavorites") : false; 61 bool quantitySelector = !string.IsNullOrEmpty(Model.Item.GetString("ShowQuantitySelector")) ? Model.Item.GetBoolean("ShowQuantitySelector") : false; 62 bool unitsSelector = !string.IsNullOrEmpty(Model.Item.GetString("ShowUnitsSelector")) ? Model.Item.GetBoolean("ShowUnitsSelector") : false; 63 bool hideInventory = !string.IsNullOrEmpty(Model.Item.GetString("HideInventory")) ? Model.Item.GetBoolean("HideInventory") : false; 64 bool hideStockState = !string.IsNullOrEmpty(Model.Item.GetString("HideStockState")) ? Model.Item.GetBoolean("HideStockState") : false; 65 66 string buttonSize = Model.Item.GetRawValueString("ButtonSize", "regular"); 67 string inputSize = string.Empty; 68 69 switch (buttonSize) 70 { 71 case "small": 72 inputSize = " input-group-sm"; 73 buttonSize = " btn-sm"; 74 break; 75 case "regular": 76 buttonSize = string.Empty; 77 break; 78 case "large": 79 inputSize = " input-group-lg"; 80 buttonSize = " btn-lg"; 81 break; 82 } 83 84 string iconPath = "/Files/icons/"; 85 string url = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("CartService")); 86 if (!url.Contains("LayoutTemplate")) 87 { 88 url += url.Contains("?") ? "&LayoutTemplate=Swift_MiniCart.cshtml" : "?LayoutTemplate=Swift_MiniCart.cshtml"; 89 } 90 91 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]); 92 string disableAddToCart = (product.StockLevel <= 0) ? "disabled" : ""; 93 bool isNeverOutOfStock = product.NeverOutOfstock; 94 disableAddToCart = isNeverOutOfStock && !isLazyLoadingForProductInfoEnabled ? "" : disableAddToCart; 95 96 string whenVariantsExist = Model.Item.GetRawValueString("WhenVariantsExist", "hide"); 97 98 string flexFill = Model.Item.GetRawValueString("HorizontalAlignment", "") == "full" ? "flex-fill" : ""; 99 string fullWidth = Model.Item.GetRawValueString("HorizontalAlignment", "") == "full" ? "w-100" : ""; 100 string addToCartIcon = Model.Item.GetRawValueString("Icon", iconPath + "shopping-cart.svg"); 101 string addToCartLabel = !addToCartIcon.Contains("_none") ? $"<span class=\"icon-2\">{ReadFile(addToCartIcon)}</span>" : ""; 102 addToCartLabel += !addToCartIcon.Contains("_none") && !Model.Item.GetBoolean("HideButtonText") ? " " : ""; 103 addToCartLabel += !Model.Item.GetBoolean("HideButtonText") ? $"<span class=\"d-none d-md-inline\">{Translate("Add to cart")}</span><span class=\"d-inline d-md-none\">{Translate("Add")}</span>" : ""; 104 105 bool userHasPendingQuote = Dynamicweb.Ecommerce.Common.Context.Cart != null && Dynamicweb.Ecommerce.Common.Context.Cart.IsQuote; 106 107 if (product.VariantInfo.VariantInfo == null || whenVariantsExist == "disable") { 108 string unitId = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("UnitId")) ? Dynamicweb.Context.Current.Request.Form.Get("UnitId") : product.DefaultUnitId; 109 if (string.IsNullOrEmpty(unitId) && product?.UnitOptions != null) { 110 if (product.UnitOptions.FirstOrDefault<UnitOptionViewModel>() != null) { 111 unitId = product.UnitOptions.FirstOrDefault<UnitOptionViewModel>().Id; 112 } 113 } 114 115 double? stepQty = product.PurchaseQuantityStep > 0 ? product.PurchaseQuantityStep : 1; 116 double? minQty = product.PurchaseMinimumQuantity > 0 ? product.PurchaseMinimumQuantity : 1; 117 double? valueQty = minQty > stepQty ? minQty : stepQty; 118 bool isStockType = product.ProductType == Dynamicweb.Ecommerce.Products.ProductType.Stock; 119 bool neverOutOfStock = product.NeverOutOfstock; 120 bool setMaxQty = isStockType && !neverOutOfStock; 121 //CS NT Hide max check. double? maxQty = setMaxQty ? product.StockLevel : null; 122 double? maxQty = null; 123 124 125 126 //CS NT..Google tag manager new fields 127 128 string itemCategory = ""; 129 string itemCategory2 = ""; 130 string itemCategory3 = ""; 131 string itemCategory4 = ""; 132 string itemCategory5 = ""; 133 var prodCategories = CommonFunctions.GetProductCategories(Dynamicweb.Ecommerce.Services.ProductGroups.GetGroup(product.PrimaryOrDefaultGroup.Id)); 134 if (prodCategories != null && prodCategories.Count >= 1) 135 { 136 prodCategories.Reverse(); 137 for (int i = 0; i < prodCategories.Count; i++) 138 { 139 if (i == 0) 140 { 141 itemCategory = prodCategories[i]; 142 } 143 else if (i == 1) 144 { 145 itemCategory2 = prodCategories[i]; 146 } 147 else if (i == 2) 148 { 149 itemCategory3 = prodCategories[i]; 150 } 151 else if (i == 3) 152 { 153 itemCategory4 = prodCategories[i]; 154 } 155 else if (i == 4) 156 { 157 itemCategory5 = prodCategories[i]; 158 } 159 } 160 } 161 string brand = ""; 162 if (product.ProductFields != null && product.ProductFields.Count > 0) 163 { 164 foreach (var p in product.ProductFields) 165 { 166 if (p.Key == "Brand" && p.Value != null) 167 { 168 brand = p.Value.ToString(); 169 } 170 } 171 } 172 173 //..CS NT 174 175 176 177 if (unitsSelector && product.UnitOptions.Count > 0) { 178 <form method="post" action="/Default.aspx?ID=@(Pageview.Page.ID)&ProductId=@product.Id" id="UnitSelectorForm_@(product.Id)_@(product.VariantId.Replace(".", "_"))_@Model.ID"> 179 <input type="hidden" name="redirect" value="false"> 180 <input type="hidden" name="VariantID" value="@product.VariantId"> 181 <input type="hidden" name="UnitID" class="js-unit-id" value="@unitId"> 182 </form> 183 } 184 185 <div class="d-flex @horizontalAlign @fullWidth js-input-group item_@Model.Item.SystemName.ToLower()"> 186 <form method="post" action="@url" class="@fullWidth" style="z-index: 1"> 187 <input type="hidden" name="redirect" value="false"> 188 <input type="hidden" name="ProductId" value="@product.Id"> 189 <input type="hidden" name="ProductName" value="@HtmlEncoder.HtmlEncode(product.Name)"> 190 <input type="hidden" name="ProductVariantName" value="@product.VariantName"> 191 <input type="hidden" name="ProductCurrency" value="@Dynamicweb.Ecommerce.Common.Context.Currency.Code"> 192 <input type="hidden" name="ProductPrice" value="@PriceViewModelExtensions.ToStringInvariant(product.Price)"> 193 <input type="hidden" name="ProductReferer" value="component_ProductAddToCart"> 194 <input type="hidden" name="cartcmd" value="add"> 195 196 @*CS NT..*@ 197 <input type="hidden" name="brand" value="@brand"> 198 <input type="hidden" name="itemCategory" value="@itemCategory"> 199 <input type="hidden" name="itemCategory2" value="@itemCategory2"> 200 <input type="hidden" name="itemCategory3" value="@itemCategory3"> 201 <input type="hidden" name="itemCategory4" value="@itemCategory4"> 202 <input type="hidden" name="itemCategory5" value="@itemCategory5"> 203 @*..CS NT*@ 204 205 <input type="submit" class="d-none" onclick="event.preventDefault(); swift.Cart.Update(event)"> @* Fix for enterKey should not redirect to minicart page *@ 206 207 @if (!string.IsNullOrEmpty(product.VariantId)) 208 { 209 <input type="hidden" name="VariantId" value="@product.VariantId"> 210 } 211 212 <template class="js-step-quantity-warning"> 213 <div class="modal-header"> 214 <h1 class="modal-title fs-5">@Translate("The quantity is not valid")</h1> 215 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 216 </div> 217 <div class="modal-body"> 218 @Translate("Please select a quantity that is dividable by") @stepQty 219 </div> 220 </template> 221 222 223 <template class="js-min-quantity-warning"> 224 <div class="modal-header"> 225 <h1 class="modal-title fs-5">@Translate("The product could not be added to the cart")</h1> 226 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 227 </div> 228 <div class="modal-body"> 229 @Translate("The quantity is not valid. You must buy at least") @product.PurchaseMinimumQuantity 230 </div> 231 </template> 232 233 234 @if (userHasPendingQuote) 235 { 236 <input type="hidden" name="PendingQuote" value="true"> 237 238 <template class="js-pending-quote-notice"> 239 <div class="modal-header"> 240 <h1 class="modal-title fs-5">@Translate("Pending Quote")</h1> 241 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="@Translate("Close")"></button> 242 </div> 243 <div class="modal-body"> 244 @Translate("You need to complete your current quote or empty the cart before adding this product to cart.") 245 </div> 246 </template> 247 } 248 249 @if (quantitySelector || (!anonymousUser && product.VariantInfo.VariantInfo != null) || (!anonymousUser && favoritesSelector)) 250 { 251 <input type="hidden" id="Unit_@(product.Id)_@product.VariantId.Replace(".", "_")" name="UnitID" value="@unitId" /> 252 } 253 254 <div class="d-flex flex-row w-100"> 255 @if (!quantitySelector) 256 { 257 <input id="Quantity_@(product.Id)_@product.VariantId.Replace(".", "_")" class="swift_quantity_field" name="Quantity" value="@valueQty" type="hidden" @disableAddToCart> 258 } 259 260 @if (unitsSelector && product.UnitOptions.Count > 0) 261 { 262 string selectedUnitName = !string.IsNullOrEmpty(unitId) && product?.UnitOptions != null ? unitId : product.UnitOptions.FirstOrDefault<UnitOptionViewModel>().Name; 263 264 foreach (var unitOption in product.UnitOptions) 265 { 266 if (unitOption.Id == unitId) 267 { 268 selectedUnitName = unitOption.Name; 269 } 270 } 271 272 <div class="d-flex flex-column gap-2 w-100"> 273 <div class="input-group input-primary-button-group flex-nowrap@(inputSize)"> 274 @if (!anonymousUser && favoritesSelector) 275 { 276 @RenderPartial("Components/ToggleFavorite.cshtml", product) 277 } 278 279 @if (quantitySelector) 280 { 281 <input id="Quantity_@(product.Id)_@product.VariantId.Replace(".", "_")" name="Quantity" value="@valueQty" step="@stepQty" min="@minQty" max="@maxQty" class="form-control swift_quantity-field" style="min-width: 60px; max-width: 100px; z-index: 1" type="number" @disableAddToCart> 282 } 283 284 <button class="btn btn-secondary @flexFill dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false"> 285 @selectedUnitName 286 </button> 287 288 <ul class="dropdown-menu swift_unit-field"> 289 @foreach (var unitOption in product.UnitOptions) 290 { 291 var selectedUnit = unitOption.Id == unitId ? "selected" : ""; 292 293 <li> 294 <button type="button" class="btn dropdown-item" data-value="@unitOption.Id" onclick="document.querySelector('#UnitSelectorForm_@(product.Id)_@(product.VariantId.Replace(".", "_"))_@Model.ID').querySelector('.js-unit-id').value = this.getAttribute('data-value'); 295 document.querySelector('#Unit_@(product.Id)_@product.VariantId.Replace(".", "_")').value = this.getAttribute('data-value'); 296 swift.PageUpdater.Update(document.querySelector('#UnitSelectorForm_@(product.Id)_@(product.VariantId.Replace(".", "_"))_@Model.ID'))"> 297 <span>@unitOption.Name</span> 298 <span> 299 @if (unitOption.StockLevel > 0 || unitOption.NeverOutOfStock) 300 { 301 if (!Model.Item.GetBoolean("HideInventory") && !unitOption.NeverOutOfStock) 302 { 303 <span class="small text-success">@unitOption.StockLevel @Translate("In stock")</span> 304 } 305 else 306 { 307 <span class="small text-success">@Translate("In stock")</span> 308 } 309 } 310 else 311 { 312 <span class="small text-danger">@Translate("Out of Stock")</span> 313 } 314 </span> 315 </button> 316 </li> 317 } 318 </ul> 319 </div> 320 <button type="button" onclick="swift.Cart.Update(event)" class="btn btn-primary @(buttonSize) js-add-to-cart-button" style="white-space: nowrap" @disableAddToCart title="@Translate("Add to cart")" id="AddToCartButton@(product.Id)_@Pageview.CurrentParagraph.ID"> 321 @if (!Model.Item.GetBoolean("HideButtonText")) 322 { 323 <span class="text-nowrap d-flex align-items-center justify-content-center gap-2"> 324 @addToCartLabel 325 </span> 326 } 327 else 328 { 329 @addToCartLabel 330 } 331 </button> 332 </div> 333 } 334 else 335 { 336 if (!anonymousUser && favoritesSelector) 337 { 338 @RenderPartial("Components/ToggleFavorite.cshtml", product) 339 } 340 341 <div class="input-group input-primary-button-group flex-nowrap@(inputSize)"> 342 @if (quantitySelector) 343 { 344 <input id="Quantity_@(product.Id)_@product.VariantId.Replace(".", "_")" name="Quantity" value="@valueQty" step="@stepQty" min="@minQty" max="@maxQty" class="form-control swift_quantity-field" style="min-width: 60px; max-width: 100px; z-index: 1" type="number" @disableAddToCart> 345 } 346 347 <button type="button" onclick="swift.Cart.Update(event)" class="btn btn-primary @(buttonSize) @flexFill js-add-to-cart-button" style="white-space: nowrap" @disableAddToCart title="@Translate("Add to cart")" id="AddToCartButton@(product.Id)_@Pageview.CurrentParagraph.ID"> 348 @if (!Model.Item.GetBoolean("HideButtonText")) 349 { 350 <span class="text-nowrap d-flex align-items-center justify-content-center gap-2"> 351 @addToCartLabel 352 </span> 353 } 354 else 355 { 356 @addToCartLabel 357 } 358 </button> 359 </div> 360 } 361 </div> 362 </form> 363 </div> 364 } else if (whenVariantsExist == "modal") { 365 string ButtonShape = Model.Item.GetRawValueString("VariantButtonShape", "square"); 366 string buttonAspectRatio = Model.Item.GetRawValueString("VariantImageAspectRatio", "56%"); 367 368 string buttonText = Translate("Select"); 369 string variantId = !string.IsNullOrWhiteSpace(product.VariantId) ? product.VariantId : product.DefaultVariantId; 370 371 string variantSelectorServicePageId = !string.IsNullOrEmpty(Model.Item.GetString("VariantSelectorServicePageId")) ? Model.Item.GetLink("VariantSelectorServicePageId").PageId.ToString() : ""; 372 variantSelectorServicePageId = variantSelectorServicePageId != "" ? variantSelectorServicePageId : GetPageIdByNavigationTag("VariantSelectorService").ToString(); 373 374 <div class="d-flex @horizontalAlign w-100 item_@Model.Item.SystemName.ToLower()"> 375 @if (!anonymousUser && favoritesSelector) 376 { 377 @RenderPartial("Components/ToggleFavorite.cshtml", product) 378 } 379 <form action="/Default.aspx?ID=@variantSelectorServicePageId" data-response-target-element="DynamicModalContent" data-preloader="inline" style="z-index: 1" class="@fullWidth"> 380 <input type="hidden" name="ProductID" value="@product.Id"> 381 <input type="hidden" name="VariantID" value="@variantId"> 382 <input type="hidden" name="QuantitySelector" value="@quantitySelector.ToString()"> 383 <input type="hidden" name="HideInventory" value="@hideInventory.ToString()"> 384 <input type="hidden" name="HideStockState" value="@hideStockState.ToString()"> 385 <input type="hidden" name="ButtonLayout" value="@ButtonShape"> 386 <input type="hidden" name="ButtonAspectRatio" value="@buttonAspectRatio"> 387 <input type="hidden" name="VariantSelectorServicePage" value="@variantSelectorServicePageId"> 388 <input type="hidden" name="ViewType" value="ModalContent"> 389 @if (isLazyLoadingForProductInfoEnabled) 390 { 391 @* If lazy loading is enabled, bypass it because we're loading a modal window, so render everything as if it was server-side *@ 392 <input type="hidden" name="getproductinfo" value="true"> 393 } 394 <button type="button" onclick="swift.PageUpdater.Update(event)" class="btn btn-primary@(buttonSize) @fullWidth" title="@Translate("Select")" data-bs-toggle="modal" data-bs-target="#DynamicModal" id="OpenVariantSelectorModal@(product.Id)_@Pageview.CurrentParagraph.ID">@buttonText</button> 395 </form> 396 </div> 397 } 398 } else if (Pageview.IsVisualEditorMode) { 399 <div class="alert alert-dark m-0">@Translate("No products available")</div> 400 } 401
Out of stock
Error executing template "Designs/Swift/Paragraph/Swift_BackInStockNotification.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_f5789991974646b7bc9ccf2b4af949c7.Execute() in D:\dynamicweb.net\Solutions\Nextech\superhome.cloud.dynamicweb-cms.com\Files\Templates\Designs\Swift\Paragraph\Swift_BackInStockNotification.cshtml:line 53
   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 System.Linq 3 @using Dynamicweb.Ecommerce 4 @using Dynamicweb.Ecommerce.ProductCatalog 5 @using Dynamicweb.Ecommerce.Products 6 @using Dynamicweb.Ecommerce.Stocks 7 8 @functions 9 { 10 private static bool BackInStockRegisteredForUser(ProductViewModel product, string unitId = "", long stocklocationId = 0) 11 { 12 if (!Dynamicweb.Security.UserManagement.User.IsExtranetUserLoggedIn()) return false; 13 14 Product productObject = Services.Products.GetProductById(product.Id, product.VariantId, product.LanguageId); 15 StockLocation stockLocation = Services.StockService.GetStockLocation(stocklocationId); 16 double unitStock = productObject.GetUnitStock(stockLocation, unitId); 17 return unitStock <= 0d && ProductBackInStockNotification.BackInStockNotificationExists(productObject, unitId); 18 } 19 } 20 21 @{ 22 ProductViewModel product = null; 23 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 24 { 25 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 26 } 27 else if (Pageview.Page.Item["DummyProduct"] != null) 28 { 29 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 30 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 31 32 if (productList?.Products is object) 33 { 34 product = productList.Products[0]; 35 } 36 } 37 38 //CS NT.. 39 40 string productId = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("ProductID")) ? Dynamicweb.Context.Current.Request.QueryString.Get("ProductID") : ""; 41 bool isProductDetailsPage = !string.IsNullOrEmpty(productId); 42 43 //..CS NT 44 } 45 46 @if ((product is object && product.ProductType == Dynamicweb.Ecommerce.Products.ProductType.Stock)) 47 { 48 bool productInStock = product.StockLevel > 0 || product.NeverOutOfstock; 49 50 if (!productInStock) 51 { 52 string notifiedMessage = Model.Item.GetString("NotifiedMessage"); 53 string notifiedMessageId = $"NotifiedMessage_item_{Model.Item.SystemName.ToLower()}_{product.Id}_{product.VariantId.Replace(".", "_")}"; 54 55 string horizontalAlign = Model.Item.GetRawValueString("HorizontalAlignment", ""); 56 horizontalAlign = horizontalAlign == "center" ? "justify-content-center" : horizontalAlign; 57 horizontalAlign = horizontalAlign == "end" ? "justify-content-end" : horizontalAlign; 58 horizontalAlign = horizontalAlign == "full" ? "" : horizontalAlign; 59 60 string flexFill = Model.Item.GetRawValueString("HorizontalAlignment", "") == "full" ? "flex-fill" : ""; 61 string fullWidth = Model.Item.GetRawValueString("HorizontalAlignment", "") == "full" ? "w-100" : ""; 62 63 bool notificationRegisteredForUser = BackInStockRegisteredForUser(product); 64 65 <div class="d-flex flex-row w-100 @horizontalAlign @fullWidth item_@Model.Item.SystemName.ToLower()"> 66 <div class="@horizontalAlign @fullWidth @(notificationRegisteredForUser ? "" : "d-none")" id="@notifiedMessageId"> 67 @notifiedMessage 68 </div> 69 70 @if (!notificationRegisteredForUser) 71 { 72 73 string notifyIcon = Model.Item.GetString("Icon"); 74 string notifyLabel = !notifyIcon.Contains("_none") ? "<span class=\"icon-2\">" + ReadFile(notifyIcon) + "</span>" : ""; 75 76 if(isProductDetailsPage)//CS NT 77 { 78 notifyLabel += " " + Translate("Notify me when available"); 79 } 80 else 81 { 82 notifyLabel += " " + Translate("Notify me"); 83 } 84 85 string buttonSize = Model.Item.GetRawValueString("ButtonSize", "regular"); 86 string inputSize = string.Empty; 87 88 switch (buttonSize) 89 { 90 case "small": 91 inputSize = " input-group-sm"; 92 buttonSize = " btn-sm"; 93 break; 94 case "regular": 95 buttonSize = string.Empty; 96 break; 97 case "large": 98 inputSize = " input-group-lg"; 99 buttonSize = " btn-lg"; 100 break; 101 } 102 103 string modalId = $"modal_item_{Model.Item.SystemName.ToLower()}_{product.Id}_{product.VariantId.Replace(".", "_")}"; 104 string formId = $"NotificationForm_item_{Model.Item.SystemName.ToLower()}_{product.Id}_{product.VariantId.Replace(".", "_")}"; 105 string notificationButtonId = $"NotificationButton_item_{Model.Item.SystemName.ToLower()}_{product.Id}_{product.VariantId.Replace(".", "_")}"; 106 string notificationOnClick = !Pageview.IsVisualEditorMode ? $"swift.BackInStockNotification.SubmitNotification('{formId}', '{modalId}', '{notifiedMessageId}', '{notificationButtonId}')" : ""; 107 108 string productPage = $"Default.aspx?ID={GetPageIdByNavigationTag("ProductDetailPage")}"; 109 110 <div class="d-flex @horizontalAlign @fullWidth js-input-group"> 111 <div class="input-group input-primary-button-group flex-nowrap@(inputSize)"> 112 <button type="button" class="btn btn-primary @(buttonSize) @flexFill" id="@notificationButtonId" data-bs-toggle="modal" data-bs-target="#@modalId"> 113 <span class="text-nowrap d-flex align-items-center justify-content-center gap-2"> 114 @notifyLabel 115 </span> 116 </button> 117 </div> 118 </div> 119 120 <div class="modal fade" id="@modalId" tabindex="-1" aria-labelledby="label_@modalId" aria-hidden="true"> 121 <div class="modal-dialog modal-dialog-centered"> 122 <div class="modal-content"> 123 <div class="modal-header"> 124 <h5 class="modal-title" id="label_@modalId">@Translate("Back in Stock")</h5> 125 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="@Translate("Close")"></button> 126 </div> 127 <div class="modal-body"> 128 <form name="@product.Id" id="@formId" method="post" action="@productPage"> 129 <input type="hidden" name="ProductID" value="@product.Id" /> 130 <input type="hidden" name="VariantID" value="@product.VariantId" /> 131 <input type="hidden" name="LanguageID" value="@product.LanguageId" /> 132 <input type="hidden" name="CartCmd" value="createnotificationforthisproduct" /> 133 <div class="m-2"> 134 @Translate("Send me an email when the product is in stock.") 135 </div> 136 <div class="form-floating m-2"> 137 <input class="form-control" type="text" id="NotificationEmail" value="@(Pageview.User != null ? Pageview.User.Email : "")" name="NotificationEmail" placeholder="@Translate("Email")" required="required" aria-required="aria-required" /> 138 <label for="NotificationEmail" class="form-label">@Translate("Email")</label> 139 </div> 140 </form> 141 </div> 142 <div class="modal-footer"> 143 <button type="button" class="btn btn-primary" onclick="@notificationOnClick">@Translate("Submit")</button> 144 </div> 145 </div> 146 </div> 147 </div> 148 } 149 </div> 150 } 151 } 152 else if (Pageview.IsVisualEditorMode) 153 { 154 <div class="alert alert-dark m-0">@Translate("Back in stock notification will be shown here")</div> 155 } 156
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_c88582db0e654e589ef50804ebb34079.Execute() in D:\dynamicweb.net\Solutions\Nextech\superhome.cloud.dynamicweb-cms.com\Files\Templates\Designs\Swift\Paragraph\Swift_ProductDetailsGallery.cshtml:line 133
   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.Text.RegularExpressions; 6 7 @functions { 8 public ProductViewModel product { get; set; } = new ProductViewModel(); 9 public string galleryLayout { get; set; } 10 public string[] supportedImageFormats { get; set; } 11 public string[] supportedVideoFormats { get; set; } 12 public string[] supportedDocumentFormats { get; set; } 13 public string[] allSupportedFormats { get; set; } 14 15 public class RatioSettings { 16 public string Ratio { get; set; } 17 public string CssClass { get; set; } 18 public string CssVariable { get; set; } 19 public string Fill { get; set; } 20 } 21 22 public RatioSettings GetRatioSettings(string size = "desktop") { 23 var ratioSettings = new RatioSettings(); 24 25 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 26 ratio = ratio != "0" ? ratio : ""; 27 string cssClass = ratio != "" && ratio != "fill" ? " ratio" : ""; 28 string cssVariable = ratio != "" && ratio != "fill" ? "--bs-aspect-ratio: " + ratio : ""; 29 cssClass = ratio != "" && ratio == "fill" && size == "mobile" ? " ratio" : cssClass; 30 cssVariable = ratio != "" && ratio == "fill" && size == "mobile" ? "--bs-aspect-ratio: 66%" : cssVariable; 31 32 ratioSettings.Ratio = ratio; 33 ratioSettings.CssClass = cssClass; 34 ratioSettings.CssVariable = cssVariable; 35 ratioSettings.Fill = ratio == "fill" ? " h-100" : ""; 36 37 return ratioSettings; 38 } 39 40 public string GetColumnClass(int total, int assetNumber) { 41 string colClass = total > 1 ? "g-col-lg-6" : "g-col-12"; 42 colClass = galleryLayout == "full-first" && assetNumber == 0 ? "g-col-12" : colClass; 43 colClass = galleryLayout == "full-last" && assetNumber == (total - 1) ? "g-col-12" : colClass; 44 colClass = galleryLayout == "advanced-grid" && assetNumber > 1 ? "g-col-4" : colClass; 45 46 colClass = galleryLayout == "advanced-grid" && total == 1 ? "g-col-12" : colClass; 47 colClass = galleryLayout == "advanced-grid" && total == 3 && assetNumber == 2 ? "g-col-12" : colClass; 48 colClass = galleryLayout == "advanced-grid" && total == 4 && assetNumber == 2 ? "g-col-6" : colClass; 49 colClass = galleryLayout == "advanced-grid" && total == 4 && assetNumber == 3 ? "g-col-6" : colClass; 50 colClass = galleryLayout == "advanced-grid" && total == 6 && assetNumber == 5 ? "g-col-12" : colClass; 51 colClass = galleryLayout == "advanced-grid" && total == 7 && assetNumber == 5 ? "g-col-6" : colClass; 52 colClass = galleryLayout == "advanced-grid" && total == 7 && assetNumber == 6 ? "g-col-6" : colClass; 53 colClass = galleryLayout == "advanced-grid" && total == 9 && assetNumber == 8 ? "g-col-12" : colClass; 54 55 return colClass; 56 } 57 58 public string GetArrowsColor() 59 { 60 var invertColor = Model.Item.GetBoolean("InvertModalArrowsColor"); 61 var arrowsColor = invertColor ? " carousel-dark" : string.Empty; 62 return arrowsColor; 63 } 64 65 public Dictionary<string, object> GetVideoParams(MediaViewModel asset, string size) 66 { 67 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Name; 68 string type = GetVideoType(asset.Value); 69 bool openInModal = Model.Item.GetString("OpenVideoInModal") == "true" ? true : false; 70 bool autoPlay = Model.Item.GetBoolean("VideoAutoPlay"); 71 72 var videoParams = new Dictionary<string, object>(); 73 videoParams.Add("AssetName", asset.Name); 74 videoParams.Add("AssetVideoType", type); 75 videoParams.Add("AssetDisplayName", asset.DisplayName); 76 videoParams.Add("OpenVideoInModal", openInModal); 77 videoParams.Add("VideoAutoPlay", autoPlay); 78 videoParams.Add("Size", size); 79 videoParams.Add("Id", Model.ID); 80 return videoParams; 81 } 82 83 public string GetVideoType(string assetValue) 84 { 85 string type = assetValue.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "youtube" : string.Empty; 86 type = assetValue.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "vimeo" : type; 87 type = string.IsNullOrEmpty(type) ? "selfhosted" : type; 88 return type; 89 } 90 91 public string GetYoutubeScreenDump(string assetValue, string quality) 92 { 93 var regex = new Regex(@"(?:youtube\.com\/.*[\?&]v=|youtu\.be\/)([\w-]+)"); 94 Match match = regex.Match(assetValue); 95 string videoId = match.Success ? match.Groups[1].Value : string.Empty; 96 string youtubeThumbnail = $"https://img.youtube.com/vi/{videoId}/{quality}.jpg"; 97 return youtubeThumbnail; 98 } 99 } 100 101 @{ 102 @* Get the product data *@ 103 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 104 { 105 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 106 } 107 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 108 { 109 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 110 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 111 112 if (productList?.Products is object) 113 { 114 product = productList.Products[0]; 115 } 116 } 117 } 118 119 @if (product is object) 120 { 121 @* Supported formats *@ 122 supportedImageFormats = new string[] { ".jpg", ".jpeg", ".webp", ".png", ".gif", ".bmp", ".tiff" }; 123 supportedVideoFormats = new string[] { "youtu.be", "youtube", "vimeo", ".mp4", ".webm" }; 124 supportedDocumentFormats = new string[] { ".pdf", ".docx", ".xlsx", ".ppt", "pptx" }; 125 allSupportedFormats = supportedImageFormats.Concat(supportedVideoFormats).Concat(supportedDocumentFormats).ToArray(); 126 127 @* Collect the assets *@ 128 var selectedAssetCategories = Model.Item.GetList("ImageAssets")?.GetRawValue().OfType<string>(); 129 bool includeImagePatternImages = Model.Item.GetBoolean("ImagePatternImages"); 130 131 @* Needed image data collection to support both DefaultImage, ImagePatterns and Image Assets *@ 132 string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : ""; 133 IEnumerable<MediaViewModel> assetsImages = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets); 134 assetsImages = assetsImages.OrderByDescending(x => x.Value.Equals(defaultImage)); 135 IEnumerable<MediaViewModel> assetsList = new MediaViewModel[]{}; 136 assetsList = assetsList.Union(assetsImages); 137 assetsList = includeImagePatternImages ? assetsList.Union(product.ImagePatternImages) : assetsList; 138 assetsList = includeImagePatternImages && assetsList.Count() == 0 ? assetsList.Append(product.DefaultImage) : assetsList; 139 140 bool defaultImageFallback = Model.Item.GetBoolean("DefaultImageFallback"); 141 142 int totalAssets = 0; 143 foreach (MediaViewModel asset in assetsList) { 144 var assetValue = asset.Value; 145 foreach (string format in allSupportedFormats) { 146 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 147 totalAssets++; 148 } 149 } 150 } 151 152 if (totalAssets == 0) 153 { 154 if (defaultImageFallback) { 155 assetsList = new List<MediaViewModel>(){ product.DefaultImage }; 156 totalAssets = 1; 157 } else { 158 assetsList = new List<MediaViewModel>(){ }; 159 totalAssets = 0; 160 } 161 } 162 163 @* Layout settings *@ 164 string spacing = Model.Item.GetRawValueString("Spacing", "4"); 165 spacing = spacing == "none" ? "gap-0" : spacing; 166 spacing = spacing == "small" ? "gap-3" : spacing; 167 spacing = spacing == "large" ? "gap-4" : spacing; 168 169 galleryLayout = Model.Item.GetRawValueString("Layout", "grid"); 170 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 171 172 var badgeParms = new Dictionary<string, object>(); 173 badgeParms.Add("size", "h5"); 174 badgeParms.Add("saleBadgeType", Model.Item.GetRawValue("SaleBadgeType")); 175 badgeParms.Add("saleBadgeCssClassName", Model.Item.GetRawValue("SaleBadgeDesign")); 176 badgeParms.Add("newBadgeCssClassName", Model.Item.GetRawValue("NewBadgeDesign")); 177 badgeParms.Add("newPublicationDays", Model.Item.GetInt32("NewPublicationDays")); 178 badgeParms.Add("campaignBadgesValues", Model.Item.GetList("CampaignBadges")?.GetRawValue().OfType<string>().ToList()); 179 180 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("SaleBadgeDesign")) && Model.Item.GetRawValueString("SaleBadgeDesign") != "none" ? true : false; 181 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("NewBadgeDesign")) && Model.Item.GetRawValueString("NewBadgeDesign") != "none" ? true : false; 182 DateTime createdDate = product.Created.Value; 183 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false; 184 showBadges = (newBadgeEnabled && Model.Item.GetInt32("NewPublicationDays") == 0) || (newBadgeEnabled && (createdDate.AddDays(Model.Item.GetInt32("NewPublicationDays")) > DateTime.Now)) ? true : showBadges; 185 showBadges = !string.IsNullOrEmpty(Model.Item.GetRawValueString("CampaignBadges")) ? true : showBadges; 186 187 188 @* Get assets from selected categories or get all assets *@ 189 if (totalAssets != 0 && assetsList.Count() != 0) { 190 int desktopAssetNumber = 0; 191 int mobileAssetNumber = 0; 192 int mobileAssetThumbnailNumber = 0; 193 int modalAssetNumber = 0; 194 195 @* Desktop: Show the gallery on large screens *@ 196 <div class="d-none d-lg-block h-100 position-relative @theme item_@Model.Item.SystemName.ToLower() desktop"> 197 <div class="grid @spacing"> 198 @foreach (MediaViewModel asset in assetsList) { 199 var assetName = asset.Value; 200 foreach (string format in allSupportedFormats) { 201 if (assetName.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 202 string size = "desktop"; 203 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 204 string assetValue = asset.Value; 205 206 <div class="@GetColumnClass(totalAssets, desktopAssetNumber)"> 207 <div class="h-100 @(imageTheme)"> 208 @foreach (string imageFormat in supportedImageFormats) 209 { //Images 210 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 211 { 212 string productName = product.Name; 213 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 214 string imageLinkPath = Dynamicweb.Context.Current.Server.UrlEncode(imagePath); 215 216 RatioSettings ratioSettings = GetRatioSettings(size); 217 218 var parms = new Dictionary<string, object>(); 219 parms.Add("alt", productName); 220 parms.Add("itemprop", "image"); 221 if (totalAssets > 1) 222 { 223 parms.Add("columns", 2); 224 } 225 else 226 { 227 parms.Add("columns", 1); 228 } 229 parms.Add("eagerLoadNewImages", Model.Item.GetBoolean("DisableLazyLoading")); 230 parms.Add("doNotUseGetimage", Model.Item.GetBoolean("DisableGetImage")); 231 232 if (!string.IsNullOrEmpty(asset.DisplayName)) 233 { 234 parms.Add("title", asset.DisplayName); 235 } 236 237 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") 238 { 239 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 240 } 241 else 242 { 243 parms.Add("cssClass", "mw-100 mh-100"); 244 } 245 246 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 247 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@desktopAssetNumber"> 248 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 249 </div> 250 </a> 251 } 252 } 253 @foreach (string videoFormat in supportedVideoFormats) 254 { //Videos 255 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 256 { 257 if (Model.Item.GetString("OpenVideoInModal") == "true") 258 { 259 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 260 261 string type = GetVideoType(asset.Value); 262 string videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeDataString(asset.Value) : string.Empty; 263 videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value, "maxresdefault") : videoScreendumpPath; 264 string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : string.Empty; 265 266 string productName = product.Name; 267 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 268 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 269 270 RatioSettings ratioSettings = GetRatioSettings(size); 271 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable); cursor: pointer" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 272 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@desktopAssetNumber"> 273 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 274 @if (type != "selfhosted") 275 { 276 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@productName" @assetTitle class="@videoJsClass mw-100 mh-100" data-asset-value="@asset.Value" style="object-fit: cover;" /> 277 } 278 else 279 { 280 string videoType = Path.GetExtension(asset.Value).ToLower(); 281 282 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 283 <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> 284 </video> 285 } 286 </div> 287 </div> 288 289 } 290 else 291 { 292 var videoParams = GetVideoParams(asset, size); 293 @RenderPartial("Components/VideoPlayer.cshtml", new FileViewModel { Path = asset.Value }, videoParams); 294 } 295 } 296 } 297 @foreach (string documentFormat in supportedDocumentFormats) 298 { //Documents 299 if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) 300 { 301 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 302 303 string productName = product.Name; 304 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 305 string imageLinkPath = imagePath; 306 307 RatioSettings ratioSettings = GetRatioSettings(size); 308 309 var parms = new Dictionary<string, object>(); 310 parms.Add("alt", productName); 311 parms.Add("itemprop", "image"); 312 parms.Add("fullwidth", true); 313 parms.Add("columns", Model.GridRowColumnCount); 314 if (!string.IsNullOrEmpty(asset.DisplayName)) 315 { 316 parms.Add("title", asset.DisplayName); 317 } 318 319 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") 320 { 321 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 322 } 323 else 324 { 325 parms.Add("cssClass", "mw-100 mh-100"); 326 } 327 328 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download title="@Translate("Download")"> 329 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 330 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 331 @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) 332 { 333 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 334 } 335 else 336 { 337 338 } 339 </div> 340 </a> 341 } 342 } 343 </div> 344 </div> 345 desktopAssetNumber++; 346 } 347 } 348 } 349 </div> 350 351 @if (showBadges) { 352 <div class="position-absolute top-0 left-0 p-2 p-lg-3 w-100"> 353 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms) 354 </div> 355 } 356 </div> 357 358 @* Mobile: Show the thumbs on small screens *@ 359 <div class="d-block d-lg-none mx-lg-0 position-relative @theme item_@Model.Item.SystemName.ToLower() mobile"> 360 <div id="SmallScreenImages_@Model.ID" class="carousel@(GetArrowsColor())" data-bs-ride="carousel"> 361 <div class="carousel-inner h-100"> 362 @foreach (MediaViewModel asset in assetsList) { 363 var assetValue = asset.Value; 364 foreach (string format in allSupportedFormats) { 365 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 366 string activeSlide = mobileAssetNumber == 0 ? "active" : ""; 367 string size = "mobile"; 368 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 369 370 <div class="carousel-item @activeSlide" data-bs-interval="99999"> 371 <div class="h-100 @(imageTheme)"> 372 @foreach (string supportedFormat in supportedImageFormats) 373 { //Images 374 if (assetValue.IndexOf(supportedFormat, StringComparison.OrdinalIgnoreCase) >= 0) 375 { 376 string productName = product.Name; 377 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 378 string imageLinkPath = Dynamicweb.Context.Current.Server.UrlEncode(imagePath); 379 380 RatioSettings ratioSettings = GetRatioSettings(size); 381 382 var parms = new Dictionary<string, object>(); 383 parms.Add("alt", productName); 384 parms.Add("itemprop", "image"); 385 if (totalAssets > 1) 386 { 387 parms.Add("columns", 2); 388 } 389 else 390 { 391 parms.Add("columns", 1); 392 } 393 parms.Add("eagerLoadNewImages", Model.Item.GetBoolean("DisableLazyLoading")); 394 parms.Add("doNotUseGetimage", Model.Item.GetBoolean("DisableGetImage")); 395 396 if (!string.IsNullOrEmpty(asset.DisplayName)) 397 { 398 parms.Add("title", asset.DisplayName); 399 } 400 401 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") 402 { 403 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 404 } 405 else 406 { 407 parms.Add("cssClass", "mw-100 mh-100"); 408 } 409 410 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 411 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@desktopAssetNumber"> 412 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 413 </div> 414 </a> 415 } 416 } 417 @foreach (string videoFormat in supportedVideoFormats) 418 { //Videos 419 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 420 { 421 if (Model.Item.GetString("OpenVideoInModal") == "true") 422 { 423 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 424 425 string type = GetVideoType(asset.Value); 426 427 string videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeDataString(asset.Value) : string.Empty; 428 videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value, "maxresdefault") : videoScreendumpPath; 429 string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : ""; 430 431 string productName = product.Name; 432 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 433 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 434 435 RatioSettings ratioSettings = GetRatioSettings(size); 436 437 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable); cursor: pointer" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 438 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@desktopAssetNumber"> 439 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 440 @if (type != "selfhosted") 441 { 442 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@productName" @assetTitle class="@videoJsClass mw-100 mh-100" data-asset-value="@asset.Value" style="object-fit: cover;" > 443 } 444 else 445 { 446 string videoType = Path.GetExtension(asset.Value).ToLower(); 447 448 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 449 <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> 450 </video> 451 } 452 </div> 453 </div> 454 } 455 else 456 { 457 Dictionary<string, object> videoParams = GetVideoParams(asset, size); 458 @RenderPartial("Components/VideoPlayer.cshtml", new FileViewModel { Path = asset.Value }, videoParams); 459 } 460 } 461 } 462 @foreach (string documentFormat in supportedDocumentFormats) 463 { //Documents 464 if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) 465 { 466 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 467 468 string productName = product.Name; 469 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 470 string imageLinkPath = imagePath; 471 472 RatioSettings ratioSettings = GetRatioSettings(size); 473 474 var parms = new Dictionary<string, object>(); 475 parms.Add("alt", productName); 476 parms.Add("itemprop", "image"); 477 parms.Add("fullwidth", true); 478 parms.Add("columns", Model.GridRowColumnCount); 479 if (!string.IsNullOrEmpty(asset.DisplayName)) 480 { 481 parms.Add("title", asset.DisplayName); 482 } 483 484 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") 485 { 486 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 487 } 488 else 489 { 490 parms.Add("cssClass", "mw-100 mh-100"); 491 } 492 493 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download title="@Translate("Download")"> 494 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 495 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 496 @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) 497 { 498 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 499 } 500 else 501 { 502 503 } 504 </div> 505 </a> 506 } 507 } 508 </div> 509 </div> 510 mobileAssetNumber++; 511 } 512 } 513 } 514 </div> 515 </div> 516 517 @if (totalAssets > 1) { 518 <div id="SmallScreenImagesThumbnails_@Model.ID" class="d-flex flex-nowrap gap-2 overflow-x-auto my-3"> 519 @foreach (MediaViewModel asset in assetsList) { 520 var assetValue = asset.Value; 521 foreach (string format in allSupportedFormats) { 522 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 523 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 524 string type = GetVideoType(asset.Value); 525 526 string videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value, "maxresdefault") : string.Empty; 527 videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeDataString(asset.Value) : videoScreendumpPath; 528 string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : string.Empty; 529 530 string productName = product.Name; 531 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 532 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 533 534 <div class="ratio ratio-4x3 border outline-none" style="flex:0 0 80px" data-bs-target="#SmallScreenImages_@Model.ID" data-bs-slide-to="@mobileAssetThumbnailNumber"> 535 536 @foreach (string videoFormat in supportedVideoFormats) 537 { 538 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 539 { 540 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 541 <div class="icon-3 position-absolute text-light" style="z-index: 1">@ReadFile(iconPath+"play-circle.svg")</div> 542 </div> 543 } 544 } 545 @if (type != "selfhosted") 546 { 547 548 <img src="@(videoScreendumpPath)" class="p-1 @videoJsClass mw-100 mh-100" data-asset-value="@asset.Value" style="object-fit: cover;" alt="@productName" @assetTitle> 549 550 } 551 else 552 { 553 string videoType = Path.GetExtension(asset.Value).ToLower(); 554 555 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 556 <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> 557 </video> 558 } 559 560 </div> 561 562 mobileAssetThumbnailNumber++; 563 } 564 } 565 } 566 </div> 567 } 568 569 @if (showBadges) { 570 <div class="position-absolute top-0 left-0 p-2 p-lg-3"> 571 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms) 572 </div> 573 } 574 </div> 575 576 @* Modal with slides *@ 577 <div class="modal fade swift_products-details-images-modal" id="modal_@Model.ID" tabindex="-1" aria-labelledby="productDetailsGalleryModalTitle_@Model.ID" aria-hidden="true"> 578 <div class="modal-dialog modal-dialog-centered modal-xl"> 579 <div class="modal-content"> 580 <div class="modal-header visually-hidden"> 581 <h5 class="modal-title" id="productDetailsGalleryModalTitle_@Model.ID">@product.Title</h5> 582 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 583 </div> 584 <div class="modal-body p-2 p-lg-3 h-100"> 585 <div id="ModalCarousel_@Model.ID" class="carousel@(GetArrowsColor()) h-100" data-bs-ride="carousel"> 586 <div class="carousel-inner h-100 @theme"> 587 @foreach (MediaViewModel asset in assetsList) { 588 var assetValue = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 589 foreach (string format in allSupportedFormats) { 590 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 591 string imagePath = assetValue; 592 string activeSlide = modalAssetNumber == 0 ? "active" : ""; 593 594 var parms = new Dictionary<string, object>(); 595 parms.Add("cssClass", "d-block mw-100 mh-100 m-auto"); 596 parms.Add("columns", Model.GridRowColumnCount); 597 parms.Add("eagerLoadNewImages", Model.Item.GetBoolean("DisableLazyLoading")); 598 parms.Add("doNotUseGetimage", Model.Item.GetBoolean("DisableGetImage")); 599 600 <div class="carousel-item @activeSlide h-100" data-bs-interval="99999"> 601 @foreach (string imageFormat in supportedImageFormats) { 602 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) { 603 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 604 } 605 } 606 607 @foreach (string videoFormat in supportedVideoFormats) { 608 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) { 609 610 Dictionary<string, object> videoParams = GetVideoParams(asset, "modal"); 611 @RenderPartial("Components/VideoPlayer.cshtml", new FileViewModel { Path = asset.Value }, videoParams); 612 613 } 614 } 615 </div> 616 617 modalAssetNumber++; 618 } 619 } 620 } 621 <button class="carousel-control-prev carousel-control-area" type="button" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide="prev"> 622 <span class="carousel-control-prev-icon" aria-hidden="true"></span> 623 <span class="visually-hidden">@Translate("Previous")</span> 624 </button> 625 <button class="carousel-control-next carousel-control-area" type="button" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide="next"> 626 <span class="carousel-control-next-icon" aria-hidden="true"></span> 627 <span class="visually-hidden">@Translate("Next")</span> 628 </button> 629 </div> 630 </div> 631 </div> 632 </div> 633 </div> 634 </div> 635 } else if (Pageview.IsVisualEditorMode) { 636 RatioSettings ratioSettings = GetRatioSettings("desktop"); 637 638 <div class="h-100 @theme"> 639 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> 640 <img src="/Files/Images/missing_image.jpg" loading="lazy" decoding="async" class="mh-100 mw-100" style="object-fit: cover;" alt="@Translate("Missing image")"> 641 </div> 642 </div> 643 } 644 } 645
Error executing template "Designs/Swift/Paragraph/Swift_RelatedProducts.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_a5144afd244a487caaa058d465456af5.Execute() in D:\dynamicweb.net\Solutions\Nextech\superhome.cloud.dynamicweb-cms.com\Files\Templates\Designs\Swift\Paragraph\Swift_RelatedProducts.cshtml:line 164
   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.Core 3 @using Dynamicweb.Ecommerce.ProductCatalog 4 5 @{ 6 ProductViewModel product = null; 7 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 8 { 9 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 10 } 11 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 12 { 13 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 14 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 15 16 if (productList?.Products is object) 17 { 18 product = productList.Products[0]; 19 } 20 } 21 22 string title = Model?.Item?.GetRawValueString("Title", Translate("Products")); 23 string campaignValues = string.Join(",", Model.Item.GetList("CampaignBadges")?.GetRawValue().OfType<string>().ToList()); 24 25 //Styling 26 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "h3"); 27 string subtitleFontSize = Model.Item.GetRawValueString("SubtitleFontSize", "fs-5"); 28 string buttonStyle = Model.Item.GetRawValueString("ButtonStyle", ""); 29 buttonStyle = buttonStyle == "primary" ? " btn-primary" : buttonStyle; 30 buttonStyle = buttonStyle == "secondary" ? " btn-secondary" : buttonStyle; 31 buttonStyle = buttonStyle == "link" ? " btn-link" : buttonStyle; 32 string maxWidth = Model.Item.GetRawValueString("TextReadability", ""); 33 maxWidth = maxWidth == "max-width-on" ? " mw-75ch" : maxWidth; 34 maxWidth = maxWidth == "max-width-off" ? "" : maxWidth; 35 36 string generalTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("GeneralTheme")) ? " theme " + Model.Item.GetRawValueString("GeneralTheme").Replace(" ", "").Trim().ToLower() : ""; 37 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 38 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 39 40 //Link generation 41 string pageId = !string.IsNullOrEmpty(Model.Item.GetRawValueString("ProductSliderServicePage")) ? Model.Item.GetLink("ProductSliderServicePage").PageId.ToString() : ""; 42 if (string.IsNullOrEmpty(pageId)) 43 { 44 pageId = GetPageIdByNavigationTag("ProductSliderService").ToString(); 45 } 46 47 string url = "/Default.aspx?ID=" + pageId; 48 if (!url.Contains("LayoutTemplate", StringComparison.OrdinalIgnoreCase)) 49 { 50 url += url.Contains("?") ? "&LayoutTemplate=Designs/Swift/Swift_PageClean.cshtml" : "?LayoutTemplate=Designs/Swift/Swift_PageClean.cshtml"; 51 } 52 if (Pageview.IsVisualEditorMode) 53 { 54 url += "&VisualEdit=True"; 55 } 56 57 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]); 58 if (isLazyLoadingForProductInfoEnabled) 59 { 60 url += "&getproductinfo=true"; 61 } 62 63 //Source type 64 string sourceType = Model.Item.GetRawValueString("RelationType", "trending"); 65 IList<string> relateFromGroupIds = new List<string> { }; 66 IList<string> relateFromProductVariantIds = new List<string> { }; 67 IList<string> relateFromProductIds = new List<string> { }; 68 bool hasVariants = false; 69 70 //--- VARIANTS --- 71 if (sourceType == "variants" && Model.Item.GetValue<ProductListViewModel>("ProductsToRelateToVariants") is ProductListViewModel productsToRelateToVariants) 72 { 73 foreach (var productSelection in productsToRelateToVariants.Products) 74 { 75 relateFromProductIds.Add(productSelection.Id); 76 } 77 } 78 79 //--- MOST SOLD --- 80 if (sourceType == "most-sold" && Model.Item.GetValue<IList<ProductGroupViewModel>>("GroupsToRelateToMostSold") is IList<ProductGroupViewModel> groupsToRelateToMostSold) 81 { 82 foreach (var fromGroup in groupsToRelateToMostSold) 83 { 84 relateFromGroupIds.Add(fromGroup.Id); 85 } 86 } 87 88 //--- TRENDING --- 89 if (sourceType == "trending" && Model.Item.GetValue<IList<ProductGroupViewModel>>("GroupsToRelateToTrending") is IList<ProductGroupViewModel> groupsToRelateToTrending) 90 { 91 foreach (var fromGroup in groupsToRelateToTrending) 92 { 93 relateFromGroupIds.Add(fromGroup.Id); 94 } 95 } 96 97 //--- LATEST --- 98 if (sourceType == "latest" && Model.Item.GetValue<IList<ProductGroupViewModel>>("GroupsToRelateToLatest") is IList<ProductGroupViewModel> groupsToRelateToLatest) 99 { 100 foreach (var fromGroup in groupsToRelateToLatest) 101 { 102 relateFromGroupIds.Add(fromGroup.Id); 103 } 104 } 105 106 //--- FREQUENTLY BOUGHT --- 107 if (sourceType == "frequently" && Model.Item.GetValue<ProductListViewModel>("ProductsToRelateTo") is ProductListViewModel productsToRelateTo) 108 { 109 foreach (var fromProduct in productsToRelateTo.Products) 110 { 111 relateFromProductIds.Add(fromProduct.Id); 112 } 113 } 114 115 //--- SELECTED PRODUCTS --- 116 if ((sourceType == "selected" || sourceType == "frequently") && Model.Item.GetValue<ProductListViewModel>("Products") is ProductListViewModel products) 117 { 118 hasVariants = products.Products.Any(p => !string.IsNullOrEmpty(p.VariantId)); 119 foreach (var productSelection in products.Products) 120 { 121 if (hasVariants) 122 { 123 if (!string.IsNullOrEmpty(productSelection.VariantId)) 124 { 125 relateFromProductVariantIds.Add($"{productSelection.Id} {productSelection.VariantId}"); 126 } 127 else 128 { 129 relateFromProductVariantIds.Add($"{productSelection.Id}"); 130 } 131 } 132 relateFromProductIds.Add($"{productSelection.Id}"); 133 } 134 } 135 136 //--- RELATED PRODUCTS --- 137 if (sourceType == "related-products" && Model.Item.GetValue<ProductListViewModel>("ProductsToRelateTo2") is ProductListViewModel selectedRelationProduct) 138 { 139 if (selectedRelationProduct.Products.Any()) 140 { 141 product = selectedRelationProduct.Products.FirstOrDefault(); 142 } 143 144 if (product?.RelatedGroups != null) 145 { 146 foreach (var group in product.RelatedGroups) 147 { 148 foreach (var relatedProduct in group.Products) 149 { 150 if (!string.IsNullOrEmpty(relatedProduct.VariantId)) 151 { 152 relateFromProductVariantIds.Add($"{relatedProduct.ProductId} {relatedProduct.VariantId}"); 153 } 154 else 155 { 156 relateFromProductVariantIds.Add($"{relatedProduct.ProductId}"); 157 } 158 } 159 } 160 } 161 } 162 163 //Create group id collection and products id collection strings 164 string groupIds = product is object ? product.PrimaryOrDefaultGroup.Id : string.Join(",", relateFromGroupIds); 165 string productVariantIds = relateFromProductVariantIds.Count > 0 ? string.Join(",", relateFromProductVariantIds) : ""; 166 string productIds = product is object && relateFromProductIds.Count == 0 ? product.Id : string.Join(",", relateFromProductIds); 167 168 //Set the parameters to the url 169 string linkParameters = ""; 170 linkParameters += sourceType != "related-products" && sourceType != "frequently" && sourceType != "selected" ? "&GroupId=" + groupIds : ""; 171 linkParameters += !string.IsNullOrEmpty(productIds) && sourceType != "most-sold" && sourceType != "trending" && sourceType != "latest" && sourceType != "frequently" && sourceType != "related-products" ? "&MainProductId=" + productIds : ""; 172 linkParameters += !string.IsNullOrEmpty(productVariantIds) && sourceType == "related-products" ? "&ProductVariantId=" + productVariantIds : ""; 173 linkParameters += sourceType == "variants" ? "&IsVariant=true" : ""; 174 linkParameters += sourceType == "latest" ? "&SortBy=Created" : ""; 175 linkParameters += sourceType == "most-sold" ? "&SortBy=OrderCount" : ""; 176 linkParameters += sourceType == "trending" ? "&SortBy=OrderCountGrowth" : ""; 177 linkParameters += !string.IsNullOrEmpty(productIds) && sourceType == "frequently" ? $"&BoughtWithProductIds=[{productIds}]" : ""; 178 var productListPageId = GetPageIdByNavigationTag("Shop"); 179 string link = "/Default.aspx?ID=" + productListPageId + linkParameters; 180 181 // Slider settings (documentation: swiffyslider.com/configuration) 182 string navigationStyle = $"{Model.Item.GetRawValueString("NavigationStyle", "slider-nav-round")}"; 183 string navigationPlacement = $"{Model.Item.GetRawValueString("NavigationPlacement", "slider-nav-on-slides")}"; 184 string indicatorStyle = $"{Model.Item.GetRawValueString("IndicatorStyle", "slider-indicators-hidden")}"; 185 string revealSlides = Model.Item.GetRawValueString("RevealSlides", "no-reveal") == "reveal" ? "slider-item-reveal" : string.Empty; 186 string navigationAlwaysVisible = (Model.Item.GetBoolean("NavigationAlwaysVisible")) ? "slider-nav-visible" : string.Empty; 187 string navigationVisibleOnTouch = (Model.Item.GetBoolean("NavigationVisibleOnTouch")) ? "slider-nav-touch" : string.Empty; 188 string navigationShowScrollbar = (Model.Item.GetBoolean("NavigationShowScrollbar")) ? "slider-nav-scrollbar" : string.Empty; 189 string navigationSmall = (Model.Item.GetBoolean("NavigationSmall")) ? "slider-nav-sm" : string.Empty; 190 string navigationInvertColors = (Model.Item.GetBoolean("NavigationInvertColors")) ? "slider-nav-dark" : string.Empty; 191 string navigationSlideEntirePage = (Model.Item.GetBoolean("NavigationSlideEntirePage")) ? "slider-nav-page" : string.Empty; 192 string navigationNoLoop = (Model.Item.GetBoolean("NavigationNoLoop")) ? "slider-nav-noloop" : string.Empty; 193 string indicatorsOutsideSlider = (Model.Item.GetBoolean("IndicatorsOutsideSlider") && indicatorStyle != string.Empty) ? "slider-indicators-outside" : string.Empty; 194 string indicatorsHighlightActive = (Model.Item.GetBoolean("IndicatorsHighlightActive")) ? "slider-indicators-highlight" : string.Empty; 195 string indicatorsInvertColors = (Model.Item.GetBoolean("IndicatorsInvertedColors")) ? "slider-indicators-dark" : string.Empty; 196 string indicatorsVisibleOnSmallDevices = (Model.Item.GetBoolean("IndicatorsVisibleOnSmallDevices")) ? "slider-indicators-sm" : string.Empty; 197 198 bool productsFound = true; 199 if (string.IsNullOrEmpty(groupIds) && string.IsNullOrEmpty(productIds) && string.IsNullOrEmpty(productVariantIds)) 200 { 201 if (Pageview.IsVisualEditorMode) 202 { 203 productIds = product.Id; 204 sourceType = "selected"; 205 } 206 else 207 { 208 productsFound = false; 209 } 210 } 211 } 212 213 @*Container element for the request*@ 214 @if (productsFound) 215 { 216 <form method="post" action="@url" id="RelatedProductsForm_@Model.ID" data-response-target-element="RelatedProducts_@Model.ID" data-preloader="inline" data-update-url="false" class="item_@Model.Item.SystemName.ToLower()"> 217 <input type="hidden" name="ModelID" value="@Model.ID"> 218 <input type="hidden" name="SourceType" value="@sourceType"> 219 220 @*--- SLIDER SETTINGS ---*@ 221 <input type="hidden" name="NavigationStyle" value="@navigationStyle"> 222 <input type="hidden" name="NavigationPlacement" value="@navigationPlacement"> 223 <input type="hidden" name="IndicatorStyle" value="@indicatorStyle"> 224 <input type="hidden" name="RevealSlides" value="@revealSlides"> 225 <input type="hidden" name="NavigationAlwaysVisible" value="@(navigationAlwaysVisible)"> 226 <input type="hidden" name="NavigationVisibleOnTouch" value="@(navigationVisibleOnTouch)"> 227 <input type="hidden" name="NavigationShowScrollbar" value="@(navigationShowScrollbar)"> 228 <input type="hidden" name="NavigationSmall" value="@(navigationSmall)"> 229 <input type="hidden" name="NavigationInvertColors" value="@(navigationInvertColors)"> 230 <input type="hidden" name="NavigationNoLoop" value="@(navigationNoLoop)"> 231 <input type="hidden" name="NavigationSlideEntirePage" value="@(navigationSlideEntirePage)"> 232 <input type="hidden" name="IndicatorsOutsideSlider" value="@(indicatorsOutsideSlider)"> 233 <input type="hidden" name="IndicatorsHighlightActive" value="@(indicatorsHighlightActive)"> 234 <input type="hidden" name="IndicatorsInvertColors" value="@(indicatorsInvertColors)"> 235 <input type="hidden" name="IndicatorsVisibleOnSmallDevices" value="@(indicatorsVisibleOnSmallDevices)"> 236 237 @*--- VARIANTS ---*@ 238 @if (sourceType == "variants") 239 { 240 <input type="hidden" name="isVariant" value="true"> 241 <input type="hidden" name="MainProductID" id="MainProductID_@Model.ID" value="@productIds"> 242 } 243 244 @*--- MOST SOLD ---*@ 245 @if (sourceType == "most-sold") 246 { 247 <input type="hidden" name="SortBy" value="OrderCount"> 248 if (groupIds != "") 249 { 250 <input type="hidden" name="GroupId" value="@groupIds"> 251 } 252 } 253 254 @*--- TRENDING ---*@ 255 @if (sourceType == "trending") 256 { 257 <input type="hidden" name="SortBy" value="OrderCountGrowth"> 258 if (groupIds != "") 259 { 260 <input type="hidden" name="GroupId" value="@groupIds"> 261 } 262 } 263 264 @*--- FREQUENTLY BOUGHT ---*@ 265 @if (sourceType == "frequently" && !string.IsNullOrEmpty(productIds)) 266 { 267 <input type="hidden" name="BoughtWithProductIds" value="[@productIds]"> 268 } 269 @if (sourceType != "frequently" && hasVariants) 270 { 271 <input type="hidden" name="ProductVariantId" value="@productVariantIds"> 272 } 273 274 @*--- LATEST ---*@ 275 @if (sourceType == "latest") 276 { 277 <input type="hidden" name="SortBy" value="Created"> 278 <input type="hidden" name="GroupId" value="@groupIds"> 279 } 280 281 @*--- SELECTED PRODUCTS ---*@ 282 @if (sourceType == "selected" && !string.IsNullOrEmpty(productIds) && !hasVariants) 283 { 284 <input type="hidden" name="MainProductId" id="MainProductID_@Model.ID" value="@productIds"> 285 } 286 @if (sourceType == "selected" && hasVariants) 287 { 288 <input type="hidden" name="ProductVariantId" value="@productVariantIds"> 289 } 290 291 @*--- RELATED PRODUCTS ---*@ 292 @if (sourceType == "related-products") 293 { 294 <input type="hidden" name="ProductVariantId" id="MainProductID_@Model.ID" value="@productVariantIds"> 295 } 296 297 @* General parameters *@ 298 <input type="hidden" name="Link" value="@link"> 299 <input type="hidden" name="HideTitle" value="@Model.Item.GetString("HideTitle")"> 300 301 @if (Model.Item.GetInt32("ProductsCount") != 0) 302 { 303 <input type="hidden" name="PageSize" value="@Model.Item.GetInt32("ProductsCount")"> 304 } 305 <input type="hidden" name="HeadingTitle" id="RelatedProductsTitle_@Model.ID" value="@title"> 306 @if (!string.IsNullOrEmpty(Model.Item.GetString("Subtitle"))) 307 { 308 <input type="hidden" name="Subtitle" value="@Model.Item.GetString("Subtitle")"> 309 } 310 @if (!string.IsNullOrEmpty(Model.Item.GetString("LinkText"))) 311 { 312 <input type="hidden" name="LinkText" value="@Model.Item.GetString("LinkText")"> 313 } 314 @if (!string.IsNullOrEmpty(Model.Item.GetString("ImageAspectRatio"))) 315 { 316 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 317 ratio = ratio != "0" ? ratio : ""; 318 <input type="hidden" name="ImageAspectRatio" value="@ratio"> 319 } 320 @if (!string.IsNullOrEmpty(Model.Item.GetString("Layout"))) 321 { 322 <input type="hidden" name="Layout" value="@Model.Item.GetRawValueString("Layout")"> 323 } 324 @if (titleFontSize != "") 325 { 326 <input type="hidden" name="TitleFontSize" value="@titleFontSize"> 327 } 328 @if (subtitleFontSize != "") 329 { 330 <input type="hidden" name="SubtitleFontSize" value="@subtitleFontSize"> 331 } 332 @if (buttonStyle != "") 333 { 334 <input type="hidden" name="ButtonStyle" value="@buttonStyle"> 335 } 336 @if (generalTheme != "") 337 { 338 <input type="hidden" name="GeneralTheme" value="@generalTheme"> 339 } 340 @if (theme != "") 341 { 342 <input type="hidden" name="Theme" value="@theme"> 343 } 344 @if (imageTheme != "") 345 { 346 <input type="hidden" name="ImageTheme" value="@imageTheme"> 347 } 348 @if (!string.IsNullOrEmpty(Model.Item.GetString("ContentPadding"))) 349 { 350 string contentPadding = Model.Item.GetRawValueString("ContentPadding"); 351 <input type="hidden" name="ContentPadding" value="@contentPadding"> 352 } 353 <input type="hidden" name="TextReadability" value="@maxWidth"> 354 <input type="hidden" name="ParentColumnSize" id="ParentColumnSize_@Model.ID" value="12"> 355 356 <input type="hidden" name="SaleBadgeType" value="@Model.Item.GetRawValue("SaleBadgeType")"> 357 <input type="hidden" name="SaleBadgeCssClassName" value="@Model.Item.GetRawValue("SaleBadgeDesign")"> 358 <input type="hidden" name="NewBadgeCssClassName" value="@Model.Item.GetRawValue("NewBadgeDesign")"> 359 <input type="hidden" name="NewPublicationDays" value="@Model.Item.GetInt32("NewPublicationDays")"> 360 361 @if (campaignValues != string.Empty) 362 { 363 <input type="hidden" name="CampaignBadgesValues" value="@campaignValues"> 364 } 365 </form> 366 367 <script type="module" src="/Files/Templates/Designs/Swift/Assets/js/swiffy-slider.js"></script> 368 <script> 369 window.addEventListener("load", () => { 370 swift.AssetLoader.Load('/Files/Templates/Designs/Swift/Assets/css/swiffy-slider.min.css', 'css'); 371 }); 372 </script> 373 374 if (Pageview.IsVisualEditorMode) 375 { 376 <div class="alert alert-info" role="alert"> 377 <span>@Translate("Product slider: Edit this column to configure")</span> 378 </div> 379 } 380 381 if (sourceType != "related-products") 382 { 383 <div class="w-100 h-100"> 384 <div id="@Model.ID" class="user-select-none" style="scroll-margin-top:var(--header-height,150px)"></div> 385 <div id="RelatedProducts_@Model.ID" class="h-100 swift_product_slider_container"></div> 386 </div> 387 } 388 else if (product?.RelatedGroups != null) 389 { 390 @* Create multiple slider containers, if type is Product relation *@ 391 <div class="grid w-100 h-100@(generalTheme)" style="grid-row-gap: 4rem"> 392 <div id="@Model.ID" class="user-select-none" style="scroll-margin-top:var(--header-height,150px)"></div> 393 @foreach (var group in product.RelatedGroups) 394 { 395 <div id="RelatedProducts_@(Model.ID)_@group.Id" class="g-col-12 h-100 swift_product_slider_container"></div> 396 } 397 </div> 398 } 399 400 @* Initialize *@ 401 if (sourceType != "related-products") 402 { 403 <script type="module"> 404 if (document.querySelector("#RelatedProducts_@Model.ID").closest("[data-col-size]")) { 405 document.querySelector("#ParentColumnSize_@Model.ID").value = document.querySelector("#RelatedProducts_@Model.ID").closest("[data-col-size]").getAttribute("data-col-size"); 406 } 407 swift.PageUpdater.Update(document.querySelector("#RelatedProductsForm_@Model.ID")).then(function () { 408 setTimeout(function() { 409 const isVisualEditor = @(Converter.ToString(Pageview.IsVisualEditorMode).ToLowerInvariant()); 410 const productSliderContainer = document.querySelector(".swift_product_slider_container"); 411 412 if (productSliderContainer && productSliderContainer.innerHTML !== "") { 413 productSliderContainer.classList.remove("d-none"); 414 } 415 else if (!isVisualEditor) { 416 productSliderContainer.closest("[class*=column]").classList.add("d-none"); 417 } 418 }, 150); 419 }); 420 </script> 421 } 422 else if (product?.RelatedGroups != null) 423 { 424 @* Create multiple sliders, if type is Product relation *@ 425 foreach (var group in product.RelatedGroups) 426 { 427 IList<string> fromProductIds = new List<string> { }; 428 429 foreach (var relatedProduct in group.Products) 430 { 431 if (!string.IsNullOrEmpty(relatedProduct.VariantId)) 432 { 433 fromProductIds.Add($"{relatedProduct.ProductId} {relatedProduct.VariantId}"); 434 } 435 else 436 { 437 fromProductIds.Add($"{relatedProduct.ProductId}"); 438 } 439 } 440 <script type="module"> 441 document.querySelector("#ParentColumnSize_@Model.ID").value = document.querySelector("#RelatedProducts_@(Model.ID)_@group.Id").closest("[data-col-size]").getAttribute("data-col-size"); 442 document.querySelector("#MainProductID_@Model.ID").value = "@string.Join(",", fromProductIds)"; 443 document.querySelector("#RelatedProductsTitle_@Model.ID").value = "@group.Name"; 444 document.querySelector("#RelatedProductsForm_@Model.ID").setAttribute("data-response-target-element", "RelatedProducts_@(Model.ID)_@group.Id"); 445 446 swift.PageUpdater.Update(document.querySelector("#RelatedProductsForm_@Model.ID")); 447 </script> 448 } 449 } 450 } 451
Error executing template "/Designs/Swift/Paragraph/PreviouslyViewed_ProductPage_Carousel.cshtml"
System.IndexOutOfRangeException: Index was outside the bounds of the array.
   at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
   at Dynamicweb.Ecommerce.FieldTypeProviders.ConversionUnitFieldTypeProviderBase.GetLanguageUnits(Dictionary`2 settings)
   at Dynamicweb.Ecommerce.FieldTypeProviders.ConversionUnitFieldTypeProviderBase.GetValue(Object value, String languageId, Dictionary`2 settings)
   at Dynamicweb.Ecommerce.Products.ProductFieldTypeProvider.GetProductValue(Product product, FieldType fieldType, Object fieldValue)
   at Dynamicweb.Ecommerce.Products.ProductFieldValueCollection.RefreshProviderFields(Product product)
   at Dynamicweb.Ecommerce.Products.ProductRepository.ExtractProduct(IDataReader dataReader, Nullable`1& groupProductRelationSortingExists)
   at Dynamicweb.Ecommerce.Products.ProductRepository.GetProductsBySql(CommandBuilder query, Boolean doRefactoring, Boolean bulkFill, Boolean useAssortments)
   at Dynamicweb.Ecommerce.Products.ProductService.GetByProductIDs(String[] productIds, Boolean doRefactoring, String productLanguageId, Boolean bulkFill, Boolean useAssortments)
   at CompiledRazorTemplates.Dynamic.RazorEngine_5c3087e159684c5095d1baf8db234750.Execute() in D:\dynamicweb.net\Solutions\Nextech\superhome.cloud.dynamicweb-cms.com\Files\Templates\Designs\Swift\Paragraph\PreviouslyViewed_ProductPage_Carousel.cshtml:line 73
   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.RazorTemplateBase<Dynamicweb.Rendering.RazorTemplateModel<Dynamicweb.Rendering.Template>> 2 3 @using System.Linq; 4 @using System.Web; 5 @using Dynamicweb.Core; 6 @using System; 7 @using System.Web; 8 @using System.Collections.Generic; 9 @using Dynamicweb.Rapido.Blocks.Components.General; 10 @using Dynamicweb.Rapido.Blocks; 11 @using Dynamicweb.Rapido.Services; 12 @using RaptorRecommendation.Recommendations; 13 @using NextechDWAddIn.Common; 14 @using Dynamicweb.Security.UserManagement; 15 @using Dynamicweb.Environment; 16 @using System.Globalization 17 @using NextechDWAddIn.Helpers 18 @using Dynamicweb.Ecommerce.ProductCatalog 19 @using Dynamicweb.Core.Encoders 20 @using Dynamicweb.Ecommerce.CustomerExperienceCenter.Favorites 21 @using Dynamicweb.Ecommerce 22 @using Dynamicweb.Caching 23 @using Dynamicweb 24 @using Dynamicweb.Ecommerce.Products 25 26 @{ 27 bool DiscountExplanationEnabled = true; 28 29 30 //Standard Swift to get the product object.. 31 ProductViewModel productVM = null; 32 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 33 { 34 productVM = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 35 } 36 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 37 { 38 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 39 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 40 41 if (productList?.Products is object) 42 { 43 productVM = productList.Products[0]; 44 } 45 } 46 //..Standard Swift to get the product object 47 48 49 //https://doc.dynamicweb.com/forum/ecommerce-standard-features/ecommerce-standard-features/recently-viewed-products-list-is-not-populated 50 //First in your product detail template add a string array to session: 51 IEnumerable<Product> youHaveSeenTheseProductsList = null; 52 if (Context.Current.Session["youHaveSeenTheseProductsList"] is null && productVM != null) 53 { 54 Context.Current.Session.Add("youHaveSeenTheseProductsList", new string[] { productVM.Id }); 55 } 56 else 57 { 58 var productIds = ((string[])Dynamicweb.Context.Current.Session["youHaveSeenTheseProductsList"]).ToList(); 59 /* 60 foreach (var pId in productIds) 61 { 62 Logger.Instance.Log(ErrorLevel.DebugInfo, "PreviouslyViewed_ProductPage_Carousel.cshtml pId: " + pId); 63 } 64 */ 65 productIds.Add(productVM.Id); 66 Context.Current.Session["youHaveSeenTheseProductsList"] = productIds.ToArray(); 67 } 68 69 //And then fetch them something like this: 70 if (Context.Current.Session["youHaveSeenTheseProductsList"] != null) 71 { 72 var productIds = (string[])Dynamicweb.Context.Current.Session["youHaveSeenTheseProductsList"]; 73 youHaveSeenTheseProductsList = Dynamicweb.Ecommerce.Services.Products.GetByProductIDs(productIds, false, Dynamicweb.Ecommerce.Common.Context.LanguageID, true, true); 74 } 75 76 77 78 79 80 string url = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("CartService")); 81 string isGuestUser = Pageview.User != null ? "0" : "1"; 82 83 //CS NT The path to the tutorial 84 //https://www.jqueryscript.net/slider/Fully-Responsive-Flexible-jQuery-Carousel-Plugin-slick.html 85 //Good slick example 86 //https://codepen.io/vilcu/pen/ZQwdGQ 87 88 89 //bool showCartButton = Pageview.AreaSettings.GetItem("ProductList").GetItem("GridView").GetBoolean("ShowAddToCartButton"); 90 91 string currentLanguageTwoLetter = ExecutingContext.GetCulture(true).TwoLetterISOLanguageName; 92 string baseCategoryBasedOnLanguage = "/Default.aspx?ID=" + CommonFunctions.GetPageIdByNavigationTag("Shop",currentLanguageTwoLetter) + "&"; 93 94 95 96 var PS = new Dynamicweb.Ecommerce.Products.ProductService(); 97 string imagePrefix = "/Admin/Public/GetImage.ashx?width=150&amp;height=150&amp;crop=5&FillCanvas=True&DoNotUpscale=true&amp;Compression=75&amp;image="; 98 99 var shopId = string.Empty; 100 if (Pageview != null && Pageview.Area != null) 101 { 102 shopId = Pageview.Area.EcomShopId; 103 } 104 105 string productPageProductId = productVM != null ? productVM.Id : ""; 106 //Previously Viewed 107 string CarouselID1 = "3"; 108 string CarouselDetails1 = Translate("Previously Viewed"); 109 string SliderDescription1 = "slider slider-nav"; 110 111 List<string> finalIds1 = new List<string>(); 112 113 bool skipLoop = false;//CS NT if the only item is the current item then skip loop 114 foreach (LoopItem loopProd in GetLoop("eCom:Related.YouHaveSeenTheseProducts")) 115 { 116 if((GetLoop("eCom:Related.YouHaveSeenTheseProducts").Count == 1) && (loopProd.GetString("Ecom:Product.ID") == GetString("Ecom:Product.ID"))) 117 { 118 skipLoop = true; 119 }else 120 { 121 finalIds1.Add(loopProd.GetString("Ecom:Product.ID")); 122 } 123 } 124 if (youHaveSeenTheseProductsList != null && youHaveSeenTheseProductsList.Count() > 0) 125 { 126 foreach (var tP in youHaveSeenTheseProductsList) 127 { 128 if((youHaveSeenTheseProductsList.Count() == 1) && (productVM.Id == tP.Id)) 129 { 130 skipLoop = true; 131 }else 132 { 133 finalIds1.Add(tP.Id); 134 } 135 } 136 } 137 string[] arrayOfIDs1 = finalIds1.ToArray(); 138 139 140 141 142 143 int maxItemsToShow = 16; 144 int prodsCounter = 0; 145 int prodsCounter1 = 0; 146 147 var cartLoop = Dynamicweb.Ecommerce.Common.Context.Cart != null && Dynamicweb.Ecommerce.Common.Context.Cart.OrderLines != null ? Dynamicweb.Ecommerce.Common.Context.Cart.OrderLines : null; 148 int newBadgePublicationDays = CommonFunctions.GetNewBadgePublicationDays(); 149 } 150 151 @inherits Dynamicweb.Rendering.RazorTemplateBase<Dynamicweb.Rendering.RazorTemplateModel<Dynamicweb.Rendering.Template>> 152 153 @using System.Linq; 154 @using System.Web; 155 @using Dynamicweb.Core; 156 @using System; 157 @using System.Web; 158 @using System.Collections.Generic; 159 @using Dynamicweb.Rapido.Blocks.Components.General; 160 @using Dynamicweb.Rapido.Blocks; 161 @using Dynamicweb.Rapido.Services; 162 @using RaptorRecommendation.Recommendations; 163 @using NextechDWAddIn.Common; 164 @using Dynamicweb.Security.UserManagement; 165 @using Dynamicweb.Environment; 166 @using System.Globalization 167 @using NextechDWAddIn.Helpers 168 @using Dynamicweb.Ecommerce.ProductCatalog 169 @using Dynamicweb.Core.Encoders 170 @using Dynamicweb.Ecommerce.CustomerExperienceCenter.Favorites 171 172 @{ 173 } 174 175 176 @helper RenderAddToCartNT(Dynamicweb.Ecommerce.Products.Product product) 177 { 178 string url = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("CartService")); 179 string variantName = Dynamicweb.Ecommerce.Services.Variants.GetVariantName(product.VariantId); 180 bool anonymousUser = Pageview.User == null; 181 //string showLoginModalOrAddToCart = anonymousUser ? "showLoginModal();" : "swift.Cart.Update(event)"; 182 string showLoginModalOrAddToCart = "swift.Cart.Update(event)"; 183 //bool favoritesSelector = true; 184 string PageviewCurrentParagraphID = (Pageview != null && Pageview.CurrentParagraph != null) ? Pageview.CurrentParagraph.ID.ToString() : ""; 185 //string unitId = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("UnitId")) ? Dynamicweb.Context.Current.Request.Form.Get("UnitId") : product.DefaultUnitId; 186 string unitId = product.DefaultUnitId; 187 188 189 190 //CS NT..Google tag manager new fields 191 192 string itemCategory = ""; 193 string itemCategory2 = ""; 194 string itemCategory3 = ""; 195 string itemCategory4 = ""; 196 string itemCategory5 = ""; 197 198 string shopId = (Pageview is object && Pageview != null) ? Pageview.Area.EcomShopId : ""; 199 var defaultProductGroup = product.GetDefaultGroupByShopId(shopId); 200 string PrimaryOrDefaultGroupId = defaultProductGroup != null ? defaultProductGroup.Id : ""; 201 202 203 var prodCategories = CommonFunctions.GetProductCategories(Dynamicweb.Ecommerce.Services.ProductGroups.GetGroup(PrimaryOrDefaultGroupId)); 204 if (prodCategories != null && prodCategories.Count >= 1) 205 { 206 prodCategories.Reverse(); 207 for (int i = 0; i < prodCategories.Count; i++) 208 { 209 if (i == 0) 210 { 211 itemCategory = prodCategories[i]; 212 } 213 else if (i == 1) 214 { 215 itemCategory2 = prodCategories[i]; 216 } 217 else if (i == 2) 218 { 219 itemCategory3 = prodCategories[i]; 220 } 221 else if (i == 3) 222 { 223 itemCategory4 = prodCategories[i]; 224 } 225 else if (i == 4) 226 { 227 itemCategory5 = prodCategories[i]; 228 } 229 } 230 } 231 232 //..CS NT 233 234 bool isPhoneOrder = false; 235 string brand = ""; 236 if (product.ProductFieldValues != null && product.ProductFieldValues.Count > 0) 237 { 238 foreach (var pf in product.ProductFieldValues) 239 { 240 if (pf.ProductField.Name == "PhoneOrders" && pf.Value != null && !string.IsNullOrWhiteSpace(pf.Value.ToString())) 241 { 242 isPhoneOrder = pf.Value.ToString() == "1" ? true : false; 243 } 244 else if (pf.ProductField.Name == "Brand" && pf.Value != null && !string.IsNullOrWhiteSpace(pf.Value.ToString())) 245 { 246 brand = pf.Value.ToString(); 247 } 248 } 249 } 250 251 if (isPhoneOrder) 252 { 253 254 <div class="w-100" style="text-align: center;"> 255 <button id="PhoneOrderBtn" class="PhoneOrderBtnNT"><a href="tel:+77777545" style="height: auto !important;"> @Translate("Phone Order") <i class="fas fa-phone"></i></a></button> 256 </div> 257 } 258 else 259 { 260 <div class="d-flex justify-content-center w-100 js-input-group "> 261 <form method="post" action="@url" class="w-100" style="z-index: 1"> 262 <input type="hidden" name="redirect" value="false"> 263 <input type="hidden" name="ProductId" value="@product.Id"> 264 <input type="hidden" name="ProductName" value="@HtmlEncoder.HtmlEncode(product.Name)"> 265 <input type="hidden" name="ProductVariantName" value="@variantName"> 266 <input type="hidden" name="ProductCurrency" value="@Dynamicweb.Ecommerce.Common.Context.Currency.Code"> 267 <input type="hidden" name="ProductPrice" value="@product.Price.PriceWithVAT"> 268 <input type="hidden" name="ProductReferer" value="component_ProductAddToCart"> 269 <input type="hidden" name="cartcmd" value="add"> 270 <input type="hidden" id="Unit_@(product.Id)_@product.VariantId.Replace(".", "_")" name="UnitID" value="@unitId" /> 271 272 @*CS NT..*@ 273 <input type="hidden" name="brand" value="@brand"> 274 <input type="hidden" name="itemCategory" value="@itemCategory"> 275 <input type="hidden" name="itemCategory2" value="@itemCategory2"> 276 <input type="hidden" name="itemCategory3" value="@itemCategory3"> 277 <input type="hidden" name="itemCategory4" value="@itemCategory4"> 278 <input type="hidden" name="itemCategory5" value="@itemCategory5"> 279 @*..CS NT*@ 280 281 <input type="submit" class="d-none" onclick="event.preventDefault(); swift.Cart.Update(event)"> @* Fix for enterKey should not redirect to minicart page *@ 282 283 @if (!product.NeverOutOfStock) 284 { 285 <input type="hidden" name="Stock" value="@product.Stock"> 286 287 <template class="js-out-of-stock-notice"> 288 <div class="modal-header"> 289 <h1 class="modal-title fs-5">@Translate("Stock limit")</h1> 290 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="@Translate("Close")"></button> 291 </div> 292 <div class="modal-body"> 293 @Translate("There are not enough products in stock. The product might be sold out or discontinued. Please adjust the quantity.") 294 </div> 295 </template> 296 } 297 298 299 <div class="d-flex flex-row w-100"> 300 301 302 <button type="button" onclick="@showLoginModalOrAddToCart" class="btn btn-primary flex-fill js-add-to-cart-button" style="white-space: nowrap;/*margin-right: 2rem;margin-left: 2rem;*/" 303 title="@Translate("Add to cart")" id="AddToCartButton@(product.Id)_@PageviewCurrentParagraphID"> 304 <span class="text-nowrap d-flex align-items-center justify-content-center gap-2"> 305 @Translate("Add to cart") 306 </span> 307 </button> 308 309 310 </div> 311 </form> 312 </div> 313 } 314 } 315 316 317 @helper RenderAddToCart_QuantitySelectorNT(Dynamicweb.Ecommerce.Products.Product product) 318 { 319 320 try 321 { 322 string url = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("CartService")); 323 string variantName = Dynamicweb.Ecommerce.Services.Variants.GetVariantName(product.VariantId); 324 bool anonymousUser = Pageview.User == null; 325 string showLoginModalOrAddToCart = anonymousUser ? "showLoginModal();" : "swift.Cart.Update(event)"; 326 //bool favoritesSelector = true; 327 string PageviewCurrentParagraphID = (Pageview != null && Pageview.CurrentParagraph != null) ? Pageview.CurrentParagraph.ID.ToString() : ""; 328 //string unitId = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("UnitId")) ? Dynamicweb.Context.Current.Request.Form.Get("UnitId") : product.DefaultUnitId; 329 string unitId = product.DefaultUnitId; 330 331 double? stepQty = product.PurchaseQuantityStep > 0 ? product.PurchaseQuantityStep : 1; 332 double? minQty = product.PurchaseMinimumQuantity > 0 ? product.PurchaseMinimumQuantity : 1; 333 double? valueQty = minQty > stepQty ? minQty : stepQty; 334 bool isStockType = product.Type == Dynamicweb.Ecommerce.Products.ProductType.Stock; 335 bool neverOutOfStock = product.NeverOutOfStock; 336 bool setMaxQty = isStockType && !neverOutOfStock; 337 //cs nt 338 long stockLocationID = Dynamicweb.Ecommerce.Common.Context.StockLocation is object ? Dynamicweb.Ecommerce.Common.Context.StockLocation.ID : 0; 339 double? maxQty = null; 340 double? productStock = product.Stock; 341 342 if (setMaxQty && productStock > 0) 343 { 344 maxQty = productStock; 345 } 346 string subId = product.Id + "_" + product.VariantId.Replace(".", "_") + "_MiniCart"; 347 348 <div class="d-flex justify-content-end w-100 js-input-group "> 349 <form method="post" action="@url" class="d-flex justify-content-end" style="z-index: 0"> 350 <input type="hidden" name="redirect" value="false"> 351 <input type="hidden" name="ProductId" value="@product.Id"> 352 <input type="hidden" name="ProductName" value="@HtmlEncoder.HtmlEncode(product.Name)"> 353 <input type="hidden" name="ProductVariantName" value="@variantName"> 354 <input type="hidden" name="ProductCurrency" value="@Dynamicweb.Ecommerce.Common.Context.Currency.Code"> 355 <input type="hidden" name="ProductPrice" value="product.Price"> 356 <input type="hidden" name="ProductReferer" value="component_ProductAddToCart"> 357 <input type="hidden" name="cartcmd" value="add"> 358 359 <input type="hidden" id="Unit_@(product.Id)_@product.VariantId.Replace(".", "_")" name="UnitID" value="@unitId" /> 360 361 <button type="button" id="decreaseQtyButton" onclick="decreaseQtyButtonMiniCart_OnClick('@subId')" class="decrease_IncreaseQuantityButton_miniCart">-</button> 362 <input id="Quantity_@subId" name="Quantity" value="@valueQty" step="@stepQty" min="@minQty" max="@maxQty" class="form-control swift_quantity-field" style="min-width: 60px; max-width: 100px; z-index: 1" type="number" onKeyDown="CheckCharacter_OnlyDigits()"> 363 <button type="button" id="increaseQtyButton" onclick="increaseQtyButtonMiniCart_OnClick('@subId')" class="decrease_IncreaseQuantityButton_miniCart">+</button> 364 365 <input type="submit" class="d-none" onclick="event.preventDefault(); swift.Cart.Update(event)"> @* Fix for enterKey should not redirect to minicart page *@ 366 367 @if (!product.NeverOutOfStock) 368 { 369 <input type="hidden" name="Stock" value="@productStock"> 370 371 <template class="js-out-of-stock-notice"> 372 <div class="modal-header"> 373 <h1 class="modal-title fs-5">@Translate("Stock limit")</h1> 374 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="@Translate("Close")"></button> 375 </div> 376 <div class="modal-body"> 377 @Translate("There are not enough products in stock. The product might be sold out or discontinued. Please adjust the quantity.") 378 </div> 379 </template> 380 } 381 382 383 <div class="d-flex flex-row w-100"> 384 385 386 <button type="button" onclick="@showLoginModalOrAddToCart" class="btn btn-primary flex-fill js-add-to-cart-button" style="white-space: nowrap;margin-right: 2rem;" 387 title="@Translate("Add to cart")" id="AddToCartButton@(product.Id)_@PageviewCurrentParagraphID"> 388 <span class="text-nowrap d-flex align-items-center justify-content-center gap-2"> 389 @Translate("Add to cart") 390 </span> 391 </button> 392 393 394 <template class="js-min-quantity-warning"> 395 <div class="modal-header"> 396 <h1 class="modal-title fs-5">@Translate("The product could not be added to the cart")</h1> 397 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 398 </div> 399 <div class="modal-body"> 400 @Translate("The quantity is not valid. You must buy at least") @product.PurchaseMinimumQuantity 401 </div> 402 </template> 403 404 </div> 405 </form> 406 </div> 407 } 408 catch (Exception ex) 409 { 410 <div id="catchError">@ex.Message + @ex.StackTrace</div> 411 } 412 413 414 } 415 416 417 418 @if (arrayOfIDs1.Length > 0) 419 { 420 <div class="productPageHorizontalCarousel grid gap-0 superhome" id="productPageHorizontalCarousel3" style="visibility:hidden;"> 421 @*<h4 class="u-no-margin productPageHorizontalCarouselTitle titleWithLines"> <!--<a href=""> </a>--></h4>*@ 422 @if (!string.IsNullOrWhiteSpace(@CarouselDetails1)) 423 { 424 <p class="u-no-margin g-col-12 header-lead-carousel productPageHorizontalCarouselDescription">@CarouselDetails1</p> 425 } 426 <div class="productsCarousel g-col-12"> 427 <div class="@SliderDescription1@CarouselID1" style="max-width: 100vw;"> 428 @*@foreach (var product in masterProducts1)*@ 429 @foreach (var product_str in arrayOfIDs1) 430 { 431 try 432 { 433 var product = PS.GetProductById(product_str, "", Pageview.Area.EcomLanguageId.ToString()); 434 if(product.IsVariantMaster || !string.IsNullOrWhiteSpace(product.VariantId)) 435 { 436 continue; 437 } 438 if(product.Id == productPageProductId) 439 { 440 continue; 441 } 442 443 if(maxItemsToShow <= prodsCounter1) 444 { 445 continue; 446 } 447 prodsCounter1 = prodsCounter1 + 1; 448 449 string noStockErrorBaseId = "error-noStock-message"; 450 string stockSpanId = "stockSpan"; 451 string maxQtyErrorBaseId = "error-maxQty-message"; 452 string prodId = product.Id;// GetString("Ecom:Product.ID"); 453 string quantityId = "Quantity" + CarouselID1 + "_" + prodId; 454 string QuickAddSpan = "QuickAdd_Span_" + CarouselID1 + "_" + prodId; 455 string AddedToCartSpan = "AddedToCart_Span_" + CarouselID1 + "_" + prodId; 456 string prodName = product.Name;// GetString("Ecom:Product.Name"); 457 string prodNumber = product.Number;// GetString("Ecom:Product.Number"); 458 459 string prodMaxQty = ""; 460 string discountPercentage = ""; 461 string originalPrice = ""; 462 string onOffer = ""; 463 string comparisonPrice = ""; 464 string DiscountExplanation = ""; 465 foreach (var prodCustomField in product.ProductFieldValues) 466 { 467 if (prodCustomField.ProductField.SystemName == "MaxQty") 468 { 469 prodMaxQty = prodCustomField.Value.ToString(); 470 } 471 if (prodCustomField.ProductField.SystemName == "DiscountPercentage") 472 { 473 discountPercentage = prodCustomField.Value.ToString(); 474 } 475 if (prodCustomField.ProductField.SystemName == "OriginalPrice") 476 { 477 originalPrice = prodCustomField.Value.ToString(); 478 } 479 if (prodCustomField.ProductField.SystemName == "OnOffer") 480 { 481 onOffer = prodCustomField.Value.ToString(); 482 } 483 if (prodCustomField.ProductField.SystemName == "ComparisonPrice" && !string.IsNullOrWhiteSpace(prodCustomField.Value.ToString())) 484 { 485 comparisonPrice = "€" + CommonFunctions.HandleComparisonPriceAccordingToLaw(prodCustomField.Value.ToString()); 486 } 487 if (prodCustomField.ProductField.SystemName == "DiscountExplanation" && !string.IsNullOrWhiteSpace(prodCustomField.Value.ToString()) && DiscountExplanationEnabled) 488 { 489 DiscountExplanation = prodCustomField.Value.ToString(); 490 } 491 } 492 originalPrice = (originalPrice == "0") ? "" : ( onOffer == "Yes" ? "€" + originalPrice : ""); 493 494 495 string DiscountExplanation_Text = ""; 496 string DiscountExplanation_percentage = ""; 497 if(DiscountExplanationEnabled) 498 { 499 DiscountExplanation = DiscountExplanation.Trim().Replace("±","€").Replace("�","€"); 500 // split based on $ character DiscountExplanation_Text 501 var DiscountExplanation_Unsplitted = DiscountExplanation.Split("$".ToCharArray()); 502 if (DiscountExplanation_Unsplitted.Length == 2) 503 { 504 if (!string.IsNullOrWhiteSpace(DiscountExplanation_Unsplitted[0])) 505 { 506 DiscountExplanation_Text = DiscountExplanation_Unsplitted[0]; 507 } 508 if (!string.IsNullOrWhiteSpace(DiscountExplanation_Unsplitted[1])) 509 { 510 DiscountExplanation_percentage = DiscountExplanation_Unsplitted[1]; 511 } 512 DiscountExplanation_Text = Pageview.Area.CultureInfo.TwoLetterISOLanguageName == "el" ? Translate(DiscountExplanation_Text.Replace("+"," ")) : DiscountExplanation_Text; 513 DiscountExplanation_Text += " <span class='discountExplanationPercentage_Span'>€" + DiscountExplanation_percentage + "</span>"; 514 } 515 else 516 { 517 DiscountExplanation_Text = Pageview.Area.CultureInfo.TwoLetterISOLanguageName == "el" ? Translate(DiscountExplanation.Replace("+"," ")) : DiscountExplanation; 518 } 519 } 520 521 bool onlyMono = string.IsNullOrWhiteSpace(originalPrice) && !string.IsNullOrWhiteSpace(discountPercentage) && discountPercentage.Contains("Super") ? true : false; 522 523 long stockLocationID = Dynamicweb.Ecommerce.Common.Context.StockLocation is object ? Dynamicweb.Ecommerce.Common.Context.StockLocation.ID : 0; 524 double stockQuantity = product.Stock; 525 bool isOutOfStock = stockQuantity <= 0; 526 string outOfStockCss = isOutOfStock ? "outOfStockNT" : ""; 527 DateTime createdDate = product.Created; //!return DateTime.Now if NULL 528 529 //CS NT 20211206 AllowSubstitute changes 530 string allowSubOLValue ="1"; 531 if(cartLoop != null) 532 { 533 foreach (var i in cartLoop.Where(x => x.ProductId == prodId)) 534 { 535 //CS NT 20211206 536 foreach (var a in i.OrderLineFieldValues) 537 { 538 if (a.OrderLineFieldName == "AllowSubstitute") 539 { 540 allowSubOLValue = a.Value != null ? a.Value.ToString() : "" ; 541 } 542 } 543 } 544 } 545 string productImage_CS = "/Files/Images/Products/" + product.Number.Trim() + ".jpg&AlternativeImage=/Images/missing_image.jpg"; 546 var primaryGroup = product.GetDefaultGroupByShopId(shopId); 547 if(primaryGroup == null) 548 { 549 continue; 550 } 551 //var productLink = "Default.aspx?ID=" + Dynamicweb.Context.Current.Request["ID"] + "&GroupID=" + primaryGroup.Id + "&ProductID=" + product.IdUrlEncoded; 552 var productLink = baseCategoryBasedOnLanguage + "GroupID=" + primaryGroup.Id + "&ProductID=" + product.IdUrlEncoded; 553 554 <div id="Product" class="grid__col-3 product-list__grid-item dw-mod productPageHorizontalProductInCarousel @outOfStockCss" > 555 556 <div class="grid__cell product-list__grid-item__image dw-mod "> 557 <a href="@productLink" onclick="Scroll.SavePosition(event)" class="u-block u-position-relative"> 558 <img class="grid__cell-img grid__cell-img--centered b-lazy b-loaded" src="@imagePrefix@productImage_CS" alt="@prodName"> 559 </a> 560 @if(onlyMono) 561 { 562 <div class="position-absolute" style="bottom:0;left:0;"> 563 <img style="width: 50px;" src="/Admin/Public/GetImage.ashx?Width=100&Height=90&Crop=5&DoNotUpscale=True&FillCanvas=True&Image=/Files/Images/General/SUPER LABEL ΜΟΝΟ.png&AlternativeImage=/Images/missing_image.jpg" alt="Only Mono Price" title="" data-image="/Files/Images/SUPER LABEL ΜΟΝΟ.png"> 564 </div> 565 }else if(!string.IsNullOrWhiteSpace(discountPercentage)) 566 { 567 <div class="position-absolute top-0 left-0 stickers-container stickers-container--bottom-right dw-mod"> 568 <div class="stickers-container__tag--sale">@discountPercentage</div> 569 </div> 570 } 571 @if((createdDate.AddDays(newBadgePublicationDays) > DateTime.Now)) 572 { 573 <div class="position-absolute top-0 right-0 p-2 p-lg-3 ps-0 ps-lg-0 fs-6"> 574 <span class="h7"><span class="badge new-nt rounded-0"></span></span> 575 </div> 576 } 577 </div> 578 579 <div class="horizontalCarouselDetailsBlock"> 580 581 @if(DiscountExplanationEnabled) 582 { 583 <div class="DiscountExplanation_Container"> 584 @if(!string.IsNullOrWhiteSpace(DiscountExplanation_Text)) 585 { 586 <div style="width:100%" class="DiscountExplanation">@DiscountExplanation_Text</div> 587 } 588 </div> 589 590 } 591 592 <a href="@productLink" onclick="Scroll.SavePosition(event)" title="@prodName "> 593 <span class="horizontalCarouselProductName"><span>@product.Name</span></span> 594 </a> 595 <div class="item-number dw-mod">@product.Number</div> 596 @*CS NT Aligned OriginalPrice, Price, ComparisonPrice 20240605*@ 597 <div class="fs-5 m-0 d-flex flex-wrap flex-column align-items-center" style="row-gap: 0 !important" > 598 <div class="fs-5 m-0 m-0 w-100 align-items-center align-items-center"style="height:30px;"> 599 @if(!string.IsNullOrWhiteSpace(originalPrice)) 600 { 601 <span class="text-decoration-line-through-with-color-nt opacity-75 original-price-nt before-price" style="">&nbsp; @originalPrice &nbsp;</span> 602 } 603 </div> 604 <span class=" p-0"> 605 <span class="text-price">@product.Price</span> 606 <span class="opacity-75" style="font-size:14px;line-height: 2;">@comparisonPrice</span> 607 </span> 608 </div> 609 610 <p id="allowSubOLValue_Paragraph_@prodId" hidden>@Translate("Allow Substitute in Cart") <span id="allowSubOLValue_Span_@prodId">@allowSubOLValue</span></p> 611 <div class="error-noStock-message" style="display:none" id="@noStockErrorBaseId@prodId"> 612 <br /> 613 <div class="error-block-nextech">@Translate("Available stock is") <span id="@stockSpanId@prodId"></span></div> 614 </div> 615 616 <div class="error-maxQty-message" style="display:none" id="@maxQtyErrorBaseId@prodId"> 617 <br /> 618 <div class="error-block-nextech">@Translate("The maximum quantity you can buy is") @prodMaxQty</div> 619 </div> 620 621 <div class="buttons-collection buttons-collection--center"> 622 @if(!isOutOfStock) 623 { 624 @*CS NT Add to cart button With quantity 625 <button class=" btn--condensed btn btn--primary dw-mod" id="CartButton_@prodId" onclick="ProductAddToCart(event, { id: '@prodId',variantId: '',unitId: '',productInfo: {&quot;link&quot;:&quot;&quot;,&quot;image&quot;:&quot;%2fFiles%2fImages%2fEcom%2fProducts%2f@prodNumber.jpg&quot;,&quot;name&quot;:&quot;@prodName&quot;,&quot;variantName&quot;:&quot;&quot;,&quot;unitName&quot;:&quot;&quot;,&quot;googleImpression&quot;:null},quantity: parseFloat(document.getElementById('@quantityId').value)}, @CarouselID1,@isGuestUser);" type="button"> 626 <i class="fal fa-shopping-bag u-flex--align-center"></i> 627 </button> 628 *@ 629 630 @RenderAddToCartNT(product) 631 @*CS NT QUICK ADD 632 <input id="@quantityId" name="@quantityId" min="1" value="1" type="number" class=" dw-mod" hidden data-qtyincart="0" data-maxqty="@prodMaxQty" data-id="" > 633 634 <button class="btn btn-primary js-add-to-cart-button" style="border-radius: 25px;" id="CartButton_@prodId" onclick="ProductAddToCart(event, { id: '@prodId',variantId: '',unitId: '',productInfo: {&quot;link&quot;:&quot;&quot;,&quot;image&quot;:&quot;%2fFiles%2fImages%2fEcom%2fProducts%2f@prodNumber.jpg&quot;,&quot;name&quot;:&quot;@prodName&quot;,&quot;variantName&quot;:&quot;&quot;,&quot;unitName&quot;:&quot;&quot;,&quot;googleImpression&quot;:null},quantity: 1}, @CarouselID1,@isGuestUser);" type="button"> 635 <span id="@QuickAddSpan">@Translate("Add to cart")</span><span id="@AddedToCartSpan" hidden>@Translate("Added")</span> 636 </button> 637 *@ 638 639 @*CS NT HIDE Add to cart button With quantity -> 640 <div class="buttons-collection buttons-collection--center"> 641 <button type="button" id="decreaseQtyButton" onclick="decreaseQtyButton_HorizontalCarousel_OnClick('@prodId','@CarouselID1')" class="decrease_IncreaseQuantityButton_VerticalCarousel">-</button> 642 <input type="number" class=" u-pull--right use-btn-primary-height verticalCarouselQuantityInput" id="@quantityId" name="@quantityId" value="1" min="1" data-qtyincart="" data-id="@prodId" data-productId="@prodId" > 643 <button type="button" id="increaseQtyButton" onclick="increaseQtyButton_HorizontalCarousel_OnClick('@prodId','@CarouselID1')" class="decrease_IncreaseQuantityButton_VerticalCarousel">+</button> 644 645 <button class=" btn--condensed btn btn--primary u-full-width dw-mod" id="CartButton_@prodId" onclick="ProductAddToCart(event, { id: '@prodId',variantId: '',unitId: '',productInfo: {&quot;link&quot;:&quot;&quot;,&quot;image&quot;:&quot;%2fFiles%2fImages%2fEcom%2fProducts%2f@prodNumber.jpg&quot;,&quot;name&quot;:&quot;@prodName&quot;,&quot;variantName&quot;:&quot;&quot;,&quot;unitName&quot;:&quot;&quot;,&quot;googleImpression&quot;:null},quantity: parseFloat(document.getElementById('@quantityId').value)}, @CarouselID1,@isGuestUser); " type="button"> 646 @Translate("Add to cart") 647 </button> 648 </div> 649 *@ 650 651 }else 652 { 653 <div class="outOfStockDiv_Carousel">@Translate("Out of stock")</div> 654 } 655 </div> 656 </div> 657 658 </div> 659 660 } 661 catch (Exception ex) 662 { 663 Logger.Instance.Log(ErrorLevel.DebugInfo, "RaptorRecommendation.Recommendations.AvailableLoops.GetOverAllTopSelling_Loop() ex: " + ex.Message + " Stack: " + ex.StackTrace); 664 prodsCounter1 = prodsCounter1 - 1; 665 } 666 } 667 </div> 668 </div> 669 </div> 670 671 <script type="text/javascript"> 672 $(document).ready(function () { 673 674 $('.slider-nav' + @CarouselID1).slick({ 675 slidesToShow: 8, 676 slidesToScroll: 4, 677 dots: true, 678 focusOnSelect: false, 679 autoplay: false, 680 autoplayspeed: 2000, 681 arrows: true, 682 prevArrow:"<button type='button' class='slick-prev'><span class='fa fa-angle-left'></span></button>", 683 nextArrow:"<button type='button' class='slick-next'><span class='fa fa-angle-right'></span></button>", 684 responsive: [ 685 { 686 breakpoint: 1024, 687 settings: { 688 slidesToShow: 8, 689 slidesToScroll: 4, 690 infinite: true, 691 dots: false, 692 arrows: false, 693 autoplay: true, 694 autoplayspeed: 2000 695 } 696 }, 697 { 698 breakpoint: 600, 699 settings: { 700 slidesToShow: 2, 701 slidesToScroll: 2, 702 arrows: false 703 } 704 }, 705 { 706 breakpoint: 480, 707 settings: { 708 slidesToShow: 2, 709 slidesToScroll: 2, 710 arrows: false 711 712 } 713 }, 714 { 715 breakpoint: 360, 716 settings: { 717 slidesToShow: 2, 718 slidesToScroll: 2, 719 arrows: false 720 } 721 } 722 // You can unslick at a given breakpoint now by adding: 723 // settings: "unslick" 724 // instead of a settings object 725 ] 726 727 }); 728 729 $('a[data-slide]').click(function (e) { 730 e.preventDefault(); 731 var slideno = $(this).data('slide'); 732 $('.slider-nav' + @CarouselID1).slick('slickGoTo', slideno - 1); 733 }); 734 735 $('#productPageHorizontalCarousel3').css('visibility','inherit'); 736 }); 737 738 </script> 739 } 740 741 742 743 744 <script type="text/javascript"> 745 function decreaseQtyButton_HorizontalCarousel_OnClick(id,carouselId){ 746 $('#Quantity' + carouselId + '_' + id).val( function(i, oldval) { 747 if(parseInt( oldval, 10) == 0) 748 return 0; 749 else 750 return parseInt( oldval, 10) - 1; 751 }); 752 753 }; 754 755 function increaseQtyButton_HorizontalCarousel_OnClick(id,carouselId){ 756 console.log('increaseQtyButton_HorizontalCarousel_OnClick id: ' + id + ' carouselId: ' + carouselId); 757 $('#Quantity' + carouselId + '_' + id).val( function(i, oldval) { 758 return parseInt( oldval, 10) + 1; 759 }); 760 }; 761 </script>