Error executing template "Designs/Swift-v2/Paragraph/Swift-v2_ProductMedia.cshtml" System.NullReferenceException: Object reference not set to an instance of an object. at CompiledRazorTemplates.Dynamic.RazorEngine_ebacf8dd7eb44952bd6cc94a52898bf6.ExecuteAsync() at RazorEngine.Templating.TemplateBase.Run(ExecuteContext context, TextWriter reader) at RazorEngine.Templating.RazorEngineCore.RunTemplate(ICompiledTemplate template, TextWriter writer, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.DynamicWrapperService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass23_0.<Run>b__0(TextWriter writer) at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter) at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, Type modelType, Object model, DynamicViewBag viewBag) 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.Encoders 3 @using Dynamicweb.Ecommerce.ProductCatalog 4 @using Dynamicweb.Frontend 5 @using System.IO 6 @using System.Text.RegularExpressions; 7 8 @functions { 9 public ProductViewModel product { get; set; } = new ProductViewModel(); 10 public string galleryLayout { get; set; } 11 public string[] supportedImageFormats { get; set; } 12 public string[] supportedVideoFormats { get; set; } 13 public string[] supportedDocumentFormats { get; set; } 14 public string[] allSupportedFormats { get; set; } 15 16 public class RatioSettings 17 { 18 public string Ratio { get; set; } 19 public string CssClass { get; set; } 20 public string CssVariable { get; set; } 21 public string Fill { get; set; } 22 } 23 24 public RatioSettings GetRatioSettings(string size = "desktop") 25 { 26 var ratioSettings = new RatioSettings(); 27 28 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 29 ratio = ratio != "0" ? ratio : ""; 30 string cssClass = ratio != "" && ratio != "fill" ? " ratio" : ""; 31 string cssVariable = ratio != "" && ratio != "fill" ? "--bs-aspect-ratio: " + ratio : ""; 32 cssClass = ratio == "fill" && size == "mobile" ? " ratio" : cssClass; 33 cssVariable = ratio == "fill" && size == "mobile" ? "--bs-aspect-ratio: 66%" : cssVariable; 34 35 ratioSettings.Ratio = ratio; 36 ratioSettings.CssClass = cssClass; 37 ratioSettings.CssVariable = cssVariable; 38 ratioSettings.Fill = ratio == "fill" ? " h-100" : ""; 39 40 return ratioSettings; 41 } 42 43 public string GetArrowsColor() 44 { 45 var invertColor = Model.Item.GetBoolean("InvertModalArrowsColor"); 46 var arrowsColor = invertColor ? " carousel-dark" : string.Empty; 47 return arrowsColor; 48 } 49 50 public string GetThumbnailPlacement() 51 { 52 return Model.Item.GetRawValueString("ThumbnailPlacement", "bottom"); 53 } 54 55 public string GetThumbnailRowSettingCss() 56 { 57 switch (GetThumbnailPlacement()) 58 { 59 case "bottom": 60 return "d-flex flex-wrap"; 61 case "left": 62 return "d-flex flex-column order-first"; 63 case "right": 64 return "d-flex flex-column order-last"; 65 default: 66 return "d-flex flex-wrap"; 67 } 68 } 69 70 public string GetVideoType(string assetValue) 71 { 72 string type = assetValue.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "youtube" : string.Empty; 73 type = assetValue.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "vimeo" : type; 74 type = string.IsNullOrEmpty(type) ? "selfhosted" : type; 75 76 return type; 77 } 78 79 public string GetYoutubeScreenDump(string assetValue, string quality) 80 { 81 var regex = new Regex(@"(?:youtube\.com\/.*[\?&]v=|youtu\.be\/|youtube\.com\/embed\/)([\w-]+)(?:\?.*)?"); 82 Match match = regex.Match(assetValue); 83 string videoId = match.Success ? match.Groups[1].Value : string.Empty; 84 string youtubeThumbnail = $"https://img.youtube.com/vi/{videoId}/{quality}.jpg"; 85 return youtubeThumbnail; 86 } 87 } 88 89 @{ 90 ProductViewModel product = null; 91 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 92 { 93 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 94 } 95 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 96 { 97 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 98 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 99 100 if (productList?.Products is object) 101 { 102 product = productList.Products[0]; 103 } 104 } 105 } 106 107 @if (product is object) 108 { 109 @* Supported formats *@ 110 supportedImageFormats = new string[] { ".jpg", ".jpeg", ".webp", ".png", ".gif", ".bmp", ".tiff"}; 111 supportedVideoFormats = new string[] { "youtu.be", "youtube", "vimeo", ".mp4", ".webm" }; 112 supportedDocumentFormats = new string[] { ".pdf", ".docx", ".xlsx", ".ppt", "pptx" }; 113 allSupportedFormats = supportedImageFormats.Concat(supportedVideoFormats).Concat(supportedDocumentFormats).ToArray(); 114 115 @* Collect the assets *@ 116 var selectedAssetCategories = Model.Item.GetList("ImageAssets")?.GetRawValue().OfType<string>(); 117 bool includeImagePatternImages = Model.Item.GetBoolean("ImagePatternImages"); 118 119 @* Needed image data collection to support both DefaultImage, ImagePatterns and Image Assets *@ 120 string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : ""; 121 IEnumerable<MediaViewModel> assetsImages = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets); 122 assetsImages = assetsImages.OrderByDescending(x => x.Value.Equals(defaultImage)); 123 IEnumerable<MediaViewModel> assetsList = new MediaViewModel[] { }; 124 assetsList = assetsList.Union(assetsImages); 125 assetsList = includeImagePatternImages ? assetsList.Union(product.ImagePatternImages) : assetsList; 126 assetsList = includeImagePatternImages && assetsList.Count() == 0 ? assetsList.Append(product.DefaultImage) : assetsList; 127 128 bool defaultImageFallback = Model.Item.GetBoolean("DefaultImageFallback"); 129 bool showOnlyPrimaryImage = Model.Item.GetBoolean("ShowOnlyPrimaryImage"); 130 131 int totalAssets = 0; 132 if (showOnlyPrimaryImage == false) 133 { 134 foreach (MediaViewModel asset in assetsList) 135 { 136 var assetValue = asset.Value; 137 foreach (string format in allSupportedFormats) 138 { 139 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 140 { 141 totalAssets++; 142 } 143 } 144 } 145 } 146 147 if ((totalAssets == 0 && product.DefaultImage != null && selectedAssetCategories.Count() == 0) || (showOnlyPrimaryImage == true && product.DefaultImage != null) || totalAssets == 0 && defaultImageFallback) 148 { 149 assetsList = new List<MediaViewModel>() { product.DefaultImage }; 150 totalAssets = 1; 151 } 152 153 @* Get assets from selected categories or get all assets *@ 154 if (totalAssets != 0) 155 { 156 int assetNumber = 0; 157 int thumbnailNumber = 0; 158 int modalAssetNumber = 0; 159 string thumbnailAxisCss = GetThumbnailPlacement() == "bottom" ? "flex-column" : string.Empty; 160 161 <div class="d-flex gap-3 h-100 @(thumbnailAxisCss) item_@Model.Item.SystemName.ToLower()" data-dw-colorscheme="@Model.ColorScheme?.Id"> 162 <div id="SmallScreenImages_@Model.ID" class="carousel@(GetArrowsColor()) col position-relative" data-bs-ride="carousel"> 163 <div class="carousel-inner h-100"> 164 @foreach (MediaViewModel asset in assetsList) 165 { 166 var assetValue = asset.Value; 167 foreach (string format in allSupportedFormats) 168 { 169 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 170 { 171 string activeSlide = assetNumber == 0 ? "active" : ""; 172 173 <div class="carousel-item @activeSlide" data-bs-interval="99999"> 174 @{ 175 string size = "mobile"; 176 177 <div class="h-100"> 178 @foreach (string imageFormat in supportedImageFormats) 179 { //Images 180 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 181 { 182 if (product is object) 183 { 184 string productName = product.Name; 185 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 186 187 RatioSettings ratioSettings = GetRatioSettings(size); 188 189 var parms = new Dictionary<string, object>(); 190 parms.Add("alt", productName + asset.Keywords); 191 parms.Add("itemprop", "image"); 192 parms.Add("columns", Model.GridRowColumnCount); 193 parms.Add("eagerLoadNewImages", Model.Item.GetBoolean("DisableLazyLoading")); 194 parms.Add("doNotUseGetimage", Model.Item.GetBoolean("DisableGetImage")); 195 if (!string.IsNullOrEmpty(asset.DisplayName)) 196 { 197 parms.Add("title", asset.DisplayName); 198 } 199 200 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") 201 { 202 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 203 } 204 else 205 { 206 parms.Add("cssClass", "mw-100 mh-100"); 207 } 208 209 <a href="@imagePath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 210 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@assetNumber"> 211 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 212 </div> 213 </a> 214 } 215 } 216 } 217 @foreach (string videoFormat in supportedVideoFormats) 218 { //Videos 219 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 220 { 221 if (product is object) { 222 var video = asset.GetVideoViewModel(); 223 224 if (Model.Item.GetString("OpenVideoInModal") == "true") 225 { 226 string iconPath = "/Files/Images/Icons/"; 227 228 string productName = product.Name; 229 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 230 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 231 232 RatioSettings ratioSettings = GetRatioSettings(size); 233 234 string type = GetVideoType(asset.Value); 235 236 string videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value, "maxresdefault") : string.Empty; 237 string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : ""; 238 239 240 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable); cursor: pointer" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 241 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@assetNumber"> 242 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 243 @if (video.IsExternalLink()) 244 { 245 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@productName" @assetTitle class="@videoJsClass mw-100 mh-100" data-asset-value="@asset.Value" style="object-fit: cover;"> 246 } 247 else 248 { 249 250 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 251 <source src="@(asset.Value)#t=0.001" type="@video.GetVideoType()"> 252 </video> 253 } 254 </div> 255 </div> 256 } 257 else 258 { 259 @RenderPartial("Components/VideoPlayer.cshtml", video) 260 } 261 } 262 } 263 } 264 @foreach (string documentFormat in supportedDocumentFormats) 265 { //Documents 266 if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) 267 { 268 if (product is object) 269 { 270 string iconPath = "/Files/Images/Icons/"; 271 272 string productName = product.Name; 273 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 274 275 RatioSettings ratioSettings = GetRatioSettings(size); 276 277 var parms = new Dictionary<string, object>(); 278 parms.Add("alt", productName + asset.Keywords); 279 parms.Add("itemprop", "image"); 280 parms.Add("fullwidth", true); 281 parms.Add("columns", Model.GridRowColumnCount); 282 if (!string.IsNullOrEmpty(asset.DisplayName)) 283 { 284 parms.Add("title", asset.DisplayName); 285 } 286 287 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") 288 { 289 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 290 } 291 else 292 { 293 parms.Add("cssClass", "mw-100 mh-100"); 294 } 295 296 <a href="@imagePath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download alt="@Translate("Download")"> 297 <div class="d-flex align-items-center justify-content-center text-center overflow-hidden h-100 border"> 298 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 299 <span>@Translate("Download") @(asset.Name)@Path.GetExtension(asset.Value).ToLower()</span> 300 </div> 301 </a> 302 } 303 304 } 305 } 306 </div> 307 } 308 309 310 </div> 311 assetNumber++; 312 } 313 } 314 } 315 </div> 316 317 </div> 318 319 @if (totalAssets > 1) 320 { 321 //Custom: added support for STL. Moved div to prevent empty thumbnails 322 <div style="width:100%; background-color:#E9EAEB; height:1px; margin:auto;"></div> 323 <div class="@(GetThumbnailRowSettingCss()) gap-3" id="SmallScreenImagesThumbnails_@Model.ID"> 324 @foreach (MediaViewModel asset in assetsList) 325 { 326 var assetValue = asset.Value; 327 string assetName = asset.Name; 328 assetName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 329 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : null; 330 string iconPath = "/Files/Images/Icons/"; 331 332 string imagePath = assetValue; 333 imagePath = assetValue.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "https://img.youtube.com/vi/" + assetValue.Substring(assetValue.LastIndexOf('/') + 1) + "/mqdefault.jpg" : imagePath; 334 string imagePathThumb = assetValue.StartsWith("/Files/", StringComparison.OrdinalIgnoreCase) ? imagePath.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) < 0 && imagePath.IndexOf(".mp4", StringComparison.OrdinalIgnoreCase) < 0 ? $"/Admin/Public/GetImage.ashx?image={imagePath}&width=180&format=webp" : imagePath : assetValue; 335 336 RatioSettings ratioSettings = GetRatioSettings("desktop"); 337 338 @foreach (string imageFormat in supportedImageFormats) 339 { //Images 340 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 341 { 342 <div class="border border-round outline-none position-relative @(ratioSettings.CssClass)" style="@(ratioSettings.CssVariable); cursor: pointer; min-width: 50px; max-width: 50px; min-height: 50px; max-height: 50px;" data-bs-target="#SmallScreenImages_@Model.ID" data-bs-slide-to="@thumbnailNumber"> 343 <img src="@imagePathThumb" alt="@HtmlEncoder.HtmlAttributeEncode(assetName)" class="border-round p-0 p-lg-1 w-100 h-100" style="object-fit: contain;"> 344 </div> 345 thumbnailNumber++; 346 } 347 } 348 349 @foreach (string videoFormat in supportedVideoFormats) 350 { //Videos 351 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 352 { 353 var video = asset.GetVideoViewModel(); 354 string type = GetVideoType(asset.Value); 355 356 string videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value, "mqdefault") : ""; 357 videoScreendumpPath = type == "vimeo" ? string.Empty : videoScreendumpPath; 358 string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : string.Empty; 359 <div class="border border-round outline-none position-relative @(ratioSettings.CssClass)" style="@(ratioSettings.CssVariable); cursor: pointer; min-width: 50px; max-width: 50px; min-height: 50px; max-height: 50px;" data-bs-target="#SmallScreenImages_@Model.ID" data-bs-slide-to="@thumbnailNumber"> 360 <div class="icon-5 position-absolute top-50 start-50 translate-middle" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 361 362 @if (video.IsExternalLink()) 363 { 364 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@assetTitle" @assetTitle class="@videoJsClass mw-100 mh-100" data-asset-value="@asset.Value" style="object-fit: cover;" /> 365 } 366 else 367 { 368 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 369 <source src="@(asset.Value)#t=0.001" type="@video.GetVideoType()"> 370 </video> 371 } 372 </div> 373 thumbnailNumber++; 374 } 375 } 376 377 @foreach (string documentFormat in supportedDocumentFormats) 378 { //Documents 379 if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) 380 { 381 <div class="border border-round outline-none position-relative @(ratioSettings.CssClass)" style="@(ratioSettings.CssVariable); cursor: pointer; min-width: 50px; max-width: 50px; min-height: 50px; max-height: 50px;" data-bs-target="#SmallScreenImages_@Model.ID" data-bs-slide-to="@thumbnailNumber"> 382 <a href="@assetValue" class="ratio ratio-4x3 border outline-none" style="cursor: pointer; min-width: 7rem; max-width: 8rem;" download title="@asset.Value"> 383 <div class="d-flex align-items-center justify-content-center text-center overflow-hidden h-100 border"> 384 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 385 <span>@(asset.Name)@Path.GetExtension(asset.Value).ToLower()</span> 386 </div> 387 </a> 388 </div> 389 390 thumbnailNumber++; 391 } 392 } 393 394 if (assetValue.IndexOf(".stl", StringComparison.OrdinalIgnoreCase) >= 0) 395 { 396 var materialColor = "#009870"; 397 var backgroundColor = "#FFFFFF"; 398 399 product.ProductFields.TryGetValue("CustomSp3DModelColor", out FieldValueViewModel color); 400 401 if (!string.IsNullOrEmpty(Dynamicweb.Core.Converter.ToString(color?.Value))) 402 { 403 materialColor = Dynamicweb.Core.Converter.ToString(color.Value); 404 } 405 406 <div class="@(Pageview.Device == Dynamicweb.Frontend.Devices.DeviceType.Desktop ? "flex-fill text-end" : "")"> 407 <button class="btn btn-link text-decoration-none" data-dw-button="btn-link" data-bs-toggle="modal" data-bs-target="#STL3DModelModal" onclick="swift.Custom.STLViewer('stl3dmodel');"> 408 <span class="text-decoration-none"> 409 @ReadFile("Files/Images/Icons/Custom/3d-model.svg") 410 <span class="text-decoration-underline ms-1"> 411 @Translate("Custom.MediaGallery.ViewModel", "View 3D model") 412 </span> 413 </span> 414 </button> 415 <div class="modal" id="STL3DModelModal"> 416 <div class="modal-dialog modal-dialog-centered"> 417 <div class="modal-content"> 418 <div class="modal-header text-center border-0"> 419 <div class="modal-title text-md semi-bold flex-fill paragraph-md-semibold">@Translate("Custom.MediaGallery.ViewModel.Modal.Header", "3D model")</div> 420 <button type="button" class="btn-close position-absolute top-1 end-0 me-2" data-bs-dismiss="modal" aria-label="Close" onclick="document.querySelector('body').style = ''"></button> 421 </div> 422 <div class="modal-body"> 423 <div id="stl3dmodel" style="width: 400px; height: 400px; margin: auto;" data-file="@assetValue" data-name="@assetName" data-color="@materialColor" data-background-color="@backgroundColor" data-light-skycolor="#FFFFFF" data-light-groundcolor="#000000"> 424 </div> 425 </div> 426 </div> 427 </div> 428 </div> 429 </div> 430 } 431 } 432 </div> 433 } 434 </div> 435 @* Modal with slides *@ 436 <div class="modal fade" id="modal_@Model.ID" tabindex="-1" aria-labelledby="mediaModalTitle_@Model.ID" aria-hidden="true"> 437 <div class="modal-dialog modal-dialog-centered modal-xl"> 438 <div class="modal-content"> 439 <div class="modal-header visually-hidden"> 440 <h5 class="modal-title" id="mediaModalTitle_@Model.ID">@product.Title</h5> 441 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 442 </div> 443 <div class="modal-body p-2 p-lg-3 h-100"> 444 <div id="ModalCarousel_@Model.ID" class="carousel@(GetArrowsColor()) h-100" data-bs-ride="carousel"> 445 <div class="carousel-inner h-100" data-dw-colorscheme="@Model.ColorScheme?.Id"> 446 @foreach (MediaViewModel asset in assetsList) 447 { 448 var assetValue = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 449 foreach (string supportedFormat in supportedImageFormats.Concat(supportedVideoFormats).ToArray()) 450 { 451 if (assetValue.IndexOf(supportedFormat, StringComparison.OrdinalIgnoreCase) >= 0) 452 { 453 string imagePath = assetValue; 454 string activeSlide = modalAssetNumber == 0 ? "active" : ""; 455 456 var parms = new Dictionary<string, object>(); 457 parms.Add("cssClass", "d-block mw-100 mh-100 m-auto"); 458 parms.Add("fullwidth", true); 459 parms.Add("columns", Model.GridRowColumnCount); 460 461 <div class="carousel-item @activeSlide h-100" data-bs-interval="99999"> 462 @foreach (string imageFormat in supportedImageFormats) 463 { //Images 464 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 465 { 466 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 467 } 468 } 469 470 @foreach (string videoFormat in supportedVideoFormats) 471 { //Videos 472 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 473 { 474 @RenderPartial("Components/VideoPlayer.cshtml", asset.GetVideoViewModel()) 475 } 476 } 477 </div> 478 modalAssetNumber++; 479 } 480 } 481 } 482 <button class="carousel-control-prev carousel-control-area" type="button" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide="prev"> 483 <span class="carousel-control-prev-icon" aria-hidden="true"></span> 484 <span class="visually-hidden">@Translate("Previous")</span> 485 </button> 486 <button class="carousel-control-next carousel-control-area" type="button" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide="next"> 487 <span class="carousel-control-next-icon" aria-hidden="true"></span> 488 <span class="visually-hidden">@Translate("Next")</span> 489 </button> 490 </div> 491 </div> 492 </div> 493 </div> 494 </div> 495 </div> 496 } 497 else if (Pageview.IsVisualEditorMode) 498 { 499 RatioSettings ratioSettings = GetRatioSettings("desktop"); 500 501 <div class="h-100" data-dw-colorscheme="@Model.ColorScheme?.Id"> 502 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> 503 <img src="/Files/Images/nopic.png" loading="lazy" decoding="async" class="mh-100 mw-100" style="object-fit: cover;"> 504 </div> 505 </div> 506 } 507 } 508 else if (Pageview.IsVisualEditorMode) 509 { 510 <div class="alert alert-dark m-0">@Translate("The images will be shown here, if any")</div> 511 } 512 513 514 515
Cyl. Caps with Ribs (GV)
Ideal for male brake, fuel, and some common rail connectors, these flexible protective caps fit a wide range of sizes. The ribs provide excess pressure release when needed, and the soft material adds flexibility for various customer requirements.
Error compiling template "Designs/Swift-v2/Paragraph/CustomProductActions.cshtml" Line 29: Argument 1: cannot convert from 'Dynamicweb.Frontend.LinkViewModel' to 'string?'
1 // <auto-generated/> 2 #pragma warning disable 1591 3 namespace CompiledRazorTemplates.Dynamic 4 { 5 #line hidden 6 using System.Threading.Tasks; 7 using System; 8 using System.Collections.Generic; 9 using System.Linq; 10 using Dynamicweb.Core; 11 using Dynamicweb.Ecommerce.ProductCatalog; 12 internal class RazorEngine_f80e0909a2aa4b70be7cf547cd88a808 : Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 13 { 14 #pragma warning disable 1998 15 public async override global::System.Threading.Tasks.Task ExecuteAsync() 16 { 17 WriteLiteral("\r\n"); 18 19 ProductViewModel product = null; 20 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 21 { 22 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 23 } 24 25 var iconPath = "Files/Images/Icons/Custom/"; 26 27 var productFamilyParagraphLink = Pageview.SearchFriendlyUrl + "#" + Model.Item.GetLink("ProductFamilyParagraph"); 28 var signInPageLink = Model.Item.GetLink("SignInPage"); 29 bool hasSignInPageLink = !string.IsNullOrWhiteSpace(signInPageLink); 30 31 bool showPricesWithVat = Dynamicweb.Ecommerce.Common.Context.DisplayPricesWithVat; 32 bool neverShowVat = string.IsNullOrEmpty(showPricesWithVat.ToString()); 33 string unitId = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("UnitId")) ? Dynamicweb.Context.Current.Request.Form.Get("UnitId") : string.Empty; 34 WriteLiteral("\r\n"); 35 if (product is object) 36 { 37 if (Pageview.User == null) { 38 39 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", ""); 40 bool hideAddToCart = anonymousUsersLimitations.Contains("cart"); 41 hideAddToCart = Pageview.IsVisualEditorMode ? false : hideAddToCart; 42 43 WriteLiteral("\t\t<div class=\"container\">\r\n\t\t\t<div class=\"row\">\r\n"); 44 if (!hideAddToCart) 45 { 46 WriteLiteral("\t\t\t\t\t<div class=\"col-4 border-end ps-0 d-flex flex-column justify-content-between\">\r\n\t\t\t\t\t\t<div class=\"text-lg semi-bold mb-2\">"); 47 Write(Translate("Custom.ProductActions.Try", "Try before you decide")); 48 WriteLiteral("</div>\r\n\t\t\t\t\t\t<a"); 49 BeginWriteAttribute("href", " href=\"", 1581, "\"", 1615, 1); 50 WriteAttributeValue("", 1588, productFamilyParagraphLink, 1588, 27, false); 51 EndWriteAttribute(); 52 WriteLiteral(" class=\"btn btn-primary w-100\" data-dw-button=\"primary\">\r\n\t\t\t\t\t\t\t<span>"); 53 Write(Translate("Custom.ProductActions.Try.ButtonText", "Request sample")); 54 WriteLiteral("<span class=\"ps-2\">"); 55 Write(ReadFile(iconPath + "arrow-down.svg")); 56 WriteLiteral("</span></span>\r\n\t\t\t\t\t\t</a>\r\n\t\t\t\t\t</div>\r\n"); 57 } 58 WriteLiteral("\t\t\t\t<div"); 59 BeginWriteAttribute("class", " class=\"", 1868, "\"", 1962, 6); 60 WriteAttributeValue("", 1876, "col-", 1876, 4, true); 61 WriteAttributeValue("", 1880, hideAddToCart ? "6" : "4", 1880, 28, false); 62 WriteAttributeValue(" ", 1908, "border-end", 1909, 11, true); 63 WriteAttributeValue(" ", 1919, "d-flex", 1920, 7, true); 64 WriteAttributeValue(" ", 1926, "flex-column", 1927, 12, true); 65 WriteAttributeValue(" ", 1938, "justify-content-between", 1939, 24, true); 66 EndWriteAttribute(); 67 WriteLiteral(">\r\n\t\t\t\t\t<div class=\"text-lg semi-bold mb-2\">"); 68 Write(Translate("Custom.ProductActions.Variants", "Variants")); 69 WriteLiteral("</div>\r\n\t\t\t\t\t<a"); 70 BeginWriteAttribute("href", " href=\"", 2078, "\"", 2112, 1); 71 WriteAttributeValue("", 2085, productFamilyParagraphLink, 2085, 27, false); 72 EndWriteAttribute(); 73 WriteLiteral(" class=\"btn btn-outline-secondary w-100\" data-dw-button=\"outline-secondary\">\r\n\t\t\t\t\t\t<span>"); 74 Write(Translate("Custom.ProductActions.Variants.ButtonText", "View products")); 75 WriteLiteral("<span class=\"ps-2\">"); 76 Write(ReadFile(iconPath + "arrow-down.svg")); 77 WriteLiteral("</span></span>\r\n\t\t\t\t\t</a>\r\n\t\t\t\t</div>\r\n\t\t\t\t<div"); 78 BeginWriteAttribute("class", " class=\"", 2379, "\"", 2462, 5); 79 WriteAttributeValue("", 2387, "col-", 2387, 4, true); 80 WriteAttributeValue("", 2391, hideAddToCart ? "6" : "4", 2391, 28, false); 81 WriteAttributeValue(" ", 2419, "d-flex", 2420, 7, true); 82 WriteAttributeValue(" ", 2426, "flex-column", 2427, 12, true); 83 WriteAttributeValue(" ", 2438, "justify-content-between", 2439, 24, true); 84 EndWriteAttribute(); 85 WriteLiteral(">\r\n\t\t\t\t\t<div class=\"text-lg semi-bold d-inline-flex align-items-center mb-2\">\r\n\t\t\t\t\t\t<div class=\"rounded-circle green me-1\">\r\n\t\t\t\t\t\t</div>\r\n\t\t\t\t\t\t"); 86 Write(Translate("Custom.ProductActions.Stocklabel", "In stock")); 87 WriteLiteral("\r\n\t\t\t\t\t</div>\r\n"); 88 if (!hideAddToCart && hasSignInPageLink) 89 { 90 WriteLiteral("\t\t\t\t\t\t<a"); 91 BeginWriteAttribute("href", " href=\"", 2746, "\"", 2768, 1); 92 WriteAttributeValue("", 2753, signInPageLink, 2753, 15, false); 93 EndWriteAttribute(); 94 WriteLiteral(" class=\"btn btn-outline-secondary w-100\" data-dw-button=\"outline-secondary\">\r\n\t\t\t\t\t\t\t<span>"); 95 Write(Translate("Custom.ProductActions.SignIn.ButtonText", "Sign in to buy")); 96 WriteLiteral("<span class=\"ps-2\">"); 97 Write(ReadFile(iconPath + "shopping-cart.svg")); 98 WriteLiteral("</span></span>\r\n\t\t\t\t\t\t</a>\r\n"); 99 } 100 WriteLiteral("\t\t\t\t</div>\r\n\t\t\t</div>\r\n\t\t</div>\r\n"); 101 102 } 103 else { 104 WriteLiteral("\t\t<div class=\"container\">\r\n\t\t\t<div class=\"row align-items-center\">\r\n\t\t\t<div class=\"col-6\">\r\n\t\t\t\t\t<div class=\"text-sm medium mb-1\">"); 105 Write(Translate("Custom.ProductActions.Prices", "Prices starting from")); 106 WriteLiteral("</div>\r\n\t\t\t\t\t<div class=\"mb-1\">\r\n"); 107 if (!showPricesWithVat && !neverShowVat) 108 { 109 string price = !string.IsNullOrEmpty(unitId) ? product.GetPrice(unitId).Price.PriceWithoutVatFormatted : product.Price.PriceWithoutVatFormatted; 110 111 if (product?.VariantInfo?.VariantInfo != null) 112 { 113 price = product?.VariantInfo?.PriceMin?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithoutVatFormatted : price; 114 } 115 116 WriteLiteral("\t\t\t\t\t\t\t<span class=\"display-sm bold\">"); 117 Write(price); 118 WriteLiteral("</span>\r\n\t\t\t\t\t\t\t<span class=\"text-sm medium opacity-75\">"); 119 Write(Translate("Custom.ProductActions.Prices.WithoutVat", "ex. VAT")); 120 WriteLiteral("</span>\r\n"); 121 } 122 else 123 { 124 string price = !string.IsNullOrEmpty(unitId) ? product.GetPrice(unitId).Price.PriceFormatted : product.Price.PriceFormatted; 125 126 if (product?.VariantInfo?.VariantInfo != null) 127 { 128 price = product?.VariantInfo?.PriceMin?.PriceFormatted != null ? product.VariantInfo.PriceMin.PriceFormatted : price; 129 } 130 131 132 WriteLiteral("\t\t\t\t\t\t\t<span class=\"display-sm bold\">"); 133 Write(price); 134 WriteLiteral("</span>\r\n\t\t\t\t\t\t\t<span class=\"text-sm medium opacity-75\">"); 135 Write(Translate("Custom.ProductActions.Prices.WithVat", "incl. VAT")); 136 WriteLiteral("</span>\r\n"); 137 } 138 WriteLiteral("\t\t\t\t\t</div>\r\n\t\t\t\t\t<div class=\"fs-7 d-inline-flex align-items-center\">\r\n\t\t\t\t\t\t<div class=\"rounded-circle green me-1\">\r\n\t\t\t\t\t\t</div>\r\n <div class=\"text-sm medium opacity-75\">"); 139 Write(Translate("Custom.ProductActions.Stocklabel", "In stock")); 140 WriteLiteral("</div>\r\n\t\t\t\t\t</div>\r\n\t\t\t</div>\r\n\t\t\t<div class=\"col-6\">\r\n\t\t\t\t\t<a"); 141 BeginWriteAttribute("href", " href=\"", 4781, "\"", 4815, 1); 142 WriteAttributeValue("", 4788, productFamilyParagraphLink, 4788, 27, false); 143 EndWriteAttribute(); 144 WriteLiteral(" class=\"btn btn-primary w-100\" data-dw-button=\"primary\">\r\n\t\t\t\t\t\t<span>"); 145 Write(Translate("Custom.ProductActions.SignedIn.ButtonText", "Buy products")); 146 WriteLiteral("<span class=\"ps-2\">"); 147 Write(ReadFile(iconPath + "shopping-cart.svg")); 148 WriteLiteral("</span></span>\r\n\t\t\t\t\t</a>\r\n\t\t\t</div>\r\n\t\t\t</div>\r\n\t\t</div>\r\n"); 149 } 150 151 } 152 else if (Pageview.IsVisualEditorMode) 153 { 154 WriteLiteral("\t<div class=\"alert alert-dark m-0\" role=\"alert\">\r\n\t\t<span>"); 155 Write(Translate("The product actions will be shown here, if available")); 156 WriteLiteral("</span>\r\n\t</div>\r\n"); 157 } 158 } 159 #pragma warning restore 1998 160 } 161 } 162 #pragma warning restore 1591 163
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 12 var iconPath = "Files/Images/Icons/Custom/"; 13 14 var productFamilyParagraphLink = Pageview.SearchFriendlyUrl + "#" + Model.Item.GetLink("ProductFamilyParagraph"); 15 var signInPageLink = Model.Item.GetLink("SignInPage"); 16 bool hasSignInPageLink = !string.IsNullOrWhiteSpace(signInPageLink); 17 18 bool showPricesWithVat = Dynamicweb.Ecommerce.Common.Context.DisplayPricesWithVat; 19 bool neverShowVat = string.IsNullOrEmpty(showPricesWithVat.ToString()); 20 string unitId = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("UnitId")) ? Dynamicweb.Context.Current.Request.Form.Get("UnitId") : string.Empty; 21 } 22 23 @if (product is object) 24 { 25 if (Pageview.User == null) { 26 27 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", ""); 28 bool hideAddToCart = anonymousUsersLimitations.Contains("cart"); 29 hideAddToCart = Pageview.IsVisualEditorMode ? false : hideAddToCart; 30 31 <div class="container"> 32 <div class="row"> 33 @if (!hideAddToCart) 34 { 35 <div class="col-4 border-end ps-0 d-flex flex-column justify-content-between"> 36 <div class="text-lg semi-bold mb-2">@Translate("Custom.ProductActions.Try", "Try before you decide")</div> 37 <a href="@productFamilyParagraphLink" class="btn btn-primary w-100" data-dw-button="primary"> 38 <span>@Translate("Custom.ProductActions.Try.ButtonText", "Request sample")<span class="ps-2">@ReadFile(iconPath + "arrow-down.svg")</span></span> 39 </a> 40 </div> 41 } 42 <div class="col-@(hideAddToCart ? "6" : "4") border-end d-flex flex-column justify-content-between"> 43 <div class="text-lg semi-bold mb-2">@Translate("Custom.ProductActions.Variants", "Variants")</div> 44 <a href="@productFamilyParagraphLink" class="btn btn-outline-secondary w-100" data-dw-button="outline-secondary"> 45 <span>@Translate("Custom.ProductActions.Variants.ButtonText", "View products")<span class="ps-2">@ReadFile(iconPath + "arrow-down.svg")</span></span> 46 </a> 47 </div> 48 <div class="col-@(hideAddToCart ? "6" : "4") d-flex flex-column justify-content-between"> 49 <div class="text-lg semi-bold d-inline-flex align-items-center mb-2"> 50 <div class="rounded-circle green me-1"> 51 </div> 52 @Translate("Custom.ProductActions.Stocklabel", "In stock") 53 </div> 54 @if (!hideAddToCart && hasSignInPageLink) 55 { 56 <a href="@signInPageLink" class="btn btn-outline-secondary w-100" data-dw-button="outline-secondary"> 57 <span>@Translate("Custom.ProductActions.SignIn.ButtonText", "Sign in to buy")<span class="ps-2">@ReadFile(iconPath + "shopping-cart.svg")</span></span> 58 </a> 59 } 60 </div> 61 </div> 62 </div> 63 64 } 65 else { 66 <div class="container"> 67 <div class="row align-items-center"> 68 <div class="col-6"> 69 <div class="text-sm medium mb-1">@Translate("Custom.ProductActions.Prices", "Prices starting from")</div> 70 <div class="mb-1"> 71 @if (!showPricesWithVat && !neverShowVat) 72 { 73 string price = !string.IsNullOrEmpty(unitId) ? product.GetPrice(unitId).Price.PriceWithoutVatFormatted : product.Price.PriceWithoutVatFormatted; 74 75 if (product?.VariantInfo?.VariantInfo != null) 76 { 77 price = product?.VariantInfo?.PriceMin?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithoutVatFormatted : price; 78 } 79 80 <span class="display-sm bold">@price</span> 81 <span class="text-sm medium opacity-75">@Translate("Custom.ProductActions.Prices.WithoutVat", "ex. VAT")</span> 82 } 83 else 84 { 85 string price = !string.IsNullOrEmpty(unitId) ? product.GetPrice(unitId).Price.PriceFormatted : product.Price.PriceFormatted; 86 87 if (product?.VariantInfo?.VariantInfo != null) 88 { 89 price = product?.VariantInfo?.PriceMin?.PriceFormatted != null ? product.VariantInfo.PriceMin.PriceFormatted : price; 90 } 91 92 93 <span class="display-sm bold">@price</span> 94 <span class="text-sm medium opacity-75">@Translate("Custom.ProductActions.Prices.WithVat", "incl. VAT")</span> 95 } 96 </div> 97 <div class="fs-7 d-inline-flex align-items-center"> 98 <div class="rounded-circle green me-1"> 99 </div> 100 <div class="text-sm medium opacity-75">@Translate("Custom.ProductActions.Stocklabel", "In stock")</div> 101 </div> 102 </div> 103 <div class="col-6"> 104 <a href="@productFamilyParagraphLink" class="btn btn-primary w-100" data-dw-button="primary"> 105 <span>@Translate("Custom.ProductActions.SignedIn.ButtonText", "Buy products")<span class="ps-2">@ReadFile(iconPath + "shopping-cart.svg")</span></span> 106 </a> 107 </div> 108 </div> 109 </div> 110 } 111 112 } 113 else if (Pageview.IsVisualEditorMode) 114 { 115 <div class="alert alert-dark m-0" role="alert"> 116 <span>@Translate("The product actions will be shown here, if available")</span> 117 </div> 118 } 119
|
|
CO2
|
A
mm
|
C
mm
|
D
mm
|
|---|
| GV0069R-AV10A | Natural | Recycled LDPE | 0 | 6.85 | 22.6 | 8.5 |
|
| GV0080A-AA20A | Yellow | Recycled LDPE | 0 | 0 | 21.8 | 0 |
|
| GV0080A-EA20A | Yellow | HDPE | 0 | 0 | 21.8 | 0 |
|
| GV0080C-AA20AX1 | Yellow | Recycled LDPE | 0 | 7.8 | 24.5 | 8.5 |
|
| GV0080R-EA20A | Yellow | HDPE | 0 | 7.2 | 21.6 | 8 |
|
| GV0132R-AE20A | Yellow | EVA | 0 | 13.35 | 14 | 15.2 |
|
Learn how our full service can add value to your business
Your full-service protection solution partner