An error occurred while attaching module (Dynamicweb.Frontend.Content)
System.NullReferenceException: Object reference not set to an instance of an object.
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)
System.NullReferenceException: Object reference not set to an instance of an object. 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_06cfe40587f848a487a7240519d5c4a3.Execute() in D:\dynamicweb.net\Solutions\Dynamicweb\bomedys.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_ProductDetailsImage.cshtml:line 78 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 6 @functions { 7 public ProductViewModel product { get; set; } = new ProductViewModel(); 8 public string galleryLayout { get; set; } 9 public string[] supportedImageFormats { get; set; } 10 public string[] supportedVideoFormats { get; set; } 11 public string[] supportedDocumentFormats { get; set; } 12 public string[] allSupportedFormats { get; set; } 13 14 public class RatioSettings { 15 public string Ratio { get; set; } 16 public string CssClass { get; set; } 17 public string CssVariable { get; set; } 18 public string Fill { get; set; } 19 } 20 21 public RatioSettings GetRatioSettings(string size = "desktop") { 22 var ratioSettings = new RatioSettings(); 23 24 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 25 ratio = ratio != "0" ? ratio : ""; 26 string cssClass = ratio != "" && ratio != "fill" ? " ratio" : ""; 27 string cssVariable = ratio != "" && ratio != "fill" ? "--bs-aspect-ratio: " + ratio : ""; 28 cssClass = ratio == "fill" && size == "mobile" ? " ratio" : cssClass; 29 cssVariable = ratio == "fill" && size == "mobile" ? "--bs-aspect-ratio: 66%" : cssVariable; 30 31 ratioSettings.Ratio = ratio; 32 ratioSettings.CssClass = cssClass; 33 ratioSettings.CssVariable = cssVariable; 34 ratioSettings.Fill = ratio == "fill" ? " h-100" : ""; 35 36 return ratioSettings; 37 } 38 39 public string GetArrowsColor() 40 { 41 var invertColor = Model.Item.GetBoolean("InvertModalArrowsColor"); 42 var arrowsColor = invertColor ? " carousel-dark" : string.Empty; 43 return arrowsColor; 44 } 45 } 46 47 @{ 48 ProductViewModel product = null; 49 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 50 { 51 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 52 } 53 else if (Pageview.Item["DummyProduct"] != null) 54 { 55 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 56 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 57 58 if (productList?.Products is object) 59 { 60 product = productList.Products[0]; 61 } 62 } 63 } 64 65 @if (product is object) { 66 @* Supported formats *@ 67 supportedImageFormats = new string[] { ".jpg", ".Jpg", ".JPG", ".jpeg", ".webp", ".png", ".Png", ".PNG", ".gif", ".bmp", ".tiff", ".Tiff", ".TIFF" }; 68 supportedVideoFormats = new string[] { "youtu.be", "youtube", "vimeo", ".mp4", ".webm" }; 69 supportedDocumentFormats = new string[] { ".pdf", ".docx", ".xlsx", ".ppt", "pptx" }; 70 allSupportedFormats = supportedImageFormats.Concat(supportedVideoFormats).Concat(supportedDocumentFormats).ToArray(); 71 72 @* Collect the assets *@ 73 var selectedAssetCategories = Model.Item.GetRawValueString("ImageAssets").Split(',').ToList(); 74 bool includeImagePatternImages = Model.Item.GetBoolean("ImagePatternImages"); 75 76 @* Needed image data collection to support both DefaultImage, ImagePatterns and Image Assets *@ 77 string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : ""; 78 IEnumerable<MediaViewModel> assetsImages = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets); 79 assetsImages = assetsImages.OrderByDescending(x => x.Value.Equals(defaultImage)); 80 IEnumerable<MediaViewModel> assetsList = new MediaViewModel[]{}; 81 assetsList = assetsList.Union(assetsImages); 82 assetsList = includeImagePatternImages ? assetsList.Union(product.ImagePatternImages) : assetsList; 83 assetsList = includeImagePatternImages && assetsList.Count() == 0 ? assetsList.Append(product.DefaultImage) : assetsList; 84 85 bool defaultImageFallback = Model.Item.GetBoolean("DefaultImageFallback"); 86 bool showOnlyPrimaryImage = Model.Item.GetBoolean("ShowOnlyPrimaryImage"); 87 88 int totalAssets = 0; 89 if (showOnlyPrimaryImage == false) { 90 foreach (MediaViewModel asset in assetsList) { 91 var assetValue = asset.Value.ToLower(); 92 foreach (string format in allSupportedFormats) { 93 if (assetValue.Contains(format) ) { 94 totalAssets++; 95 } 96 } 97 } 98 } 99 100 if((totalAssets == 0 && product.DefaultImage != null && selectedAssetCategories.Count() == 0) || (showOnlyPrimaryImage == true && product.DefaultImage != null) || totalAssets == 0 && defaultImageFallback) 101 { 102 assetsList = new List<MediaViewModel>(){ product.DefaultImage }; 103 totalAssets = 1; 104 } 105 106 107 @* Theme settings *@ 108 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 109 110 var badgeParms = new Dictionary<string, object>(); 111 badgeParms.Add("size", "h5"); 112 badgeParms.Add("saleBadgeType", Model.Item.GetRawValue("SaleBadgeType")); 113 badgeParms.Add("saleBadgeCssClassName", Model.Item.GetRawValue("SaleBadgeDesign")); 114 badgeParms.Add("newBadgeCssClassName", Model.Item.GetRawValue("NewBadgeDesign")); 115 badgeParms.Add("newPublicationDays", Model.Item.GetInt32("NewPublicationDays")); 116 badgeParms.Add("campaignBadgesValues", Model.Item.GetRawValueString("CampaignBadges")); 117 118 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("SaleBadgeDesign")) && Model.Item.GetRawValueString("SaleBadgeDesign") != "none" ? true : false; 119 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("NewBadgeDesign")) && Model.Item.GetRawValueString("NewBadgeDesign") != "none" ? true : false; 120 DateTime createdDate = product.Created.Value; 121 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false; 122 showBadges = (newBadgeEnabled && Model.Item.GetInt32("NewPublicationDays") == 0) || (newBadgeEnabled && (createdDate.AddDays(Model.Item.GetInt32("NewPublicationDays")) > DateTime.Now)) ? true : showBadges; 123 showBadges = !string.IsNullOrEmpty(Model.Item.GetRawValueString("CampaignBadges")) ? true : showBadges; 124 125 @* Get assets from selected categories or get all assets *@ 126 if (totalAssets != 0) { 127 int assetNumber = 0; 128 int thumbnailNumber = 0; 129 int modalAssetNumber = 0; 130 131 <div class="h-100@(theme) position-relative [email protected]()"> 132 <div id="[email protected]" class="carousel@(GetArrowsColor())" data-bs-ride="carousel"> 133 <div class="carousel-inner h-100"> 134 @foreach (MediaViewModel asset in assetsList) { 135 var assetValue = asset.Value.ToLower(); 136 foreach (string format in allSupportedFormats) { 137 if (assetValue.Contains(format)) { 138 string activeSlide = assetNumber == 0 ? "active" : ""; 139 140 <div class="carousel-item @activeSlide" data-bs-interval="99999"> 141 @{@RenderAsset(asset, assetNumber, "mobile")} 142 </div> 143 assetNumber++; 144 } 145 } 146 } 147 </div> 148 </div> 149 150 @if (totalAssets > 1) { 151 <div id="[email protected]" class="grid grid-10 gap-2 overflow-x-auto my-3"> 152 @foreach (MediaViewModel asset in assetsList) { 153 var assetValue = asset.Value; 154 foreach (string format in allSupportedFormats) { 155 if (assetValue.Contains(format)) { 156 string imagePath = Dynamicweb.Context.Current.Server.UrlEncode(assetValue); 157 imagePath = assetValue.Contains("youtu.be") || assetValue.Contains("youtube") ? "https://img.youtube.com/vi/" + assetValue.Substring(assetValue.LastIndexOf('/') + 1) + "/mqdefault.jpg" : imagePath; 158 string imagePathThumb = !imagePath.Contains("youtube") && !imagePath.Contains(".mp4") ? $"/Admin/Public/GetImage.ashx?image={imagePath}&width=180&format=webp" : imagePath; 159 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 160 161 string videoId = assetValue.Substring(assetValue.LastIndexOf('/') + 1); 162 string vimeoJsClass = assetValue.Contains("vimeo") ? "js-vimeo-video-thumbnail" : ""; 163 164 bool isDocument = false; 165 foreach (string documentFormat in supportedDocumentFormats) { 166 if (assetValue.Contains(documentFormat)) { 167 isDocument = true; 168 } 169 } 170 171 string assetName = asset.Name; 172 assetName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 173 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 174 175 if (!isDocument) { 176 RatioSettings ratioSettings = GetRatioSettings("desktop"); 177 178 <div class="border outline-none @(ratioSettings.CssClass)" style="@(ratioSettings.CssVariable); cursor: pointer" data-bs-target="#[email protected]" data-bs-slide-to="@thumbnailNumber"> 179 <div class="d-flex align-items-center justify-content-center overflow-hidden position-absolute h-100"> 180 @foreach (string videoFormat in supportedVideoFormats) { //Videos 181 if (assetValue.Contains(videoFormat)) { 182 <div class="icon-3 position-absolute text-light" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 183 } 184 } 185 </div> 186 @if (!imagePathThumb.Contains(".mp4")) { 187 <img src="@imagePathThumb" alt="@assetName" @assetTitle class="p-0 p-lg-1 @vimeoJsClass w-100 h-100" style="object-fit: contain" data-video-id="@videoId"> 188 } else { 189 string videoType = Path.GetExtension(asset.Value).ToLower(); 190 191 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 192 <source src="@imagePathThumb" type="video/@videoType.Replace(".", "")"> 193 </video> 194 } 195 </div> 196 } else { 197 <a href="@assetValue" class="ratio ratio-4x3 border outline-none" style="cursor: pointer" download title="@asset.Value"> 198 @if (asset.Value.Contains(".pdf")) { 199 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 200 <div class="icon-3 position-absolute text-light" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 201 </div> 202 <img src="@imagePathThumb" alt="@assetName" @assetTitle class="p-0 p-lg-1 mw-100 mh-100" style="object-fit: cover;"> 203 } else { 204 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 205 <div class="icon-3 position-absolute" style="z-index: 1">@ReadFile(iconPath + "file-text.svg")</div> 206 </div> 207 } 208 </a> 209 } 210 211 thumbnailNumber++; 212 } 213 } 214 } 215 </div> 216 } 217 218 @if (showBadges) { 219 <div class="position-absolute top-0 left-0 p-2 p-lg-3"> 220 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms) 221 </div> 222 } 223 </div> 224 225 @* Modal with slides *@ 226 <div class="modal fade swift_products-details-images-modal" id="[email protected]" tabindex="-1" aria-labelledby="[email protected]" aria-hidden="true"> 227 <div class="modal-dialog modal-dialog-centered modal-xl"> 228 <div class="modal-content"> 229 <div class="modal-header visually-hidden"> 230 <h5 class="modal-title" id="[email protected]">@product.Title</h5> 231 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 232 </div> 233 <div class="modal-body p-2 p-lg-3 h-100"> 234 <div id="[email protected]" class="carousel@(GetArrowsColor()) h-100" data-bs-ride="carousel"> 235 <div class="carousel-inner h-100"> 236 @foreach (MediaViewModel asset in assetsList) { 237 var assetValue = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 238 foreach (string format in supportedImageFormats.Concat(supportedVideoFormats).ToArray()) { 239 if (assetValue.Contains(format) ) { 240 string imagePath = assetValue; 241 string activeSlide = modalAssetNumber == 0 ? "active" : ""; 242 243 var parms = new Dictionary<string, object>(); 244 parms.Add("cssClass", "d-block mw-100 mh-100 m-auto"); 245 parms.Add("fullwidth", true); 246 parms.Add("columns", Model.GridRowColumnCount); 247 248 <div class="carousel-item @activeSlide h-100" data-bs-interval="99999"> 249 @foreach (string imageFormat in supportedImageFormats) { //Images 250 if (assetValue.Contains(imageFormat)) { 251 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 252 } 253 } 254 255 @foreach (string videoFormat in supportedVideoFormats) { //Videos 256 if (assetValue.Contains(videoFormat)) { 257 {@RenderVideoPlayer(asset, "modal")} 258 } 259 } 260 </div> 261 262 modalAssetNumber++; 263 } 264 } 265 } 266 <button class="carousel-control-prev" type="button" data-bs-target="#[email protected]" data-bs-slide="prev"> 267 <span class="carousel-control-prev-icon" aria-hidden="true"></span> 268 <span class="visually-hidden">@Translate("Previous")</span> 269 </button> 270 <button class="carousel-control-next" type="button" data-bs-target="#[email protected]" data-bs-slide="next"> 271 <span class="carousel-control-next-icon" aria-hidden="true"></span> 272 <span class="visually-hidden">@Translate("Next")</span> 273 </button> 274 </div> 275 </div> 276 </div> 277 </div> 278 </div> 279 </div> 280 } else if (Pageview.IsVisualEditorMode) { 281 RatioSettings ratioSettings = GetRatioSettings("desktop"); 282 283 <div class="h-100 @theme"> 284 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> 285 <img src="/Files/Images/missing_image.jpg" loading="lazy" decoding="async" class="mh-100 mw-100" style="object-fit: cover;"> 286 </div> 287 </div> 288 } 289 } else if (Pageview.IsVisualEditorMode) { 290 <div class="alert alert-dark m-0">@Translate("No products available")</div> 291 } 292 293 @helper RenderAsset(MediaViewModel asset, int assetNumber, string size = "desktop") { 294 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 295 string assetValue = asset.Value; 296 297 <div class="h-100 @(theme)"> 298 @foreach (string format in supportedImageFormats) { //Images 299 if (assetValue.Contains(format)) { 300 {@RenderImage(asset, assetNumber, size)} 301 } 302 } 303 @foreach (string format in supportedVideoFormats) { //Videos 304 if (assetValue.Contains(format)) { 305 if (Model.Item.GetString("OpenVideoInModal") == "true") { 306 {@RenderVideoScreendump(asset, assetNumber, size)} 307 } else { 308 {@RenderVideoPlayer(asset, size)} 309 } 310 } 311 } 312 @foreach (string format in supportedDocumentFormats) { //Documents 313 if (assetValue.Contains(format)) { 314 {@RenderDocument(asset, assetNumber, size)} 315 } 316 } 317 </div> 318 } 319 320 @helper RenderImage(MediaViewModel asset, int number, string size = "desktop") { 321 if (product is object) 322 { 323 string productName = product.Name; 324 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 325 string imageLinkPath = Dynamicweb.Context.Current.Server.UrlEncode(imagePath); 326 327 RatioSettings ratioSettings = GetRatioSettings(size); 328 329 var parms = new Dictionary<string, object>(); 330 parms.Add("alt", productName + asset.Keywords); 331 parms.Add("itemprop", "image"); 332 parms.Add("fullwidth", true); 333 parms.Add("columns", Model.GridRowColumnCount); 334 if (!string.IsNullOrEmpty(asset.DisplayName)) { 335 parms.Add("title", asset.DisplayName); 336 } 337 338 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") { 339 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 340 } else { 341 parms.Add("cssClass", "mw-100 mh-100"); 342 } 343 344 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" data-bs-toggle="modal" data-bs-target="#[email protected]"> 345 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#[email protected]" data-bs-slide-to="@number"> 346 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 347 </div> 348 </a> 349 } 350 } 351 352 @helper RenderVideoScreendump(MediaViewModel asset, int number, string size = "desktop") { 353 if (product is object) 354 { 355 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 356 357 string videoScreendumpPath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : ""; 358 string videoId = videoScreendumpPath.Substring(videoScreendumpPath.LastIndexOf('/') + 1); 359 videoScreendumpPath = videoScreendumpPath.Contains("youtu.be") || videoScreendumpPath.Contains("youtube") ? "https://img.youtube.com/vi/" + videoId + "/maxresdefault.jpg" : videoScreendumpPath; 360 361 string vimeoJsClass = videoScreendumpPath.Contains("vimeo") ? "js-vimeo-video-thumbnail" : ""; 362 videoScreendumpPath = videoScreendumpPath.Contains("vimeo") ? "" : videoScreendumpPath; 363 364 string productName = product.Name; 365 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 366 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 367 368 RatioSettings ratioSettings = GetRatioSettings(size); 369 370 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable); cursor: pointer" data-bs-toggle="modal" data-bs-target="#[email protected]"> 371 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#[email protected]" data-bs-slide-to="@number"> 372 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 373 @if (!videoScreendumpPath.Contains(".mp4")) 374 { 375 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@productName" @assetTitle class="@vimeoJsClass mw-100 mh-100" data-video-id="@videoId" style="object-fit: cover;" onload="CheckIfVideoThumbnailExist(this)"> 376 } 377 else 378 { 379 string videoType = Path.GetExtension(asset.Value).ToLower(); 380 381 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 382 <source src="@asset.Value" type="video/@videoType.Replace(".", "")"> 383 </video> 384 } 385 </div> 386 </div> 387 388 <script> 389 function CheckIfVideoThumbnailExist(image) { 390 if (image.width == 120) { 391 const lowQualityImage = "https://img.youtube.com/vi/@(videoId)/hqdefault.jpg" 392 image.src = lowQualityImage; 393 } 394 } 395 </script> 396 } 397 } 398 399 @helper RenderVideoPlayer(MediaViewModel asset, string size = "desktop") { 400 if (product is object) 401 { 402 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Name; 403 string assetValue = asset.Value; 404 string videoId = asset.Value.Substring(asset.Value.LastIndexOf('/') + 1); 405 string type = assetValue.Contains("youtu.be") || assetValue.Contains("youtube") ? "youtube" : ""; 406 type = assetValue.Contains("vimeo") ? "vimeo" : type; 407 type = assetValue.Contains(".mp4") || assetValue.Contains(".webm") ? "selfhosted" : type; 408 409 string openInModal = Model.Item.GetString("OpenVideoInModal"); 410 bool autoPlay = Model.Item.GetBoolean("VideoAutoPlay"); 411 412 <div class="h-100" itemscope itemtype="https://schema.org/VideoObject"> 413 <span class="visually-hidden" itemprop="name">@assetName</span> 414 <span class="visually-hidden" itemprop="contentUrl">@asset.Value</span> 415 <span class="visually-hidden" itemprop="thumbnailUrl">@asset.Value</span> 416 417 @if (type != "selfhosted") 418 { 419 <div 420 id="player_@(Pageview.CurrentParagraph.ID)_@(videoId)_@size" 421 class="plyr__video-embed" 422 data-plyr-provider="@(type)" 423 data-plyr-embed-id="@videoId" 424 style="--plyr-color-main: var(--swift-foreground-color); height: 100%"> 425 </div> 426 427 <script type="module" src="/Files/Templates/Designs/Swift/Assets/js/plyr.js"></script> 428 429 <script type="module"> 430 var player = new Plyr('#player_@(Pageview.CurrentParagraph.ID)_@(videoId)_@size', { 431 type: 'video', 432 youtube: { 433 noCookie: true, 434 showinfo: 0 435 }, 436 fullscreen: { 437 enabled: true, 438 iosNative: true, 439 } 440 }); 441 442 @if (autoPlay && openInModal == "false") 443 { 444 <text> 445 player.config.autoplay = true; 446 player.config.muted = true; 447 player.config.volume = 0; 448 player.media.loop = true; 449 450 player.on('ready', function() { 451 if (player.config.autoplay === true) { 452 player.media.play(); 453 } 454 }); 455 </text> 456 } 457 458 @if (openInModal == "true") 459 { 460 <text> 461 var productDetailsGalleryModal = document.querySelector('#[email protected]') 462 productDetailsGalleryModal.addEventListener('hidden.bs.modal', function (event) { 463 player.media.pause(); 464 }) 465 </text> 466 } 467 </script> 468 } 469 else 470 { 471 string autoPlayAttributes = (autoPlay && openInModal == "false") ? "loop autoplay muted playsinline" : ""; 472 string videoType = Path.GetExtension(assetValue).ToLower(); 473 474 <video preload="auto" @autoPlayAttributes class="h-100 w-100" style="object-fit: cover;" controls> 475 <source src="@assetValue" type="video/@videoType.Replace(".", "")"> 476 </video> 477 } 478 </div> 479 } 480 } 481 482 @helper RenderDocument(MediaViewModel asset, int number, string size = "desktop") { 483 if (product is object) 484 { 485 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 486 487 string productName = product.Name; 488 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 489 string imageLinkPath = imagePath; 490 491 RatioSettings ratioSettings = GetRatioSettings(size); 492 493 var parms = new Dictionary<string, object>(); 494 parms.Add("alt", productName + asset.Keywords); 495 parms.Add("itemprop", "image"); 496 parms.Add("fullwidth", true); 497 parms.Add("columns", Model.GridRowColumnCount); 498 if (!string.IsNullOrEmpty(asset.DisplayName)) { 499 parms.Add("title", asset.DisplayName); 500 } 501 502 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") { 503 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 504 } else { 505 parms.Add("cssClass", "mw-100 mh-100"); 506 } 507 508 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download alt="@Translate("Download")"> 509 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 510 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 511 @if (asset.Value.Contains(".pdf")) { 512 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 513 } 514 </div> 515 </a> 516 } 517 } 518
Error executing template "/Designs/Swift/Paragraph/Swift_ProductDetailsInfo.cshtml" System.NullReferenceException: Object reference not set to an instance of an object. at CompiledRazorTemplates.Dynamic.RazorEngine_0864e43f345d48ff980dca7d088791e1.Execute() in D:\dynamicweb.net\Solutions\Dynamicweb\bomedys.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_ProductDetailsInfo.cshtml:line 74 at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader) at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer) at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter) at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template) at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template) at Dynamicweb.Rendering.Template.RenderRazorTemplate()
1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 @using Dynamicweb.Ecommerce.CustomerExperienceCenter.Favorites 4 @using Dynamicweb.Ecommerce.Products.FieldDisplayGroups 5 @using Dynamicweb.Frontend 6 @using Dynamicweb.Core 7 @using System.Drawing 8 @using Dynamicweb.Environment 9 10 @functions { 11 //Find contrast color (white, black) 12 public static string GetContrastColor(string hexString) 13 { 14 System.Drawing.Color bg = System.Drawing.ColorTranslator.FromHtml(hexString); 15 16 int nThreshold = 105; 17 int bgDelta = Convert.ToInt32((bg.R * 0.299) + (bg.G * 0.587) + 18 (bg.B * 0.114)); 19 20 string foreColor = (255 - bgDelta < nThreshold) ? "#333" : "#fff"; 21 return foreColor; 22 } 23 } 24 25 @{ 26 string googleAnalyticsTrackingID = Pageview.AreaSettings.GetString("GoogleAnalyticsTrackingID"); 27 string googleAnalyticsMeasurementID = Pageview.AreaSettings.GetString("GoogleAnalyticsMeasurementID"); 28 var cookieOptInLevel = CookieManager.GetCookieOptInLevel(); 29 bool allowTracking = cookieOptInLevel == CookieOptInLevel.All || (cookieOptInLevel == CookieOptInLevel.Functional && CookieManager.GetCookieOptInCategories().Contains("Statistical")); 30 31 ProductViewModel product = new ProductViewModel(); 32 33 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 34 { 35 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 36 } 37 38 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", ""); 39 bool anonymousUser = Pageview.User == null; 40 bool isErpConnectionDown = !Dynamicweb.Ecommerce.DynamicwebLiveIntegration.TemplatesHelper.IsWebServiceConnectionAvailable(); 41 bool hideAddToCart = anonymousUsersLimitations.Contains("cart") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHideAddToCart") && isErpConnectionDown; 42 hideAddToCart = product?.VariantInfo?.VariantInfo != null && Model.Item.GetBoolean("HideVariantSelector") ? true : hideAddToCart; 43 bool hideStock = Model.Item.GetBoolean("HideStockState") || (Pageview.AreaSettings.GetBoolean("ErpDownHideStock") && isErpConnectionDown); 44 bool hidePrice = anonymousUsersLimitations.Contains("price") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHidePrices") && isErpConnectionDown; 45 bool hideFavoritesSelector = !string.IsNullOrEmpty(Model.Item.GetString("HideFavoritesSelector")) ? Model.Item.GetBoolean("HideFavoritesSelector") : false; 46 47 bool isDiscontinued = product.Discontinued; 48 bool IsNeverOutOfStock = product.NeverOutOfstock; 49 string[] variantId = { }; 50 51 if (product?.VariantId != null) { 52 variantId = product.VariantId.Split('.'); 53 } 54 55 string disableAddToCart = (product.StockLevel <= 0) ? "disabled" : ""; 56 disableAddToCart = isDiscontinued ? "disabled" : disableAddToCart; 57 disableAddToCart = IsNeverOutOfStock ? "" : disableAddToCart; 58 59 // Does product has a expected delivery data 60 bool hasExpectedDelivery = product.ExpectedDelivery != null && product.ExpectedDelivery > DateTime.Now; 61 string expectedDeliveryDate = product.ExpectedDelivery?.ToShortDateString() ?? ""; 62 63 string url = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("CartService")); 64 if (!url.Contains("LayoutTemplate")) 65 { 66 url += url.Contains("?") ? "&LayoutTemplate=Swift_MiniCart.cshtml" : "?LayoutTemplate=Swift_MiniCart.cshtml"; 67 } 68 69 IEnumerable<string> selectedDisplayGroups = Model.Item.GetRawValueString("MainFeatures").Split(',').ToList(); 70 List<CategoryFieldViewModel> mainFeatures = new List<CategoryFieldViewModel>(); 71 72 foreach (var selection in selectedDisplayGroups) 73 { 74 foreach (CategoryFieldViewModel group in product.FieldDisplayGroups.Values) 75 { 76 if (selection == group.Id) 77 { 78 mainFeatures.Add(group); 79 } 80 } 81 } 82 83 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 84 85 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "display-6"); 86 87 string contentPadding = Model.Item.GetRawValueString("ContentPadding", ""); 88 contentPadding = contentPadding == "small" ? "p-2 p-md-3" : contentPadding; 89 contentPadding = contentPadding == "large" ? "p-4 p-md-5" : contentPadding; 90 91 string quantityPricesLayout = Model.Item.GetRawValueString("QuantityPricesLayout", "list"); 92 93 string minQty = product.PurchaseMinimumQuantity != 1 ? "min=\"" + product.PurchaseMinimumQuantity.ToString() + "\"" : "min=\"1\""; 94 string stepQty = product.PurchaseQuantityStep > 1 ? product.PurchaseQuantityStep.ToString() : "1"; 95 string valueQty = product.PurchaseMinimumQuantity > product.PurchaseQuantityStep ? product.PurchaseMinimumQuantity.ToString() : stepQty; 96 string qtyValidCheck = stepQty != "1" ? "onkeyup=\"swift.Cart.QuantityValidate(event)\"" : ""; 97 98 string showPricesWithVat = Pageview.Area.EcomPricesWithVat.ToLower(); 99 bool neverShowVat = string.IsNullOrEmpty(showPricesWithVat); 100 101 string priceMin = ""; 102 string priceMax = ""; 103 104 var favoriteParameters = new Dictionary<string, object>(); 105 if (!anonymousUser && !hideFavoritesSelector) 106 { 107 IEnumerable<FavoriteList> favoreiteLists = Pageview.User.GetFavoriteLists(); 108 int defaultFavoriteListId = 0; 109 110 if (favoreiteLists.Count() == 1) { 111 foreach (FavoriteList list in favoreiteLists) { 112 defaultFavoriteListId = list.ListId; 113 } 114 } 115 116 favoriteParameters.Add("ListId", defaultFavoriteListId); 117 } 118 119 var priceParms = new Dictionary<string, object>(); 120 priceParms.Add("theme", ""); 121 122 var badgeParms = new Dictionary<string, object>(); 123 badgeParms.Add("size", "h7"); 124 badgeParms.Add("saleBadgeType", Model.Item.GetRawValue("SaleBadgeType")); 125 badgeParms.Add("saleBadgeCssClassName", Model.Item.GetRawValue("SaleBadgeDesign")); 126 badgeParms.Add("newBadgeCssClassName", Model.Item.GetRawValue("NewBadgeDesign")); 127 badgeParms.Add("newPublicationDays", Model.Item.GetInt32("NewPublicationDays")); 128 badgeParms.Add("campaignBadgesValues", Model.Item.GetRawValueString("CampaignBadges")); 129 130 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("SaleBadgeDesign")) && Model.Item.GetRawValueString("SaleBadgeDesign") != "none" ? true : false; 131 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("NewBadgeDesign")) && Model.Item.GetRawValueString("NewBadgeDesign") != "none" ? true : false; 132 DateTime createdDate = product.Created.Value; 133 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false; 134 showBadges = (newBadgeEnabled && Model.Item.GetInt32("NewPublicationDays") == 0) || (newBadgeEnabled && (createdDate.AddDays(Model.Item.GetInt32("NewPublicationDays")) > DateTime.Now)) ? true : showBadges; 135 showBadges = !string.IsNullOrEmpty(Model.Item.GetRawValueString("CampaignBadges")) ? true : showBadges; 136 137 string liveInfoClass = ""; 138 string productInfoFeed = ""; 139 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Ecommerce.DynamicwebLiveIntegration.TemplatesHelper.IsLazyLoadingForProductInfoEnabled; 140 if (isLazyLoadingForProductInfoEnabled) 141 { 142 if (Dynamicweb.Context.Current.Items.Contains("ProductInfoFeed")) 143 { 144 productInfoFeed = Dynamicweb.Context.Current.Items["ProductInfoFeed"]?.ToString(); 145 if (!string.IsNullOrEmpty(productInfoFeed)) 146 { 147 productInfoFeed = $"data-product-info-feed=\"{productInfoFeed}\""; 148 } 149 } 150 liveInfoClass = "js-live-info"; 151 } 152 @* Product stock state *@ 153 double? currentStockLevel = product.StockLevel; 154 155 156 string stockStateLabel = !string.IsNullOrEmpty(product.StockStatus) ? product.StockStatus : ""; 157 158 string stockStateCss = currentStockLevel > 0 ? "text-success" : "text-danger"; 159 string stockStateIconCss = currentStockLevel > 0 ? "bg-success" : "bg-danger"; 160 } 161 162 @if (!string.IsNullOrWhiteSpace(googleAnalyticsMeasurementID) && allowTracking) 163 { 164 <script> 165 gtag("event", "view_item", { 166 currency: "@product.Price.CurrencyCode", 167 value: @product.Price.Price, 168 items: [ 169 { 170 item_id: "@product.Number", 171 item_name: "@product.Name", 172 currency: "@product.Price.CurrencyCode", 173 price: @product.Price.Price 174 } 175 ] 176 }); 177 </script> 178 } 179 180 <div class="h-100 @(contentPadding) @(theme) [email protected]()" @productInfoFeed> 181 <div class="d-flex flex-column gap-4 js-product" data-product-id="@product.Id"> 182 @if (showBadges) { 183 <div class="swift_badge-collection"> 184 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms) 185 </div> 186 } 187 188 <div class="d-flex flex-column gap-2"> 189 <h1 class="@titleFontSize m-0" itemprop="name">@product.Name</h1> 190 @if (!Model.Item.GetBoolean("HideProductNumber")) 191 { 192 @RenderPartial("Paragraph/Swift_ProductNumber.cshtml", Model) 193 } 194 </div> 195 196 @if (!hidePrice && !isDiscontinued) 197 { 198 @RenderPartial("Paragraph/Swift_ProductPrice.cshtml", Model, priceParms) 199 200 if (isLazyLoadingForProductInfoEnabled) 201 { 202 <div class="product-prices-container @liveInfoClass d-none" data-show-if="LiveProductInfo.product.Prices.length > 0"> 203 @if (quantityPricesLayout == "list") 204 { 205 <div class="mt-3 product-prices"> 206 <small class="d-block opacity-75 product-prices-template"><span><span class="js-text-price-quantity"></span> @Translate("PCS")</span> - <span class="fw-bold"><span class="js-text-price-price"></span> <span class="d-none" data-show-if="LiveProductInfo.productPrice.Quantity > 1">@Translate("pr. PCS")</span></span></small> 207 </div> 208 } 209 else if (quantityPricesLayout == "table") 210 { 211 <div class="grid"> 212 <table class="table table-sm mt-3 g-col-12 g-col-lg-6"> 213 <thead> 214 <tr> 215 <td>@Translate("QTY")</td> 216 <td>@Translate("pr. PCS")</td> 217 </tr> 218 </thead> 219 <tbody class="product-prices"> 220 <tr class="product-prices-template"> 221 <td class="js-text-price-quantity"></td> 222 <td class="js-text-price-price"></td> 223 </tr> 224 </tbody> 225 </table> 226 </div> 227 } 228 </div> 229 } 230 else 231 { 232 if (product.Prices.Count > 0) 233 { 234 <div> 235 @if (quantityPricesLayout == "list") 236 { 237 <div class="mt-3"> 238 @foreach (PriceListViewModel quantityPrice in product.Prices) 239 { 240 string quantityLabel = Translate("PCS"); 241 string quantityPriceSuffix = quantityPrice.Quantity > 1 ? Translate("pr. PCS") : ""; 242 243 <small class="d-block opacity-75"><span>@quantityPrice.Quantity @quantityLabel</span> - <span class="fw-bold">@quantityPrice.Price.PriceFormatted @quantityPriceSuffix</span></small> 244 } 245 </div> 246 } 247 else if (quantityPricesLayout == "table") 248 { 249 <div class="grid"> 250 <table class="table table-sm mt-3 g-col-12 g-col-lg-6"> 251 <thead> 252 <tr> 253 <td>@Translate("QTY")</td> 254 <td>@Translate("pr. PCS")</td> 255 </tr> 256 </thead> 257 <tbody> 258 @foreach (PriceListViewModel quantityPrice in product.Prices) 259 { 260 <tr> 261 <td>@quantityPrice.Quantity</td> 262 <td>@quantityPrice.Price.PriceFormatted</td> 263 </tr> 264 } 265 </tbody> 266 </table> 267 </div> 268 } 269 </div> 270 } 271 } 272 } 273 274 @RenderPartial("Paragraph/Swift_ProductShortDescription.cshtml", Model) 275 276 @if (mainFeatures.Count > 0) 277 { 278 foreach (CategoryFieldViewModel mainFeatureGroup in mainFeatures) 279 { 280 <dl class="grid gap-0"> 281 @foreach (var field in mainFeatureGroup.Fields) 282 { 283 @RenderField(field.Value) 284 } 285 </dl> 286 } 287 } 288 289 @if (product.VariantInfo.VariantInfo != null && !Model.Item.GetBoolean("HideVariantSelector")) 290 { 291 int groupNumber = 1; 292 293 string baseUrl = $"Default.aspx?ID={GetPageIdByNavigationTag("Shop")}&GroupID={product.PrimaryOrDefaultGroup.Id}&ProductID={product.Id}"; 294 string variantUrl = ""; 295 if (!string.IsNullOrEmpty(product.VariantId)) 296 { 297 variantUrl = Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl($"Default.aspx?ID={GetPageIdByNavigationTag("Shop")}&GroupID={product.PrimaryOrDefaultGroup.Id}&ProductID={product.Id}&VariantID={product.VariantId}"); 298 } 299 300 <form class="js-variant-selector" data-combinations="@string.Join(",", product.VariantCombinations())" data-base-url="@baseUrl" data-friendly-url="@variantUrl"> 301 <input type="hidden" name="variantid" /> 302 303 @foreach (var variantGroup in product.VariantGroups()) 304 { 305 VariantGroupViewModel group = variantGroup; 306 307 <h3 class="h6">@group.Name</h3> 308 <div class="d-flex gap-2 flex-wrap js-variant-group" data-group-id="@groupNumber"> 309 @foreach (var option in group.Options) 310 { 311 string active = variantId.Contains(option.Id) ? "active" : ""; 312 313 if (!string.IsNullOrEmpty(option.Color)) 314 { 315 string contrastColor = GetContrastColor(option.Color); 316 <button type="button" class="btn colorbox rounded-circle d-inline-block variant-option border js-variant-option @active" style="background-color: @option.Color; --variantoption-check-color: @contrastColor" onclick="swift.VariantSelector.OptionClick(event)" data-variant-id="@option.Id" id="@(product.Id)_@(option.Id)[email protected]"></button> 317 } 318 else if (!string.IsNullOrEmpty(option.Color) && !string.IsNullOrEmpty(option.Image.Value)) 319 { 320 <button type="button" class="btn p-0 d-inline-block variant-option border js-variant-option @active" onclick="swift.VariantSelector.OptionClick(event)" data-variant-id="@option.Id"> 321 <img src="/Admin/Public/GetImage.ashx?image=@(option.Image.Value)&width=42&Format=WebP&Quality=70" /> 322 </button> 323 } 324 else 325 { 326 <button type="button" class="btn btn-secondary d-inline-block variant-option js-variant-option @active" onclick="swift.VariantSelector.OptionClick(event)" data-variant-id="@option.Id"> 327 @option.Name 328 </button> 329 } 330 } 331 </div> 332 333 groupNumber++; 334 } 335 </form> 336 } 337 338 <div class="d-flex flex-row flex-nowrap gap-2"> 339 @if (!hideAddToCart) 340 { 341 <form method="post" action="@url" class="flex-fill"> 342 <input type="hidden" name="redirect" value="false" /> 343 <input type="hidden" name="ProductId" value="@product.Id" /> 344 <input type="hidden" name="ProductName" value="@product.Name" /> 345 <input type="hidden" name="ProductPrice" value="@product.Price.Price" /> 346 <input type="hidden" name="ProductCurrency" value="@product.Price.CurrencyCode" /> 347 <input type="hidden" name="ProductReferer" value="product_details_info"> 348 <input type="hidden" name="cartcmd" value="add" /> 349 350 @if (!string.IsNullOrEmpty(product.VariantId)) 351 { 352 <input type="hidden" name="VariantId" value="@product.VariantId" /> 353 } 354 @if (!Model.Item.GetBoolean("QuantitySelector")) 355 { 356 <input id="[email protected]" name="Quantity" value="@valueQty" type="hidden"> 357 <button type="button" onclick="swift.Cart.Update(event)" class="btn btn-primary w-100 js-add-to-cart-button @disableAddToCart" @disableAddToCart title="@Translate("Add to cart")" id="AddToCartButton@(product.Id)[email protected]">@Translate("Add to cart")</button> 358 } else { 359 <div class="input-group input-primary-button-group js-input-group d-flex flex-row flex-nowrap"> 360 <label for="Quantity_@(product.Id)" class="visually-hidden">@Translate("Quantity")</label> 361 <input id="[email protected]" name="Quantity" value="@valueQty" step="@stepQty" @minQty class="form-control" style="max-width: 96px; min-width:64px;" type="number" onkeydown="swift.Cart.UpdateOnEnterKey(event)"> 362 <button type="button" onclick="swift.Cart.Update(event)" class="btn btn-primary flex-fill js-add-to-cart-button @disableAddToCart" @disableAddToCart title="@Translate("Add to cart")" id="AddToCartButton@(product.Id)[email protected]">@Translate("Add to cart")</button> 363 </div> 364 365 if (stepQty != "1") 366 { 367 <div class="invalid-feedback d-none"> 368 @Translate("Please select a quantity that is dividable by") @stepQty 369 </div> 370 } 371 } 372 </form> 373 if (!anonymousUser && !hideFavoritesSelector) 374 { 375 @RenderPartial("Components/ToggleFavorite.cshtml", product, favoriteParameters) 376 } 377 } 378 else if (!anonymousUser && !hideFavoritesSelector && !isDiscontinued) 379 { 380 <div class="flex-fill" id="[email protected]"> 381 @Translate("Add to favorites") @RenderPartial("Components/ToggleFavorite.cshtml", product, favoriteParameters) 382 </div> 383 } 384 385 @if (isDiscontinued && product.ReplacementProduct != null) { 386 List<ProductInfoViewModel> replacementProductList = new List<ProductInfoViewModel>(); 387 replacementProductList.Add(product.ReplacementProduct); 388 var replacementProduct = replacementProductList.GetProducts().FirstOrDefault(); 389 390 if ((product.DiscontinuedAction == 0 || product.DiscontinuedAction == 1) && product?.ReplacementProduct.ProductId != null) { 391 var parms = new Dictionary<string, object>(); 392 parms.Add("cssClass", "d-block mw-100 mh-100 m-auto"); 393 parms.Add("fullwidth", true); 394 parms.Add("columns", Model.GridRowColumnCount); 395 396 string imagePath = replacementProduct?.DefaultImage?.Value != null ? replacementProduct.DefaultImage.Value : ""; 397 398 string link = "Default.aspx?ID=" + GetPageIdByNavigationTag("Shop"); 399 link += $"&GroupID={replacementProduct.PrimaryOrDefaultGroup.Id}"; 400 link += $"&ProductID={replacementProduct.Id}"; 401 link += !string.IsNullOrEmpty(replacementProduct.VariantId) ? $"&VariantID={replacementProduct.VariantId}" : ""; 402 link = Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl(link); 403 404 <div class="w-100"> 405 <div class="fw-bold w-100">@Translate("Sorry, this product is no longer available").</div> 406 <div>@Translate("We recommend this replacement product instead"):</div> 407 408 <a href="@link"> 409 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 410 </a> 411 412 <div>@replacementProduct.Name</div> 413 414 @if (!hidePrice) 415 { 416 <div class="mb-3"> 417 <div class="h4" itemprop="offers" itemscope itemtype="https://schema.org/Offer"> 418 @if (showPricesWithVat == "false" && !neverShowVat) 419 { 420 if (isLazyLoadingForProductInfoEnabled) 421 { 422 <span itemprop="price" content="" class="d-none"></span> 423 <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> 424 } 425 else 426 { 427 string beforePrice = product.PriceBeforeDiscount.PriceWithoutVatFormatted; 428 429 <span itemprop="price" content="@product.Price.PriceWithoutVat" class="d-none"></span> 430 if (product.Price.Price != product.PriceBeforeDiscount.Price) 431 { 432 <span class="text-decoration-line-through opacity-75 me-3">@beforePrice</span> 433 } 434 } 435 } 436 else 437 { 438 if (isLazyLoadingForProductInfoEnabled) 439 { 440 <span itemprop="price" content="" class="d-none"></span> 441 <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> 442 } 443 else 444 { 445 string beforePrice = product.PriceBeforeDiscount.PriceFormatted; 446 447 <span itemprop="price" content="@product.Price.Price" class="d-none"></span> 448 if (product.Price.Price != product.PriceBeforeDiscount.Price) 449 { 450 <span class="text-decoration-line-through opacity-75 me-3">@beforePrice</span> 451 } 452 } 453 } 454 455 @if (showPricesWithVat == "false" && !neverShowVat) 456 { 457 if (isLazyLoadingForProductInfoEnabled) 458 { 459 <span class="text-price js-text-price"><div class="spinner-border" role="status"></div></span> 460 } 461 else 462 { 463 string price = product.Price.PriceWithoutVatFormatted; 464 if (product?.VariantInfo?.VariantInfo != null) 465 { 466 priceMin = product?.VariantInfo?.PriceMin?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithoutVatFormatted : ""; 467 priceMax = product?.VariantInfo?.PriceMax?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithoutVatFormatted : ""; 468 } 469 if (priceMin != priceMax) 470 { 471 price = priceMin + " - " + priceMax; 472 } 473 <span class="text-price">@price</span> 474 } 475 } 476 else 477 { 478 if (isLazyLoadingForProductInfoEnabled) 479 { 480 <span class="text-price js-text-price"><div class="spinner-border" role="status"></div></span> 481 } 482 else 483 { 484 string price = product.Price.PriceFormatted; 485 if (product?.VariantInfo?.VariantInfo != null) 486 { 487 priceMin = product?.VariantInfo?.PriceMin?.PriceFormatted != null ? product.VariantInfo.PriceMin.PriceFormatted : ""; 488 priceMax = product?.VariantInfo?.PriceMax?.PriceFormatted != null ? product.VariantInfo.PriceMax.PriceFormatted : ""; 489 } 490 if (priceMin != priceMax) 491 { 492 price = priceMin + " - " + priceMax; 493 } 494 <span class="text-price">@price</span> 495 } 496 } 497 </div> 498 499 @if (showPricesWithVat == "false" && !neverShowVat) 500 { 501 if (isLazyLoadingForProductInfoEnabled) 502 { 503 <small class="opacity-85 fst-normal js-text-price-with-vat d-none" data-suffix="@Translate("Incl. VAT")"></small> 504 } 505 else 506 { 507 string price = product.Price.PriceWithVatFormatted; 508 if (product?.VariantInfo?.VariantInfo != null) 509 { 510 priceMin = product?.VariantInfo?.PriceMin?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithVatFormatted : ""; 511 priceMax = product?.VariantInfo?.PriceMax?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithVatFormatted : ""; 512 } 513 if (priceMin != priceMax) 514 { 515 price = priceMin + " - " + priceMax; 516 } 517 <small class="opacity-85 fst-normal">@price @Translate("Incl. VAT")</small> 518 } 519 } 520 </div> 521 } 522 523 <a href="@link" class="btn btn-primary w-100">@Translate("Go to the replacement")</a> 524 </div> 525 } 526 } 527 </div> 528 </div> 529 @if (!hideStock) 530 { 531 <div class="js-stock-state [email protected]()"> 532 <div class="small"> 533 <span class="@stockStateCss">@stockStateLabel </span> 534 </div> 535 </div> 536 } 537 </div> 538 539 @helper RenderField(FieldValueViewModel field) 540 { 541 string fieldValue = field?.Value != null ? field.Value.ToString() : ""; 542 bool noValues = false; 543 544 if (!string.IsNullOrEmpty(fieldValue)) 545 { 546 if (field.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 547 { 548 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 549 noValues = values.Count > 0 ? false : true; 550 } 551 } 552 553 if (!string.IsNullOrEmpty(fieldValue) && noValues == false) 554 { 555 <dt class="g-col-12 g-col-sm-4 g-col-lg-12 fw-bold m-0">@field.Name</dt> 556 <dd class="g-col-12 g-col-sm-8 g-col-lg-12 mb-3"> 557 @RenderFieldValue(field) 558 </dd> 559 } 560 } 561 562 @helper RenderFieldValue(FieldValueViewModel field) 563 { 564 string fieldValue = field?.Value != null ? field.Value.ToString() : ""; 565 566 fieldValue = fieldValue == "False" ? Translate("No") : fieldValue; 567 fieldValue = fieldValue == "True" ? Translate("Yes") : fieldValue; 568 569 bool isColor = false; 570 571 if (field.Value.GetType() == typeof(System.Collections.Generic.List<Dynamicweb.Ecommerce.ProductCatalog.FieldOptionValueViewModel>)) 572 { 573 int valueCount = 0; 574 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 575 int totalValues = values.Count; 576 577 foreach (FieldOptionValueViewModel option in values) 578 { 579 if (option.Value.Substring(0, 1) == "#") 580 { 581 isColor = true; 582 } 583 584 if (!isColor) 585 { 586 @option.Name 587 } 588 else 589 { 590 <span class="colorbox-sm" style="background-color: @option.Value" title="@option.Value"></span> 591 } 592 593 if (valueCount != totalValues && valueCount < (totalValues - 1)) 594 { 595 if (isColor) 596 { 597 <text> </text> 598 } 599 else 600 { 601 <text>, </text> 602 } 603 } 604 valueCount++; 605 } 606 } 607 else 608 { 609 if (fieldValue.Substring(0, 1) == "#") 610 { 611 isColor = true; 612 } 613 614 if (!isColor) 615 { 616 @fieldValue 617 } 618 else 619 { 620 <span class="colorbox-sm" style="background-color: @fieldValue" title="@fieldValue"></span> 621 } 622 } 623 } 624
Error executing template "Designs/Swift/Paragraph/Swift_ProductSpecification.cshtml" System.NullReferenceException: Object reference not set to an instance of an object. at CompiledRazorTemplates.Dynamic.RazorEngine_dd2f69b6f32447b494c14b30bbd147e4.Execute() in D:\dynamicweb.net\Solutions\Dynamicweb\bomedys.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_ProductSpecification.cshtml:line 28 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.Item["DummyProduct"] != null) 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 @if (product is object) { 23 IEnumerable<string> selectedDisplayGroupIds = Model.Item.GetRawValueString("DisplayGroups").Split(',').ToList(); 24 List<CategoryFieldViewModel> displayGroups = new List<CategoryFieldViewModel>(); 25 26 foreach (var selection in selectedDisplayGroupIds) 27 { 28 foreach (CategoryFieldViewModel group in product.FieldDisplayGroups.Values) 29 { 30 if (selection == group.Id) 31 { 32 int fieldsWithNoValueOrZero = 0; 33 34 foreach (var field in group.Fields) 35 { 36 if (string.IsNullOrEmpty(field.Value.Value.ToString())) 37 { 38 fieldsWithNoValueOrZero++; 39 } 40 } 41 42 if (fieldsWithNoValueOrZero != group.Fields.Count) 43 { 44 displayGroups.Add(group); 45 } 46 } 47 } 48 } 49 50 bool showProductFields = Model.Item.GetBoolean("ProductFields"); 51 52 bool hideTitle = Model.Item.GetBoolean("HideTitle"); 53 54 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 55 56 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "display-4"); 57 58 string contentPadding = Model.Item.GetRawValueString("ContentPadding", ""); 59 contentPadding = contentPadding == "none" ? string.Empty : contentPadding; 60 contentPadding = contentPadding == "small" ? " p-2 p-md-3" : contentPadding; 61 contentPadding = contentPadding == "large" ? " p-4 p-md-5" : contentPadding; 62 63 string layout = Model.Item.GetRawValueString("Layout", "list"); 64 string size = Model.Item.GetRawValueString("Size", "full"); 65 string gaps = size == "full" ? " gap-4" : " gap-2"; 66 67 68 if (Pageview.IsVisualEditorMode && displayGroups.Count() == 0) 69 { 70 product.ProductFields.Clear(); 71 product.ProductFields.Add(Translate("Width"), new FieldValueViewModel { Name = Translate("Width"), Value = "99cm" }); 72 product.ProductFields.Add(Translate("Height"), new FieldValueViewModel { Name = Translate("Height"), Value = "195cm" }); 73 showProductFields = true; 74 } 75 76 if (layout == "commas") 77 { 78 gaps = size == "full" ? " gap-4" : " gap-2"; 79 80 } 81 82 <div class="h-100@(gaps)@(theme)@(contentPadding) [email protected]()"> 83 <div class="grid"> 84 @if ((product.ProductFields != null && Model.Item.GetBoolean("ProductFields")) || (product.ProductCategories != null && Model.Item.GetBoolean("CategoryFields")) || (displayGroups.Count != 0)) { 85 if (!hideTitle) 86 { 87 <h2 class="g-col-12 @titleFontSize">@Model.Item.GetString("Title")</h2> 88 } 89 } 90 91 @if (displayGroups.Count != 0) 92 { 93 if (layout != "accordion") 94 { 95 foreach (var group in displayGroups) 96 { 97 bool hideHeader = Model.Item.GetBoolean("HideGroupHeaders"); 98 99 if (!hideHeader) { 100 <h4 class="g-col-12 h4 mb-0">@group.Name</h4> 101 } 102 103 { @RenderFieldsFromList(group.Fields, layout) } 104 105 } 106 } 107 else 108 { 109 <div class="g-col-12"> 110 <div class="accordion accordion-flush w-100" id="[email protected]"> 111 @foreach (var group in displayGroups) 112 { 113 <div class="accordion-item"> 114 <h2 class="accordion-header" id="[email protected]"> 115 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#[email protected]" aria-expanded="false" aria-controls="[email protected]"> 116 @group.Name 117 </button> 118 </h2> 119 <div id="[email protected]" class="accordion-collapse collapse" aria-labelledby="[email protected]" data-bs-parent="#[email protected]"> 120 <div class="accordion-body"> 121 @{ @RenderFieldsFromList(group.Fields, "list") } 122 </div> 123 </div> 124 </div> 125 } 126 </div> 127 </div> 128 } 129 } 130 131 @if (product.ProductFields != null && showProductFields) 132 { 133 if (product.ProductFields.Count > 0) 134 { 135 if (layout != "accordion") 136 { 137 {@RenderFieldsFromList(product.ProductFields, layout) } 138 } 139 else 140 { 141 <div class="g-col-12"> 142 <div class="accordion accordion-flush w-100" id="[email protected]"> 143 <div class="accordion-item"> 144 <h2 class="accordion-header" id="[email protected]"> 145 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#[email protected]" aria-expanded="false" aria-controls="[email protected]"> 146 @Translate("Specifications") 147 </button> 148 </h2> 149 <div id="[email protected]" class="accordion-collapse" aria-labelledby="[email protected]" data-bs-parent="#[email protected]"> 150 <div class="accordion-body"> 151 @{ @RenderFieldsFromList(product.ProductFields, "List") } 152 </div> 153 </div> 154 </div> 155 </div> 156 </div> 157 } 158 } 159 } 160 161 @if (product.ProductCategories != null && Model.Item.GetBoolean("CategoryFields")) 162 { 163 if (product.ProductCategories.Count > 0) 164 { 165 if (layout != "accordion") 166 { 167 foreach (var group in product.ProductCategories) 168 { 169 CategoryFieldViewModel category = group.Value; 170 bool hideHeader = Model.Item.GetBoolean("HideGroupHeaders"); 171 172 if (!hideHeader) { 173 <h4 class="g-col-12 h4 mb-0">@group.Value.Name</h4> 174 } 175 176 { @RenderFieldsFromList(category.Fields, layout) } 177 } 178 } 179 else 180 { 181 <div class="g-col-12"> 182 <div class="accordion accordion-flush w-100" id="[email protected]"> 183 @foreach (var group in product.ProductCategories) 184 { 185 CategoryFieldViewModel category = group.Value; 186 187 <div class="accordion-item"> 188 <h2 class="accordion-header" id="[email protected]"> 189 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#[email protected]" aria-expanded="false" aria-controls="[email protected]"> 190 @group.Value.Name 191 </button> 192 </h2> 193 <div id="[email protected]" class="accordion-collapse" aria-labelledby="[email protected]" data-bs-parent="#[email protected]"> 194 <div class="accordion-body"> 195 @{ @RenderFieldsFromList(category.Fields, "list") } 196 </div> 197 </div> 198 </div> 199 } 200 </div> 201 </div> 202 } 203 } 204 } 205 </div> 206 </div> 207 } 208 else if (Pageview.IsVisualEditorMode) 209 { 210 <div class="alert alert-warning m-0">@Translate("No products available")</div> 211 } 212 213 @helper RenderFieldsFromList(Dictionary<string, FieldValueViewModel> fields, string layout) 214 { 215 string size = Model.Item.GetRawValueString("Size", "full"); 216 string gaps = size != "full" ? " gap-1" : string.Empty; 217 bool hideFieldLabels = Model.Item.GetBoolean("HideFieldLabels"); 218 bool hideFieldsWithZeroValue = Model.Item.GetBoolean("HideFieldsWithZeroValue"); 219 220 if (layout == "columns") { 221 <div class="g-col-12"> 222 <div class="grid@(gaps)"> 223 @foreach (var field in fields) 224 { 225 {@RenderField(field.Value, layout)} 226 } 227 </div> 228 </div> 229 } 230 if (layout == "list") { 231 <div class="g-col-12"> 232 <dl class="grid@(gaps)"> 233 @foreach (var field in fields) 234 { 235 {@RenderField(field.Value, layout)} 236 } 237 </dl> 238 </div> 239 } 240 if (layout == "table") 241 { 242 string tableSize = size == "full" ? "" : " table-sm"; 243 <div class="g-col-12"> 244 <table class="table table-striped@(tableSize)"> 245 @foreach (var field in fields) 246 { 247 {@RenderField(field.Value, layout)} 248 } 249 </table> 250 </div> 251 } 252 if (layout == "bullets") 253 { 254 string listSize = size == "full" ? "" : "m-0 p-0 lh-1 fs-7 opacity-75"; 255 string listStyle = size == "full" ? "" : "style=\"list-style-position: inside\""; 256 <div class="g-col-12"> 257 <ul class="@listSize" @listStyle> 258 @foreach (var field in fields) 259 { 260 {@RenderField(field.Value, layout)} 261 } 262 </ul> 263 </div> 264 } 265 if (layout == "commas") 266 { 267 List<string> featuresList = new List<string>(); 268 269 foreach (var field in fields) 270 { 271 string firstListItemValue = string.Empty; //Hack to support field type providers with a single value 272 273 if (field.Value?.Value != null) 274 { 275 if (field.Value.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 276 { 277 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 278 279 //Hack to support field type providers with a single value 280 if (values.FirstOrDefault() != null) 281 { 282 firstListItemValue = values.FirstOrDefault().Value; 283 } 284 } 285 } 286 287 if (!hideFieldsWithZeroValue || (firstListItemValue != "0" && firstListItemValue != "0.0" && field.Value.Value.ToString() != "0" && field.Value.Value.ToString() != "0.0")) 288 { 289 if (field.Value.Value is object && !string.IsNullOrEmpty(field.Value.Value.ToString())) 290 { 291 if (field.Value.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 292 { 293 List<string> options = new List<string>(); 294 foreach (FieldOptionValueViewModel option in field.Value.Value as System.Collections.Generic.List<FieldOptionValueViewModel>) 295 { 296 if (!string.IsNullOrWhiteSpace(option.Value)) 297 { 298 if (option.Value.ToString().Contains("#") && (Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour"))) 299 { 300 string colorSpan = "<span class=\"colorbox-sm\" style=\"background-color: " + option.Value + "\"></span>"; 301 options.Add(colorSpan); 302 } 303 else if (!string.IsNullOrEmpty(option.Value)) 304 { 305 options.Add(option.Name); 306 } 307 } 308 } 309 string optionsString = (string.Join(", ", options.Select(x => x.ToString()).ToArray())); 310 if ((Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour"))) 311 { 312 optionsString = (string.Join(" ", options.Select(x => x.ToString()).ToArray())); 313 } 314 315 if (!string.IsNullOrEmpty(optionsString)) 316 { 317 if (!hideFieldLabels) 318 { 319 featuresList.Add(field.Value.Name + ": " + optionsString); 320 } 321 else 322 { 323 featuresList.Add(optionsString); 324 } 325 } 326 } 327 else 328 { 329 if (!string.IsNullOrWhiteSpace(field.Value.Value.ToString())) 330 { 331 if (field.Value.Value.ToString().Contains("#") && (Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour"))) 332 { 333 string colorSpan = "<span class=\"colorbox-sm\" style=\"background-color: " + field.Value.Value + "\"></span>"; 334 335 if (!hideFieldLabels) 336 { 337 featuresList.Add(field.Value.Name + ": " + colorSpan); 338 } 339 else 340 { 341 featuresList.Add(colorSpan); 342 } 343 } 344 else 345 { 346 if (!hideFieldLabels) 347 { 348 featuresList.Add(field.Value.Name + ": " + field.Value.Value.ToString()); 349 } 350 else 351 { 352 featuresList.Add(field.Value.Value.ToString()); 353 } 354 } 355 } 356 } 357 } 358 } 359 } 360 361 string featuresString = (string.Join(", ", featuresList.Select(x => x.ToString()).ToArray())); 362 363 <div class="g-col-12 opacity-75 fs-7">@featuresString</div> 364 } 365 } 366 367 @helper RenderField(FieldValueViewModel field, string layout) 368 { 369 string size = Model.Item.GetRawValueString("Size", "full"); 370 string fieldValue = field?.Value != null ? field.Value.ToString() : ""; 371 bool hideFieldLabels = Model.Item.GetBoolean("HideFieldLabels"); 372 bool noValues = false; 373 string firstListItemValue = string.Empty; //Hack to support field type providers with a single value 374 bool hideFieldsWithZeroValue = Model.Item.GetBoolean("HideFieldsWithZeroValue"); 375 376 if (!string.IsNullOrEmpty(fieldValue)) 377 { 378 if (field.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 379 { 380 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 381 noValues = values.Count > 0 ? false : true; 382 383 //Hack to support field type providers with a single value 384 if (values.FirstOrDefault() != null) 385 { 386 firstListItemValue = values.FirstOrDefault().Value; 387 } 388 } 389 } 390 391 if (!string.IsNullOrEmpty(fieldValue) && noValues == false) 392 { 393 if (!hideFieldsWithZeroValue || (firstListItemValue != "0" && firstListItemValue != "0.0" && field.Value.ToString() != "0" && field.Value.ToString() != "0.0")) 394 { 395 if (layout == "columns") 396 { 397 398 <div class="grid g-col-6 g-col-lg-4 gap-1"> 399 @if (!hideFieldLabels) 400 { 401 <dt class="g-col-12 g-col-lg-4">@field.Name</dt> 402 } 403 <dd class="g-col-12 g-col-lg-8 mb-0 text-break"> 404 @{ @RenderFieldValue(field) } 405 </dd> 406 </div> 407 } 408 if (layout == "list") 409 { 410 if (!hideFieldLabels) 411 { 412 <dt class="g-col-4">@field.Name</dt> 413 } 414 <dd class="g-col-8 mb-0 text-break"> 415 @{ @RenderFieldValue(field) } 416 </dd> 417 } 418 if (layout == "table") 419 { 420 <tr> 421 @if (!hideFieldLabels) 422 { 423 <th class="w-25 w-lg-50" scope="row">@field.Name</th> 424 } 425 <td class="text-break"> 426 @{ @RenderFieldValue(field) } 427 </td> 428 </tr> 429 } 430 if (layout == "bullets") 431 { 432 <li> 433 @if (!hideFieldLabels) 434 { 435 <strong>@field.Name</strong> 436 } 437 <span> 438 @{ @RenderFieldValue(field) } 439 </span> 440 </li> 441 } 442 } 443 } 444 } 445 446 @helper RenderFieldValue(FieldValueViewModel field) 447 { 448 string fieldValue = field?.Value != null ? field.Value.ToString() : ""; 449 450 bool isLink = field?.Type == "Link"; 451 bool isColor = false; 452 bool isBrandName = field?.SystemName == "Brand_name"; 453 454 fieldValue = fieldValue == "False" ? Translate("No") : fieldValue; 455 fieldValue = fieldValue == "True" ? Translate("Yes") : fieldValue; 456 457 458 if (field.Value.GetType() == typeof(System.Collections.Generic.List<Dynamicweb.Ecommerce.ProductCatalog.FieldOptionValueViewModel>)) 459 { 460 int valueCount = 0; 461 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 462 int totalValues = values.Count; 463 464 foreach (FieldOptionValueViewModel option in values) 465 { 466 if (!string.IsNullOrEmpty(option.Value)) 467 { 468 if (option.Value.Substring(0, 1) == "#") 469 { 470 isColor = true; 471 } 472 } 473 474 if (!isColor) 475 { 476 @option.Name 477 } 478 else 479 { 480 <span class="colorbox-sm" style="background-color: @option.Value" title="@option.Name"></span> 481 } 482 483 if (valueCount != totalValues && valueCount < (totalValues - 1)) 484 { 485 if (isColor) 486 { 487 <text> </text> 488 } 489 else 490 { 491 <text>, </text> 492 } 493 } 494 valueCount++; 495 } 496 } 497 else 498 { 499 if (fieldValue.Substring(0, 1) == "#") 500 { 501 isColor = true; 502 } 503 504 if (!isColor) 505 { 506 if (isLink) 507 { 508 string linktTitle = !fieldValue.Contains("aspx") ? fieldValue : Translate("Go to link"); 509 string target = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") && fieldValue.Contains("http") ? "target=\"_blank\"" : string.Empty; 510 string rel = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") && fieldValue.Contains("http") ? "rel=\"noopener\"" : string.Empty; 511 512 <a href="@field.Value" title="@field.Name" @target @rel>@linktTitle</a> 513 } 514 else if (isBrandName) 515 { 516 <span itemprop="brand" itemtype="https://schema.org/Brand" itemscope> 517 <span itemprop="name">@fieldValue</span> 518 </span> 519 } 520 else 521 { 522 @fieldValue 523 } 524 525 } 526 else 527 { 528 <span class="colorbox-sm" style="background-color: @fieldValue" title="@fieldValue"></span> 529 } 530 } 531 } 532
Error executing template "Designs/Swift/Paragraph/Swift_ProductSpecification.cshtml" System.NullReferenceException: Object reference not set to an instance of an object. at CompiledRazorTemplates.Dynamic.RazorEngine_dd2f69b6f32447b494c14b30bbd147e4.Execute() in D:\dynamicweb.net\Solutions\Dynamicweb\bomedys.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_ProductSpecification.cshtml:line 28 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.Item["DummyProduct"] != null) 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 @if (product is object) { 23 IEnumerable<string> selectedDisplayGroupIds = Model.Item.GetRawValueString("DisplayGroups").Split(',').ToList(); 24 List<CategoryFieldViewModel> displayGroups = new List<CategoryFieldViewModel>(); 25 26 foreach (var selection in selectedDisplayGroupIds) 27 { 28 foreach (CategoryFieldViewModel group in product.FieldDisplayGroups.Values) 29 { 30 if (selection == group.Id) 31 { 32 int fieldsWithNoValueOrZero = 0; 33 34 foreach (var field in group.Fields) 35 { 36 if (string.IsNullOrEmpty(field.Value.Value.ToString())) 37 { 38 fieldsWithNoValueOrZero++; 39 } 40 } 41 42 if (fieldsWithNoValueOrZero != group.Fields.Count) 43 { 44 displayGroups.Add(group); 45 } 46 } 47 } 48 } 49 50 bool showProductFields = Model.Item.GetBoolean("ProductFields"); 51 52 bool hideTitle = Model.Item.GetBoolean("HideTitle"); 53 54 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 55 56 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "display-4"); 57 58 string contentPadding = Model.Item.GetRawValueString("ContentPadding", ""); 59 contentPadding = contentPadding == "none" ? string.Empty : contentPadding; 60 contentPadding = contentPadding == "small" ? " p-2 p-md-3" : contentPadding; 61 contentPadding = contentPadding == "large" ? " p-4 p-md-5" : contentPadding; 62 63 string layout = Model.Item.GetRawValueString("Layout", "list"); 64 string size = Model.Item.GetRawValueString("Size", "full"); 65 string gaps = size == "full" ? " gap-4" : " gap-2"; 66 67 68 if (Pageview.IsVisualEditorMode && displayGroups.Count() == 0) 69 { 70 product.ProductFields.Clear(); 71 product.ProductFields.Add(Translate("Width"), new FieldValueViewModel { Name = Translate("Width"), Value = "99cm" }); 72 product.ProductFields.Add(Translate("Height"), new FieldValueViewModel { Name = Translate("Height"), Value = "195cm" }); 73 showProductFields = true; 74 } 75 76 if (layout == "commas") 77 { 78 gaps = size == "full" ? " gap-4" : " gap-2"; 79 80 } 81 82 <div class="h-100@(gaps)@(theme)@(contentPadding) [email protected]()"> 83 <div class="grid"> 84 @if ((product.ProductFields != null && Model.Item.GetBoolean("ProductFields")) || (product.ProductCategories != null && Model.Item.GetBoolean("CategoryFields")) || (displayGroups.Count != 0)) { 85 if (!hideTitle) 86 { 87 <h2 class="g-col-12 @titleFontSize">@Model.Item.GetString("Title")</h2> 88 } 89 } 90 91 @if (displayGroups.Count != 0) 92 { 93 if (layout != "accordion") 94 { 95 foreach (var group in displayGroups) 96 { 97 bool hideHeader = Model.Item.GetBoolean("HideGroupHeaders"); 98 99 if (!hideHeader) { 100 <h4 class="g-col-12 h4 mb-0">@group.Name</h4> 101 } 102 103 { @RenderFieldsFromList(group.Fields, layout) } 104 105 } 106 } 107 else 108 { 109 <div class="g-col-12"> 110 <div class="accordion accordion-flush w-100" id="[email protected]"> 111 @foreach (var group in displayGroups) 112 { 113 <div class="accordion-item"> 114 <h2 class="accordion-header" id="[email protected]"> 115 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#[email protected]" aria-expanded="false" aria-controls="[email protected]"> 116 @group.Name 117 </button> 118 </h2> 119 <div id="[email protected]" class="accordion-collapse collapse" aria-labelledby="[email protected]" data-bs-parent="#[email protected]"> 120 <div class="accordion-body"> 121 @{ @RenderFieldsFromList(group.Fields, "list") } 122 </div> 123 </div> 124 </div> 125 } 126 </div> 127 </div> 128 } 129 } 130 131 @if (product.ProductFields != null && showProductFields) 132 { 133 if (product.ProductFields.Count > 0) 134 { 135 if (layout != "accordion") 136 { 137 {@RenderFieldsFromList(product.ProductFields, layout) } 138 } 139 else 140 { 141 <div class="g-col-12"> 142 <div class="accordion accordion-flush w-100" id="[email protected]"> 143 <div class="accordion-item"> 144 <h2 class="accordion-header" id="[email protected]"> 145 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#[email protected]" aria-expanded="false" aria-controls="[email protected]"> 146 @Translate("Specifications") 147 </button> 148 </h2> 149 <div id="[email protected]" class="accordion-collapse" aria-labelledby="[email protected]" data-bs-parent="#[email protected]"> 150 <div class="accordion-body"> 151 @{ @RenderFieldsFromList(product.ProductFields, "List") } 152 </div> 153 </div> 154 </div> 155 </div> 156 </div> 157 } 158 } 159 } 160 161 @if (product.ProductCategories != null && Model.Item.GetBoolean("CategoryFields")) 162 { 163 if (product.ProductCategories.Count > 0) 164 { 165 if (layout != "accordion") 166 { 167 foreach (var group in product.ProductCategories) 168 { 169 CategoryFieldViewModel category = group.Value; 170 bool hideHeader = Model.Item.GetBoolean("HideGroupHeaders"); 171 172 if (!hideHeader) { 173 <h4 class="g-col-12 h4 mb-0">@group.Value.Name</h4> 174 } 175 176 { @RenderFieldsFromList(category.Fields, layout) } 177 } 178 } 179 else 180 { 181 <div class="g-col-12"> 182 <div class="accordion accordion-flush w-100" id="[email protected]"> 183 @foreach (var group in product.ProductCategories) 184 { 185 CategoryFieldViewModel category = group.Value; 186 187 <div class="accordion-item"> 188 <h2 class="accordion-header" id="[email protected]"> 189 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#[email protected]" aria-expanded="false" aria-controls="[email protected]"> 190 @group.Value.Name 191 </button> 192 </h2> 193 <div id="[email protected]" class="accordion-collapse" aria-labelledby="[email protected]" data-bs-parent="#[email protected]"> 194 <div class="accordion-body"> 195 @{ @RenderFieldsFromList(category.Fields, "list") } 196 </div> 197 </div> 198 </div> 199 } 200 </div> 201 </div> 202 } 203 } 204 } 205 </div> 206 </div> 207 } 208 else if (Pageview.IsVisualEditorMode) 209 { 210 <div class="alert alert-warning m-0">@Translate("No products available")</div> 211 } 212 213 @helper RenderFieldsFromList(Dictionary<string, FieldValueViewModel> fields, string layout) 214 { 215 string size = Model.Item.GetRawValueString("Size", "full"); 216 string gaps = size != "full" ? " gap-1" : string.Empty; 217 bool hideFieldLabels = Model.Item.GetBoolean("HideFieldLabels"); 218 bool hideFieldsWithZeroValue = Model.Item.GetBoolean("HideFieldsWithZeroValue"); 219 220 if (layout == "columns") { 221 <div class="g-col-12"> 222 <div class="grid@(gaps)"> 223 @foreach (var field in fields) 224 { 225 {@RenderField(field.Value, layout)} 226 } 227 </div> 228 </div> 229 } 230 if (layout == "list") { 231 <div class="g-col-12"> 232 <dl class="grid@(gaps)"> 233 @foreach (var field in fields) 234 { 235 {@RenderField(field.Value, layout)} 236 } 237 </dl> 238 </div> 239 } 240 if (layout == "table") 241 { 242 string tableSize = size == "full" ? "" : " table-sm"; 243 <div class="g-col-12"> 244 <table class="table table-striped@(tableSize)"> 245 @foreach (var field in fields) 246 { 247 {@RenderField(field.Value, layout)} 248 } 249 </table> 250 </div> 251 } 252 if (layout == "bullets") 253 { 254 string listSize = size == "full" ? "" : "m-0 p-0 lh-1 fs-7 opacity-75"; 255 string listStyle = size == "full" ? "" : "style=\"list-style-position: inside\""; 256 <div class="g-col-12"> 257 <ul class="@listSize" @listStyle> 258 @foreach (var field in fields) 259 { 260 {@RenderField(field.Value, layout)} 261 } 262 </ul> 263 </div> 264 } 265 if (layout == "commas") 266 { 267 List<string> featuresList = new List<string>(); 268 269 foreach (var field in fields) 270 { 271 string firstListItemValue = string.Empty; //Hack to support field type providers with a single value 272 273 if (field.Value?.Value != null) 274 { 275 if (field.Value.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 276 { 277 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 278 279 //Hack to support field type providers with a single value 280 if (values.FirstOrDefault() != null) 281 { 282 firstListItemValue = values.FirstOrDefault().Value; 283 } 284 } 285 } 286 287 if (!hideFieldsWithZeroValue || (firstListItemValue != "0" && firstListItemValue != "0.0" && field.Value.Value.ToString() != "0" && field.Value.Value.ToString() != "0.0")) 288 { 289 if (field.Value.Value is object && !string.IsNullOrEmpty(field.Value.Value.ToString())) 290 { 291 if (field.Value.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 292 { 293 List<string> options = new List<string>(); 294 foreach (FieldOptionValueViewModel option in field.Value.Value as System.Collections.Generic.List<FieldOptionValueViewModel>) 295 { 296 if (!string.IsNullOrWhiteSpace(option.Value)) 297 { 298 if (option.Value.ToString().Contains("#") && (Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour"))) 299 { 300 string colorSpan = "<span class=\"colorbox-sm\" style=\"background-color: " + option.Value + "\"></span>"; 301 options.Add(colorSpan); 302 } 303 else if (!string.IsNullOrEmpty(option.Value)) 304 { 305 options.Add(option.Name); 306 } 307 } 308 } 309 string optionsString = (string.Join(", ", options.Select(x => x.ToString()).ToArray())); 310 if ((Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour"))) 311 { 312 optionsString = (string.Join(" ", options.Select(x => x.ToString()).ToArray())); 313 } 314 315 if (!string.IsNullOrEmpty(optionsString)) 316 { 317 if (!hideFieldLabels) 318 { 319 featuresList.Add(field.Value.Name + ": " + optionsString); 320 } 321 else 322 { 323 featuresList.Add(optionsString); 324 } 325 } 326 } 327 else 328 { 329 if (!string.IsNullOrWhiteSpace(field.Value.Value.ToString())) 330 { 331 if (field.Value.Value.ToString().Contains("#") && (Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour"))) 332 { 333 string colorSpan = "<span class=\"colorbox-sm\" style=\"background-color: " + field.Value.Value + "\"></span>"; 334 335 if (!hideFieldLabels) 336 { 337 featuresList.Add(field.Value.Name + ": " + colorSpan); 338 } 339 else 340 { 341 featuresList.Add(colorSpan); 342 } 343 } 344 else 345 { 346 if (!hideFieldLabels) 347 { 348 featuresList.Add(field.Value.Name + ": " + field.Value.Value.ToString()); 349 } 350 else 351 { 352 featuresList.Add(field.Value.Value.ToString()); 353 } 354 } 355 } 356 } 357 } 358 } 359 } 360 361 string featuresString = (string.Join(", ", featuresList.Select(x => x.ToString()).ToArray())); 362 363 <div class="g-col-12 opacity-75 fs-7">@featuresString</div> 364 } 365 } 366 367 @helper RenderField(FieldValueViewModel field, string layout) 368 { 369 string size = Model.Item.GetRawValueString("Size", "full"); 370 string fieldValue = field?.Value != null ? field.Value.ToString() : ""; 371 bool hideFieldLabels = Model.Item.GetBoolean("HideFieldLabels"); 372 bool noValues = false; 373 string firstListItemValue = string.Empty; //Hack to support field type providers with a single value 374 bool hideFieldsWithZeroValue = Model.Item.GetBoolean("HideFieldsWithZeroValue"); 375 376 if (!string.IsNullOrEmpty(fieldValue)) 377 { 378 if (field.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 379 { 380 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 381 noValues = values.Count > 0 ? false : true; 382 383 //Hack to support field type providers with a single value 384 if (values.FirstOrDefault() != null) 385 { 386 firstListItemValue = values.FirstOrDefault().Value; 387 } 388 } 389 } 390 391 if (!string.IsNullOrEmpty(fieldValue) && noValues == false) 392 { 393 if (!hideFieldsWithZeroValue || (firstListItemValue != "0" && firstListItemValue != "0.0" && field.Value.ToString() != "0" && field.Value.ToString() != "0.0")) 394 { 395 if (layout == "columns") 396 { 397 398 <div class="grid g-col-6 g-col-lg-4 gap-1"> 399 @if (!hideFieldLabels) 400 { 401 <dt class="g-col-12 g-col-lg-4">@field.Name</dt> 402 } 403 <dd class="g-col-12 g-col-lg-8 mb-0 text-break"> 404 @{ @RenderFieldValue(field) } 405 </dd> 406 </div> 407 } 408 if (layout == "list") 409 { 410 if (!hideFieldLabels) 411 { 412 <dt class="g-col-4">@field.Name</dt> 413 } 414 <dd class="g-col-8 mb-0 text-break"> 415 @{ @RenderFieldValue(field) } 416 </dd> 417 } 418 if (layout == "table") 419 { 420 <tr> 421 @if (!hideFieldLabels) 422 { 423 <th class="w-25 w-lg-50" scope="row">@field.Name</th> 424 } 425 <td class="text-break"> 426 @{ @RenderFieldValue(field) } 427 </td> 428 </tr> 429 } 430 if (layout == "bullets") 431 { 432 <li> 433 @if (!hideFieldLabels) 434 { 435 <strong>@field.Name</strong> 436 } 437 <span> 438 @{ @RenderFieldValue(field) } 439 </span> 440 </li> 441 } 442 } 443 } 444 } 445 446 @helper RenderFieldValue(FieldValueViewModel field) 447 { 448 string fieldValue = field?.Value != null ? field.Value.ToString() : ""; 449 450 bool isLink = field?.Type == "Link"; 451 bool isColor = false; 452 bool isBrandName = field?.SystemName == "Brand_name"; 453 454 fieldValue = fieldValue == "False" ? Translate("No") : fieldValue; 455 fieldValue = fieldValue == "True" ? Translate("Yes") : fieldValue; 456 457 458 if (field.Value.GetType() == typeof(System.Collections.Generic.List<Dynamicweb.Ecommerce.ProductCatalog.FieldOptionValueViewModel>)) 459 { 460 int valueCount = 0; 461 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 462 int totalValues = values.Count; 463 464 foreach (FieldOptionValueViewModel option in values) 465 { 466 if (!string.IsNullOrEmpty(option.Value)) 467 { 468 if (option.Value.Substring(0, 1) == "#") 469 { 470 isColor = true; 471 } 472 } 473 474 if (!isColor) 475 { 476 @option.Name 477 } 478 else 479 { 480 <span class="colorbox-sm" style="background-color: @option.Value" title="@option.Name"></span> 481 } 482 483 if (valueCount != totalValues && valueCount < (totalValues - 1)) 484 { 485 if (isColor) 486 { 487 <text> </text> 488 } 489 else 490 { 491 <text>, </text> 492 } 493 } 494 valueCount++; 495 } 496 } 497 else 498 { 499 if (fieldValue.Substring(0, 1) == "#") 500 { 501 isColor = true; 502 } 503 504 if (!isColor) 505 { 506 if (isLink) 507 { 508 string linktTitle = !fieldValue.Contains("aspx") ? fieldValue : Translate("Go to link"); 509 string target = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") && fieldValue.Contains("http") ? "target=\"_blank\"" : string.Empty; 510 string rel = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") && fieldValue.Contains("http") ? "rel=\"noopener\"" : string.Empty; 511 512 <a href="@field.Value" title="@field.Name" @target @rel>@linktTitle</a> 513 } 514 else if (isBrandName) 515 { 516 <span itemprop="brand" itemtype="https://schema.org/Brand" itemscope> 517 <span itemprop="name">@fieldValue</span> 518 </span> 519 } 520 else 521 { 522 @fieldValue 523 } 524 525 } 526 else 527 { 528 <span class="colorbox-sm" style="background-color: @fieldValue" title="@fieldValue"></span> 529 } 530 } 531 } 532
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_3d4e000841614f349ca9f17df6c8d3ba.Execute() in D:\dynamicweb.net\Solutions\Dynamicweb\bomedys.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_RelatedProducts.cshtml:line 171 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 if (Pageview.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 12 { 13 ProductListViewModel productList = Pageview.GetPageviewModel().Item.GetValue<ProductListViewModel>("DummyProduct"); 14 if (productList?.Products is object) 15 { 16 product = productList.Products[0]; 17 } 18 } 19 20 string title = Model?.Item?.GetString("Title") != null ? Model.Item.GetString("Title") : Translate("Products"); 21 string campaignValues = Model.Item.GetRawValueString("CampaignBadges", string.Empty); 22 23 //Styling 24 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "h3"); 25 string subtitleFontSize = Model.Item.GetRawValueString("SubtitleFontSize", "fs-5"); 26 string buttonStyle = Model.Item.GetRawValueString("ButtonStyle", ""); 27 buttonStyle = buttonStyle == "primary" ? " btn-primary" : buttonStyle; 28 buttonStyle = buttonStyle == "secondary" ? " btn-secondary" : buttonStyle; 29 buttonStyle = buttonStyle == "link" ? " btn-link" : buttonStyle; 30 string maxWidth = Model.Item.GetRawValueString("TextReadability", ""); 31 maxWidth = maxWidth == "max-width-on" ? " mw-75ch" : maxWidth; 32 maxWidth = maxWidth == "max-width-off" ? "" : maxWidth; 33 34 string generalTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("GeneralTheme")) ? " theme " + Model.Item.GetRawValueString("GeneralTheme").Replace(" ", "").Trim().ToLower() : ""; 35 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 36 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 37 38 //Link generation 39 string pageId = Model.Item.GetLink("ProductSliderServicePage") != null ? Model.Item.GetLink("ProductSliderServicePage").PageId.ToString() : ""; 40 string servicePageByNavigationTag = GetPageIdByNavigationTag("ProductSliderService") != 0 ? GetPageIdByNavigationTag("ProductSliderService").ToString() : ""; 41 pageId = pageId == "" ? servicePageByNavigationTag : pageId; 42 43 string url = "/Default.aspx?ID=" + pageId; 44 if (!url.Contains("LayoutTemplate")) 45 { 46 url += url.Contains("?") ? "&LayoutTemplate=Designs/Swift/Swift_PageClean.cshtml" : "?LayoutTemplate=Designs/Swift/Swift_PageClean.cshtml"; 47 } 48 if (Pageview.IsVisualEditorMode) 49 { 50 url += "&VisualEdit=True"; 51 } 52 53 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]); 54 if (isLazyLoadingForProductInfoEnabled) 55 { 56 url += "&getproductinfo=true"; 57 } 58 59 //Source type 60 string sourceType = Model.Item.GetRawValueString("RelationType", "trending"); 61 IList<string> relateFromGroupIds = new List<string> { }; 62 IList<string> relateFromProductVariantIds = new List<string> { }; 63 IList<string> relateFromProductIds = new List<string> { }; 64 bool hasVariants = false; 65 66 //--- VARIANTS --- 67 ProductListViewModel productsToRelateToVariants = Model.Item.GetValue<ProductListViewModel>("ProductsToRelateToVariants"); 68 if (productsToRelateToVariants != null && sourceType == "variants") 69 { 70 foreach (var productSelection in productsToRelateToVariants.Products) 71 { 72 relateFromProductIds.Add(productSelection.Id); 73 } 74 } 75 76 //--- MOST SOLD --- 77 IList<ProductGroupViewModel> groupsToRelateToMostSold = Model.Item.GetValue<IList<ProductGroupViewModel>>("GroupsToRelateToMostSold"); 78 if (groupsToRelateToMostSold != null && sourceType == "most-sold") 79 { 80 foreach (var fromGroup in groupsToRelateToMostSold) 81 { 82 relateFromGroupIds.Add(fromGroup.Id); 83 } 84 } 85 86 //--- TRENDING --- 87 IList<ProductGroupViewModel> groupsToRelateToTrending = Model.Item.GetValue<IList<ProductGroupViewModel>>("GroupsToRelateToTrending"); 88 if (groupsToRelateToTrending != null && sourceType == "trending") 89 { 90 foreach (var fromGroup in groupsToRelateToTrending) 91 { 92 relateFromGroupIds.Add(fromGroup.Id); 93 } 94 } 95 96 //--- LATEST --- 97 IList<ProductGroupViewModel> groupsToRelateToLatest = Model.Item.GetValue<IList<ProductGroupViewModel>>("GroupsToRelateToLatest"); 98 if (groupsToRelateToLatest != null && sourceType == "latest") 99 { 100 foreach (var fromGroup in groupsToRelateToLatest) 101 { 102 relateFromGroupIds.Add(fromGroup.Id); 103 } 104 } 105 106 //--- FREQUENTLY BOUGHT --- 107 ProductListViewModel productsToRelateTo = Model.Item.GetValue<ProductListViewModel>("ProductsToRelateTo"); 108 if (productsToRelateTo != null && sourceType == "frequently") 109 { 110 foreach (var fromProduct in productsToRelateTo.Products) 111 { 112 relateFromProductIds.Add(fromProduct.Id); 113 } 114 } 115 116 //--- SELECTED PRODUCTS --- 117 ProductListViewModel products = Model.Item.GetValue<ProductListViewModel>("Products"); 118 if (products != null && sourceType == "selected") 119 { 120 hasVariants = products.Products.Any(p => !string.IsNullOrEmpty(p.VariantId)); 121 foreach (var productSelection in products.Products) 122 { 123 if (hasVariants) 124 { 125 if (!string.IsNullOrEmpty(productSelection.VariantId)) 126 { 127 relateFromProductVariantIds.Add($"{productSelection.Id} {productSelection.VariantId}"); 128 } 129 else 130 { 131 relateFromProductVariantIds.Add($"{productSelection.Id}"); 132 } 133 } 134 relateFromProductIds.Add($"{productSelection.Id}"); 135 } 136 } 137 138 //--- RELATED PRODUCTS --- 139 if (sourceType == "related-products") 140 { 141 ProductListViewModel selectedRelationProduct = Model.Item.GetValue<ProductListViewModel>("ProductsToRelateTo2"); 142 if (selectedRelationProduct?.Products != null) 143 { 144 if (selectedRelationProduct.Products.Count > 0) 145 { 146 int productCount = 0; 147 foreach (var selectedProduct in selectedRelationProduct.Products) 148 { 149 if (productCount == 0) 150 { 151 product = selectedProduct; 152 productCount++; 153 } 154 } 155 } 156 } 157 158 if (product?.RelatedGroups != null) 159 { 160 foreach (var group in product.RelatedGroups) 161 { 162 foreach (var relatedProduct in group.Products) 163 { 164 relateFromProductIds.Add(relatedProduct.ProductId); 165 } 166 } 167 } 168 } 169 170 //Create group id collection and products id collection strings 171 string groupIds = product is object ? product.PrimaryOrDefaultGroup.Id : string.Join(",", relateFromGroupIds); 172 string productVariantIds = relateFromProductVariantIds.Count > 0 ? string.Join(",", relateFromProductVariantIds) : ""; 173 string productIds = product is object && relateFromProductIds.Count == 0 ? product.Id : string.Join(",", relateFromProductIds); 174 175 //Set the parameters to the url 176 string linkParameters = ""; 177 linkParameters += sourceType != "related-products" && sourceType != "frequently" && sourceType != "selected" ? "&GroupId=" + groupIds : ""; 178 linkParameters += !string.IsNullOrEmpty(productIds) && sourceType != "most-sold" && sourceType != "trending" && sourceType != "latest" && sourceType != "frequently" ? "&MainProductId=" + productIds : ""; 179 linkParameters += sourceType == "variants" ? "&IsVariant=true" : ""; 180 linkParameters += sourceType == "latest" ? "&SortBy=Created" : ""; 181 linkParameters += sourceType == "most-sold" ? "&SortBy=OrderCount" : ""; 182 linkParameters += sourceType == "trending" ? "&SortBy=OrderCountGrowth" : ""; 183 linkParameters += !string.IsNullOrEmpty(productIds) && sourceType == "frequently" ? $"&BoughtWithProductIds=[{productIds}]" : ""; 184 var productListPageId = GetPageIdByNavigationTag("Shop"); 185 string link = "/Default.aspx?ID=" + productListPageId + linkParameters; 186 187 // Slider settings (documentation: swiffyslider.com/configuration) 188 string navigationStyle = $"{Model.Item.GetRawValueString("NavigationStyle", "slider-nav-round")}"; 189 string navigationPlacement = $"{Model.Item.GetRawValueString("NavigationPlacement", "slider-nav-on-slides")}"; 190 string indicatorStyle = $"{Model.Item.GetRawValueString("IndicatorStyle", "slider-indicators-hidden")}"; 191 string revealSlides = Model.Item.GetRawValueString("RevealSlides", "no-reveal") == "reveal" ? "slider-item-reveal" : string.Empty; 192 string navigationAlwaysVisible = (Model.Item.GetBoolean("NavigationAlwaysVisible")) ? "slider-nav-visible" : string.Empty; 193 string navigationVisibleOnTouch = (Model.Item.GetBoolean("NavigationVisibleOnTouch")) ? "slider-nav-touch" : string.Empty; 194 string navigationShowScrollbar = (Model.Item.GetBoolean("NavigationShowScrollbar")) ? "slider-nav-scrollbar" : string.Empty; 195 string navigationSmall = (Model.Item.GetBoolean("NavigationSmall")) ? "slider-nav-sm" : string.Empty; 196 string navigationInvertColors = (Model.Item.GetBoolean("NavigationInvertColors")) ? "slider-nav-dark" : string.Empty; 197 string navigationSlideEntirePage = (Model.Item.GetBoolean("NavigationSlideEntirePage")) ? "slider-nav-page" : string.Empty; 198 string navigationNoLoop = (Model.Item.GetBoolean("NavigationNoLoop")) ? "slider-nav-noloop" : string.Empty; 199 string indicatorsOutsideSlider = (Model.Item.GetBoolean("IndicatorsOutsideSlider") && indicatorStyle != string.Empty) ? "slider-indicators-outside" : string.Empty; 200 string indicatorsHighlightActive = (Model.Item.GetBoolean("IndicatorsHighlightActive")) ? "slider-indicators-highlight" : string.Empty; 201 string indicatorsInvertColors = (Model.Item.GetBoolean("IndicatorsInvertedColors")) ? "slider-indicators-dark" : string.Empty; 202 string indicatorsVisibleOnSmallDevices = (Model.Item.GetBoolean("IndicatorsVisibleOnSmallDevices")) ? "slider-indicators-sm" : string.Empty; 203 204 bool productsFound = true; 205 if (string.IsNullOrEmpty(groupIds) && string.IsNullOrEmpty(productIds)) 206 { 207 if (Pageview.IsVisualEditorMode) 208 { 209 productIds = product.Id; 210 sourceType = "selected"; 211 } 212 else 213 { 214 productsFound = false; 215 } 216 } 217 } 218 219 @*Container element for the request*@ 220 @if (productsFound) 221 { 222 <form method="post" action="@url" id="[email protected]" data-response-target-element="[email protected]" data-preloader="inline" data-update-url="false" class="[email protected]()"> 223 <input type="hidden" name="ModelID" value="@Model.ID"> 224 <input type="hidden" name="SourceType" value="@sourceType"> 225 226 @*--- SLIDER SETTINGS ---*@ 227 <input type="hidden" name="NavigationStyle" value="@navigationStyle"> 228 <input type="hidden" name="NavigationPlacement" value="@navigationPlacement"> 229 <input type="hidden" name="IndicatorStyle" value="@indicatorStyle"> 230 <input type="hidden" name="RevealSlides" value="@revealSlides"> 231 <input type="hidden" name="NavigationAlwaysVisible" value="@(navigationAlwaysVisible)"> 232 <input type="hidden" name="NavigationVisibleOnTouch" value="@(navigationVisibleOnTouch)"> 233 <input type="hidden" name="NavigationShowScrollbar" value="@(navigationShowScrollbar)"> 234 <input type="hidden" name="NavigationSmall" value="@(navigationSmall)"> 235 <input type="hidden" name="NavigationInvertColors" value="@(navigationInvertColors)"> 236 <input type="hidden" name="NavigationNoLoop" value="@(navigationNoLoop)"> 237 <input type="hidden" name="NavigationSlideEntirePage" value="@(navigationSlideEntirePage)"> 238 <input type="hidden" name="IndicatorsOutsideSlider" value="@(indicatorsOutsideSlider)"> 239 <input type="hidden" name="IndicatorsHighlightActive" value="@(indicatorsHighlightActive)"> 240 <input type="hidden" name="IndicatorsInvertColors" value="@(indicatorsInvertColors)"> 241 <input type="hidden" name="IndicatorsVisibleOnSmallDevices" value="@(indicatorsVisibleOnSmallDevices)"> 242 243 @*--- VARIANTS ---*@ 244 @if (sourceType == "variants") 245 { 246 <input type="hidden" name="isVariant" value="true"> 247 <input type="hidden" name="MainProductID" id="[email protected]" value="@productIds"> 248 } 249 250 @*--- MOST SOLD ---*@ 251 @if (sourceType == "most-sold") 252 { 253 <input type="hidden" name="SortBy" value="OrderCount"> 254 if (groupIds != "") 255 { 256 <input type="hidden" name="GroupId" value="@groupIds"> 257 } 258 } 259 260 @*--- TRENDING ---*@ 261 @if (sourceType == "trending") 262 { 263 <input type="hidden" name="SortBy" value="OrderCountGrowth"> 264 if (groupIds != "") 265 { 266 <input type="hidden" name="GroupId" value="@groupIds"> 267 } 268 } 269 270 @*--- FREQUENTLY BOUGHT ---*@ 271 @if (sourceType == "frequently" && !string.IsNullOrEmpty(productIds)) 272 { 273 <input type="hidden" name="BoughtWithProductIds" value="[@productIds]"> 274 } 275 276 @*--- LATEST ---*@ 277 @if (sourceType == "latest") 278 { 279 <input type="hidden" name="SortBy" value="Created"> 280 <input type="hidden" name="GroupId" value="@groupIds"> 281 } 282 283 @*--- SELECTED PRODUCTS ---*@ 284 @if (sourceType == "selected" && !string.IsNullOrEmpty(productIds) && !hasVariants) 285 { 286 <input type="hidden" name="MainProductId" id="[email protected]" value="@productIds" /> 287 } 288 @if (sourceType == "selected" && hasVariants) 289 { 290 <input type="hidden" name="ProductVariantId" value="@productVariantIds" /> 291 } 292 293 @*--- RELATED PRODUCTS ---*@ 294 @if (sourceType == "related-products") 295 { 296 <input type="hidden" name="MainProductID" id="[email protected]" value="@productIds"> 297 } 298 299 @* General parameters *@ 300 <input type="hidden" name="Link" value="@link" /> 301 <input type="hidden" name="HideTitle" value="@Model.Item.GetString("HideTitle")" /> 302 303 @if (Model.Item.GetInt32("ProductsCount") != 0) 304 { 305 <input type="hidden" name="PageSize" value="@Model.Item.GetInt32("ProductsCount")"> 306 } 307 <input type="hidden" name="HeadingTitle" id="[email protected]" value="@title"> 308 @if (!string.IsNullOrEmpty(Model.Item.GetString("Subtitle"))) 309 { 310 <input type="hidden" name="Subtitle" value="@Model.Item.GetString("Subtitle")"> 311 } 312 @if (!string.IsNullOrEmpty(Model.Item.GetString("LinkText"))) 313 { 314 <input type="hidden" name="LinkText" value="@Model.Item.GetString("LinkText")"> 315 } 316 @if (!string.IsNullOrEmpty(Model.Item.GetString("ImageAspectRatio"))) 317 { 318 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 319 ratio = ratio != "0" ? ratio : ""; 320 <input type="hidden" name="ImageAspectRatio" value="@ratio"> 321 } 322 @if (!string.IsNullOrEmpty(Model.Item.GetString("Layout"))) 323 { 324 <input type="hidden" name="Layout" value="@Model.Item.GetRawValueString("Layout")"> 325 } 326 @if (titleFontSize != "") 327 { 328 <input type="hidden" name="TitleFontSize" value="@titleFontSize"> 329 } 330 @if (subtitleFontSize != "") 331 { 332 <input type="hidden" name="SubtitleFontSize" value="@subtitleFontSize"> 333 } 334 @if (buttonStyle != "") 335 { 336 <input type="hidden" name="ButtonStyle" value="@buttonStyle"> 337 } 338 @if (generalTheme != "") 339 { 340 <input type="hidden" name="GeneralTheme" value="@generalTheme"> 341 } 342 @if (theme != "") 343 { 344 <input type="hidden" name="Theme" value="@theme"> 345 } 346 @if (imageTheme != "") 347 { 348 <input type="hidden" name="ImageTheme" value="@imageTheme"> 349 } 350 @if (!string.IsNullOrEmpty(Model.Item.GetString("ContentPadding"))) 351 { 352 string contentPadding = Model.Item.GetRawValueString("ContentPadding"); 353 <input type="hidden" name="ContentPadding" value="@contentPadding"> 354 } 355 <input type="hidden" name="TextReadability" value="@maxWidth"> 356 <input type="hidden" name="ParentColumnSize" id="[email protected]" value="12"> 357 358 <input type="hidden" name="SaleBadgeType" value="@Model.Item.GetRawValue("SaleBadgeType")"> 359 <input type="hidden" name="SaleBadgeCssClassName" value="@Model.Item.GetRawValue("SaleBadgeDesign")"> 360 <input type="hidden" name="NewBadgeCssClassName" value="@Model.Item.GetRawValue("NewBadgeDesign")"> 361 <input type="hidden" name="NewPublicationDays" value="@Model.Item.GetInt32("NewPublicationDays")"> 362 363 @if (campaignValues != "") 364 { 365 <input type="hidden" name="CampaignBadgesValues" value="@campaignValues"> 366 } 367 </form> 368 369 <script type="module" src="~/Files/Templates/Designs/Swift/Assets/js/swiffy-slider.js"></script> 370 <script type="module"> 371 swift.AssetLoader.Load('/Files/Templates/Designs/Swift/Assets/css/swiffy-slider.min.css', 'css'); 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 <a id="@Model.ID" class="user-select-none" style="scroll-margin-top:var(--header-height,150px)"></a> 385 <div id="[email protected]" 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 <a id="@Model.ID" class="user-select-none" style="scroll-margin-top:var(--header-height,150px)"></a> 393 @foreach (var group in product.RelatedGroups) 394 { 395 <div id="RelatedProducts_@(Model.ID)[email protected]" 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("#[email protected]").closest("[data-col-size]")) { 405 document.querySelector("#[email protected]").value = document.querySelector("#[email protected]").closest("[data-col-size]").getAttribute("data-col-size"); 406 } 407 swift.PageUpdater.Update(document.querySelector("#[email protected]")).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 fromProductIds.Add(relatedProduct.ProductId); 432 } 433 <script type="module"> 434 document.querySelector("#[email protected]").value = document.querySelector("#RelatedProducts_@(Model.ID)[email protected]").closest("[data-col-size]").getAttribute("data-col-size"); 435 document.querySelector("#[email protected]").value = "@string.Join(",", fromProductIds)"; 436 document.querySelector("#[email protected]").value = "@group.Name"; 437 document.querySelector("#[email protected]").setAttribute("data-response-target-element", "RelatedProducts_@(Model.ID)[email protected]"); 438 439 swift.PageUpdater.Update(document.querySelector("#[email protected]")); 440 </script> 441 } 442 } 443 } 444
Sorry. There is nothing to view here