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_43124e0f311644d7a982063c2e89cb9a.Execute() in D:\dynamicweb.net\Solutions\Dynamicweb\bomedys.staging.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 item_@Model.Item.SystemName.ToLower()"> 132 <div id="SmallScreenImages_@Model.ID" 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="SmallScreenImagesThumbnails_@Model.ID" 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="#SmallScreenImages_@Model.ID" 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="modal_@Model.ID" tabindex="-1" aria-labelledby="productDetailsGalleryModalTitle_@Model.ID" 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="productDetailsGalleryModalTitle_@Model.ID">@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="ModalCarousel_@Model.ID" 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="#ModalCarousel_@Model.ID" 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="#ModalCarousel_@Model.ID" 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="#modal_@Model.ID"> 345 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" 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="#modal_@Model.ID"> 371 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" 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('#modal_@Model.ID') 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_ProductBadges.cshtml" System.NullReferenceException: Object reference not set to an instance of an object. at CompiledRazorTemplates.Dynamic.RazorEngine_5f38001159d14c638d469cc6d23aee1e.Execute() in D:\dynamicweb.net\Solutions\Dynamicweb\bomedys.staging.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_ProductBadges.cshtml:line 72 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.Frontend 3 @using Dynamicweb.Content 4 @using Dynamicweb.Ecommerce.ProductCatalog 5 6 @{ 7 ProductViewModel product = null; 8 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 9 { 10 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 11 } 12 else if (Pageview.Item["DummyProduct"] != null) 13 { 14 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 15 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 16 17 if (productList?.Products is object) 18 { 19 product = productList.Products[0]; 20 } 21 } 22 } 23 24 @if (product is object) { 25 var badgeParms = new Dictionary<string, object>(); 26 badgeParms.Add("size", "h7"); 27 badgeParms.Add("saleBadgeType", Model.Item.GetRawValue("SaleBadgeType")); 28 badgeParms.Add("saleBadgeCssClassName", Model.Item.GetRawValue("SaleBadgeDesign")); 29 badgeParms.Add("newBadgeCssClassName", Model.Item.GetRawValue("NewBadgeDesign")); 30 badgeParms.Add("newPublicationDays", Model.Item.GetInt32("NewPublicationDays")); 31 badgeParms.Add("campaignBadgesValues", Model.Item.GetRawValueString("CampaignBadges")); 32 33 string badgeSize = Model.Item.GetRawValueString("BadgeSize", "fs-2"); 34 string horizontalAlign = Model.Item.GetRawValueString("HorizontalAlignment", ""); 35 horizontalAlign = horizontalAlign == "center" ? "text-center" : horizontalAlign; 36 horizontalAlign = horizontalAlign == "end" ? "text-end" : horizontalAlign; 37 38 Dictionary<string, ParagraphInfoViewModel> badgeConfigurations; 39 List<string> campaignBadgesValues = Model.Item.GetRawValueString("CampaignBadges") != null ? Model.Item.GetRawValueString("CampaignBadges").Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList() : new List<string>(); 40 41 if (Dynamicweb.Context.Current.Items.Contains("badgeConfigurations")) 42 { 43 badgeConfigurations = (Dictionary<string, ParagraphInfoViewModel>)Dynamicweb.Context.Current.Items["badgeConfigurations"]; 44 } 45 else 46 { 47 var badgesPage = Pageview.AreaSettings.GetLink("EcommerceBadgesPage") != null ? Pageview.AreaSettings.GetLink("EcommerceBadgesPage").PageId : 0; 48 var allBadges = badgesPage != 0 ? Dynamicweb.Content.Services.Paragraphs.GetParagraphsByPageId(badgesPage) : null; 49 50 badgeConfigurations = new Dictionary<string, ParagraphInfoViewModel>(); 51 foreach (Paragraph badge in allBadges) 52 { 53 var paragraphviewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreateParagraphInfoViewModel(badge); 54 string cssClassName = paragraphviewModel.Item.GetString("CssClassName").Trim().ToLower(); 55 if (!badgeConfigurations.ContainsKey(cssClassName)) 56 { 57 badgeConfigurations.Add(cssClassName, paragraphviewModel); 58 } 59 } 60 Dynamicweb.Context.Current.Items.Add("badgeConfigurations", badgeConfigurations); 61 } 62 63 int badgesCount = 0; 64 if (badgeConfigurations.Any()) 65 { 66 foreach (string campaign in campaignBadgesValues) 67 { 68 if (!string.IsNullOrEmpty(campaign)) 69 { 70 71 FieldValueViewModel availableCampaignsObject; 72 product.ProductFields.TryGetValue("Campaign", out availableCampaignsObject); 73 74 if (availableCampaignsObject != null) 75 { 76 string campaignType = string.Empty; 77 78 if (badgeConfigurations.ContainsKey(campaign)) 79 { 80 ParagraphInfoViewModel paragraphviewModel; 81 if (badgeConfigurations.TryGetValue(campaign, out paragraphviewModel)) 82 { 83 campaignType = paragraphviewModel.Item.GetRawValueString("CampaignType"); 84 } 85 } 86 87 List<FieldOptionValueViewModel> availableCampaigns = (List<FieldOptionValueViewModel>)availableCampaignsObject.Value; 88 89 foreach (FieldOptionValueViewModel availableOption in availableCampaigns) 90 { 91 if (campaignType == availableOption.Value) 92 { 93 badgesCount++; 94 break; 95 } 96 } 97 } 98 } 99 } 100 } 101 102 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("SaleBadgeDesign")) && Model.Item.GetRawValueString("SaleBadgeDesign") != "none" ? true : false; 103 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("NewBadgeDesign")) && Model.Item.GetRawValueString("NewBadgeDesign") != "none" ? true : false; 104 DateTime createdDate = product.Created.Value; 105 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false; 106 showBadges = (newBadgeEnabled && Model.Item.GetInt32("NewPublicationDays") == 0) || (newBadgeEnabled && (createdDate.AddDays(Model.Item.GetInt32("NewPublicationDays")) > DateTime.Now)) ? true : showBadges; 107 showBadges = (!string.IsNullOrEmpty(Model.Item.GetRawValueString("CampaignBadges")) && badgesCount != 0) ? true : showBadges; 108 109 if (showBadges) 110 { 111 <div class="@badgeSize @horizontalAlign item_@Model.Item.SystemName.ToLower()"> 112 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms) 113 </div> 114 } 115 else if (Pageview.IsVisualEditorMode) 116 { 117 <span class="badge bg-success text-light rounded-0">@Translate("Badge example")</span> 118 } 119 } 120 else if (Pageview.IsVisualEditorMode) 121 { 122 <span class="badge bg-success text-light rounded-0">@Translate("Badge example")</span> 123 } 124 125
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_2d1cdc5df7f7414abd022bf94199e323.Execute() in D:\dynamicweb.net\Solutions\Dynamicweb\bomedys.staging.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) item_@Model.Item.SystemName.ToLower()"> 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="Specifications_@Model.ID"> 111 @foreach (var group in displayGroups) 112 { 113 <div class="accordion-item"> 114 <h2 class="accordion-header" id="SpecificationHeading_@group.Id"> 115 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#SpecificationItem_@group.Id" aria-expanded="false" aria-controls="SpecificationItem_@group.Id"> 116 @group.Name 117 </button> 118 </h2> 119 <div id="SpecificationItem_@group.Id" class="accordion-collapse collapse" aria-labelledby="SpecificationHeading_@group.Id" data-bs-parent="#Specifications_@Model.ID"> 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="Specifications_@Model.ID"> 143 <div class="accordion-item"> 144 <h2 class="accordion-header" id="SpecificationHeading_@Model.ID"> 145 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#SpecificationItem_@Model.ID" aria-expanded="false" aria-controls="SpecificationItem_@Model.ID"> 146 @Translate("Specifications") 147 </button> 148 </h2> 149 <div id="SpecificationItem_@Model.ID" class="accordion-collapse" aria-labelledby="SpecificationHeading_@Model.ID" data-bs-parent="#Specifications_@Model.ID"> 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="Specifications_@Model.ID"> 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="SpecificationHeading_@group.Value.Id"> 189 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#SpecificationItem_@group.Value.Id" aria-expanded="false" aria-controls="SpecificationItem_@group.Value.Id"> 190 @group.Value.Name 191 </button> 192 </h2> 193 <div id="SpecificationItem_@group.Value.Id" class="accordion-collapse" aria-labelledby="SpecificationHeading_@group.Value.Id" data-bs-parent="#Specifications_@Model.ID"> 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_2d1cdc5df7f7414abd022bf94199e323.Execute() in D:\dynamicweb.net\Solutions\Dynamicweb\bomedys.staging.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) item_@Model.Item.SystemName.ToLower()"> 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="Specifications_@Model.ID"> 111 @foreach (var group in displayGroups) 112 { 113 <div class="accordion-item"> 114 <h2 class="accordion-header" id="SpecificationHeading_@group.Id"> 115 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#SpecificationItem_@group.Id" aria-expanded="false" aria-controls="SpecificationItem_@group.Id"> 116 @group.Name 117 </button> 118 </h2> 119 <div id="SpecificationItem_@group.Id" class="accordion-collapse collapse" aria-labelledby="SpecificationHeading_@group.Id" data-bs-parent="#Specifications_@Model.ID"> 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="Specifications_@Model.ID"> 143 <div class="accordion-item"> 144 <h2 class="accordion-header" id="SpecificationHeading_@Model.ID"> 145 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#SpecificationItem_@Model.ID" aria-expanded="false" aria-controls="SpecificationItem_@Model.ID"> 146 @Translate("Specifications") 147 </button> 148 </h2> 149 <div id="SpecificationItem_@Model.ID" class="accordion-collapse" aria-labelledby="SpecificationHeading_@Model.ID" data-bs-parent="#Specifications_@Model.ID"> 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="Specifications_@Model.ID"> 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="SpecificationHeading_@group.Value.Id"> 189 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#SpecificationItem_@group.Value.Id" aria-expanded="false" aria-controls="SpecificationItem_@group.Value.Id"> 190 @group.Value.Name 191 </button> 192 </h2> 193 <div id="SpecificationItem_@group.Value.Id" class="accordion-collapse" aria-labelledby="SpecificationHeading_@group.Value.Id" data-bs-parent="#Specifications_@Model.ID"> 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_ProductDetailsMediaTable.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_e2afa107fd814b61adb7f711fa214d86.Execute() in D:\dynamicweb.net\Solutions\Dynamicweb\bomedys.staging.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_ProductDetailsMediaTable.cshtml:line 58 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[] supportedImageFormats { get; set; } 9 public string[] supportedVideoFormats { get; set; } 10 public string[] supportedDocumentFormats { get; set; } 11 public string[] allSupportedFormats { get; set; } 12 13 public class RatioSettings 14 { 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() 22 { 23 var ratioSettings = new RatioSettings(); 24 25 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 26 ratio = ratio != "0" ? ratio : ""; 27 string cssClass = ratio != "" && ratio != "fill" ? " ratio" : ""; 28 string cssVariable = ratio != "" && ratio != "fill" ? "--bs-aspect-ratio: " + ratio : ""; 29 30 ratioSettings.Ratio = ratio; 31 ratioSettings.CssClass = cssClass; 32 ratioSettings.CssVariable = cssVariable; 33 ratioSettings.Fill = ratio == "fill" ? " h-100" : ""; 34 35 return ratioSettings; 36 } 37 } 38 39 @{ 40 @* Get the product data *@ 41 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 42 { 43 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 44 } 45 46 @* Supported formats *@ 47 supportedImageFormats = new string[] { ".jpg", ".jpeg", ".webp", ".png", ".gif", ".bmp", ".tiff" }; 48 supportedVideoFormats = new string[] { "youtu.be", "youtube", "vimeo", ".mp4", ".webm" }; 49 supportedDocumentFormats = new string[] { ".pdf", ".docx", ".xlsx", ".ppt", "pptx" }; 50 allSupportedFormats = supportedImageFormats.Concat(supportedVideoFormats).Concat(supportedDocumentFormats).ToArray(); 51 52 @* Collect the assets *@ 53 var selectedAssetCategories = Model.Item.GetRawValueString("ImageAssets").Split(',').ToList(); 54 bool includeImagePatternImages = Model.Item.GetBoolean("ImagePatternImages"); 55 56 @* Needed image data collection to support both DefaultImage, ImagePatterns and Image Assets *@ 57 string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : ""; 58 IEnumerable<MediaViewModel> assetsImages = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets); 59 assetsImages = assetsImages.OrderByDescending(x => x.Value.Equals(defaultImage)); 60 IEnumerable<MediaViewModel> assetsList = new MediaViewModel[]{}; 61 62 assetsList = assetsList.Union(assetsImages); 63 assetsList = includeImagePatternImages ? assetsList.Union(product.ImagePatternImages) : assetsList; 64 assetsList = includeImagePatternImages && assetsList.Count() == 0 ? assetsList.Append(product.DefaultImage) : assetsList; 65 66 bool defaultImageFallback = Model.Item.GetBoolean("DefaultImageFallback"); 67 bool showOnlyPrimaryImage = Model.Item.GetBoolean("ShowOnlyPrimaryImage"); 68 69 int totalAssets = 0; 70 if (showOnlyPrimaryImage == false) { 71 foreach (MediaViewModel asset in assetsList) { 72 var assetValue = asset.Value.ToLower(); 73 foreach (string format in allSupportedFormats) { 74 if (assetValue.Contains(format) ) { 75 totalAssets++; 76 } 77 } 78 } 79 } 80 81 if((totalAssets == 0 && product.DefaultImage != null && selectedAssetCategories.Count() == 0) || (showOnlyPrimaryImage == true && product.DefaultImage != null)) 82 { 83 assetsList = new List<MediaViewModel>(){ product.DefaultImage }; 84 totalAssets = 1; 85 } 86 87 int videoNumber = 0; 88 89 @* Layout settings *@ 90 string spacing = Model.Item.GetRawValueString("Spacing", "p-0"); 91 spacing = spacing == "none" ? "p-0" : spacing; 92 spacing = spacing == "small" ? "p-3" : spacing; 93 spacing = spacing == "large" ? "p-5" : spacing; 94 95 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 96 97 bool hideThumbnails = Model.Item.GetBoolean("HideThumbnails"); 98 99 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 100 } 101 102 @* Get assets from selected categories or get all assets *@ 103 104 105 <div class="@spacing@(theme) item_@Model.Item.SystemName.ToLower()"> 106 @if (totalAssets != 0 && assetsList.Count() != 0) 107 { 108 if (!string.IsNullOrEmpty(Model.Item.GetString("Title")) && !Model.Item.GetBoolean("HideTitle")) 109 { 110 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "h3"); 111 112 <h3 class="@titleFontSize mb-3"> 113 @Model.Item.GetString("Title") 114 </h3> 115 } 116 117 <div class="table-responsive"> 118 <table class="table table-hover align-middle mb-0" style="table-layout: fixed;"> 119 <thead> 120 <tr> 121 @if (!hideThumbnails) 122 { 123 <th style="width:60px"> </th> 124 } 125 <th>@Translate("Name")</th> 126 <th class="text-end d-none d-lg-table-cell">@Translate("Download")</th> 127 <th class="text-end" style="width:100px">@Translate("File type")</th> 128 </tr> 129 </thead> 130 <tbody class="border-top-0"> 131 @foreach (MediaViewModel asset in assetsList) 132 { 133 var assetValue = asset.Value.ToLower(); 134 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Value.Substring( asset.Value.LastIndexOf('/') + 1); 135 136 bool isVideo = false; 137 foreach (string format in supportedVideoFormats) 138 { //Videos 139 if (assetValue.Contains(format)) 140 { 141 isVideo = true; 142 } 143 } 144 145 if (!isVideo) 146 { 147 string filePath = Dynamicweb.Context.Current.Server.MapPath(assetValue); 148 long fileSize = 0; 149 150 if (File.Exists(filePath)) { 151 fileSize = new System.IO.FileInfo(filePath) != null ? new System.IO.FileInfo(filePath).Length / 1024 : 0; 152 153 foreach (string format in allSupportedFormats) 154 { 155 if (assetValue.Contains(format)) 156 { 157 <tr class="position-relative"> 158 @if (!hideThumbnails) 159 { 160 @RenderAsset(asset) 161 } 162 <td> 163 <a href="@assetValue" class="text-decoration-none text-break" download title="@assetName"> 164 @assetName 165 </a> 166 </td> 167 <td class="text-end d-none d-lg-table-cell"> 168 <a href="@assetValue" class="text-decoration-none stretched-link" download title="@assetName"> 169 @fileSize KB <div class="icon-2" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 170 </a> 171 </td> 172 <td class="text-end">@format</td> 173 </tr> 174 } 175 } 176 } 177 } 178 else 179 { 180 string videoType = asset.Value.Contains("youtu.be") || asset.Value.Contains("youtube") ? "Youtube" : ""; 181 videoType = asset.Value.Contains("vimeo") ? "Vimeo" : videoType; 182 183 <tr data-bs-toggle="modal" data-bs-target="#modal_@(Model.ID)_@videoNumber" style="cursor: pointer"> 184 @if (!hideThumbnails) 185 { 186 @RenderAsset(asset) 187 } 188 <td> 189 @assetName 190 </td> 191 <td class="d-none d-lg-table-cell"> </td> 192 <td align="right">@videoType</td> 193 </tr> 194 195 videoNumber++; 196 } 197 } 198 </tbody> 199 </table> 200 </div> 201 202 int modalVideoNumber = 0; 203 foreach (MediaViewModel asset in assetsList) 204 { 205 var assetName = asset.Value.ToLower(); 206 207 foreach (string format in supportedVideoFormats) 208 { //Videos 209 if (assetName.Contains(format)) 210 { 211 <div class="modal fade js-video-modal" id="modal_@(Model.ID)_@modalVideoNumber" tabindex="-1" aria-labelledby="productDetailsTableModalTitle_@(Model.ID)_@modalVideoNumber" aria-hidden="true"> 212 <div class="modal-dialog modal-dialog-centered modal-xl"> 213 <div class="modal-content"> 214 <div class="modal-header visually-hidden"> 215 <h5 class="modal-title" id="productDetailsTableModalTitle_@(Model.ID)_@modalVideoNumber">@product.Title</h5> 216 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 217 </div> 218 <div class="modal-body p-2 p-lg-3 h-100"> 219 @{ @RenderVideoPlayer(asset) } 220 </div> 221 </div> 222 </div> 223 </div> 224 225 modalVideoNumber++; 226 } 227 } 228 } 229 } 230 else if (Pageview.IsVisualEditorMode) 231 { 232 RatioSettings ratioSettings = GetRatioSettings(); 233 234 <div class="h-100 @theme"> 235 <div class="alert alert-dark m-0"> 236 @Translate("No assets are available") 237 </div> 238 </div> 239 } 240 241 </div> 242 243 @helper RenderAsset(MediaViewModel asset) 244 { 245 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 246 string assetValue = asset.Value; 247 248 <td class="@(theme) px-0"> 249 @foreach (string format in supportedImageFormats) 250 { //Images 251 if (assetValue.Contains(format)) 252 { 253 @RenderImage(asset) 254 } 255 } 256 @foreach (string format in supportedVideoFormats) 257 { //Videos 258 if (assetValue.Contains(format)) 259 { 260 @RenderVideoScreendump(asset) 261 } 262 } 263 @foreach (string format in supportedDocumentFormats) 264 { //Documents 265 if (assetValue.Contains(format)) 266 { 267 @RenderDocument(asset) 268 } 269 } 270 </td> 271 } 272 273 @helper RenderImage(MediaViewModel asset) 274 { 275 string productName = product.Name; 276 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 277 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 278 string imageLinkPath = imagePath; 279 imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; 280 281 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 282 283 RatioSettings ratioSettings = GetRatioSettings(); 284 285 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download> 286 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 287 <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle itemprop="image"> 288 </div> 289 </a> 290 } 291 292 @helper RenderVideoScreendump(MediaViewModel asset) 293 { 294 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 295 296 string videoScreendumpPath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : ""; 297 string videoId = videoScreendumpPath.Substring(videoScreendumpPath.LastIndexOf('/') + 1); 298 videoScreendumpPath = videoScreendumpPath.Contains("youtu.be") || videoScreendumpPath.Contains("youtube") ? "https://img.youtube.com/vi/" + videoId + "/mqdefault.jpg" : videoScreendumpPath; 299 300 string vimeoJsClass = videoScreendumpPath.Contains("vimeo") ? "js-vimeo-video-thumbnail" : ""; 301 videoScreendumpPath = videoScreendumpPath.Contains("vimeo") ? "" : videoScreendumpPath; 302 303 string productName = product.Name; 304 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 305 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 306 307 RatioSettings ratioSettings = GetRatioSettings(); 308 309 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> 310 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 311 <div class="icon-2 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 312 @if (!videoScreendumpPath.Contains(".mp4")) { 313 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@productName" @assetTitle class="@vimeoJsClass mw-100 mh-100" data-video-id="@videoId" style="object-fit: cover;"> 314 } else { 315 string videoType = Path.GetExtension(asset.Value).ToLower(); 316 317 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 318 <source src="@asset.Value" type="video/@videoType.Replace(".", "")"> 319 </video> 320 } 321 </div> 322 </div> 323 } 324 325 @helper RenderDocument(MediaViewModel asset) 326 { 327 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 328 string productName = product.Name; 329 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 330 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 331 string imageLinkPath = imagePath; 332 imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; 333 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 334 335 RatioSettings ratioSettings = GetRatioSettings(); 336 337 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download alt="@productName"> 338 @if (asset.Value.Contains(".pdf")) { 339 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 340 <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle /> 341 </div> 342 } else { 343 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 344 <div class="icon-3 position-absolute" style="z-index: 1">@ReadFile(iconPath + "file-text.svg")</div> 345 </div> 346 } 347 </a> 348 } 349 350 @helper RenderVideoPlayer(MediaViewModel asset) 351 { 352 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Name; 353 string assetValue = asset.Value; 354 string videoId = asset.Value.Substring(asset.Value.LastIndexOf('/') + 1); 355 string type = assetValue.Contains("youtu.be") || assetValue.Contains("youtube") ? "youtube" : ""; 356 type = assetValue.Contains("vimeo") ? "vimeo" : type; 357 type = assetValue.Contains(".mp4") || assetValue.Contains(".webm") ? "selfhosted" : type; 358 359 <div class="h-100" itemscope itemtype="https://schema.org/VideoObject"> 360 <span class="visually-hidden" itemprop="name">@assetName</span> 361 <span class="visually-hidden" itemprop="contentUrl">@asset.Value</span> 362 <span class="visually-hidden" itemprop="thumbnailUrl">@asset.Value</span> 363 @if (type != "selfhosted") 364 { 365 <div id="player_@(Pageview.CurrentParagraph.ID)_@(videoId)" 366 class="plyr__video-embed" 367 data-plyr-provider="@(type)" 368 data-plyr-embed-id="@videoId" 369 style="--plyr-color-main: var(--swift-foreground-color); height: 100%"> 370 </div> 371 372 <script type="module" src="~/Files/Templates/Designs/Swift/Assets/js/plyr.js"></script> 373 <script type="module"> 374 var player = new Plyr('#player_@(Pageview.CurrentParagraph.ID)_@(videoId)', { 375 type: 'video', 376 youtube: { 377 noCookie: true, 378 showinfo: 0 379 }, 380 fullscreen: { 381 enabled: true, 382 iosNative: true, 383 } 384 }); 385 386 document.querySelectorAll('.js-video-modal').forEach(function (modal) { 387 modal.addEventListener('hidden.bs.modal', function (event) { 388 player.media.pause(); 389 }) 390 }); 391 </script> 392 } 393 else 394 { 395 string videoType = Path.GetExtension(assetValue).ToLower(); 396 397 <video preload="auto" class="h-100 w-100" style="object-fit: cover;" controls> 398 <source src="@assetValue" type="video/@videoType.Replace(".", "")"> 399 </video> 400 } 401 </div> 402 } 403
Error executing template "Designs/Swift/Paragraph/Swift_ProductDetailsMediaTable.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_e2afa107fd814b61adb7f711fa214d86.Execute() in D:\dynamicweb.net\Solutions\Dynamicweb\bomedys.staging.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_ProductDetailsMediaTable.cshtml:line 58 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[] supportedImageFormats { get; set; } 9 public string[] supportedVideoFormats { get; set; } 10 public string[] supportedDocumentFormats { get; set; } 11 public string[] allSupportedFormats { get; set; } 12 13 public class RatioSettings 14 { 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() 22 { 23 var ratioSettings = new RatioSettings(); 24 25 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 26 ratio = ratio != "0" ? ratio : ""; 27 string cssClass = ratio != "" && ratio != "fill" ? " ratio" : ""; 28 string cssVariable = ratio != "" && ratio != "fill" ? "--bs-aspect-ratio: " + ratio : ""; 29 30 ratioSettings.Ratio = ratio; 31 ratioSettings.CssClass = cssClass; 32 ratioSettings.CssVariable = cssVariable; 33 ratioSettings.Fill = ratio == "fill" ? " h-100" : ""; 34 35 return ratioSettings; 36 } 37 } 38 39 @{ 40 @* Get the product data *@ 41 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 42 { 43 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 44 } 45 46 @* Supported formats *@ 47 supportedImageFormats = new string[] { ".jpg", ".jpeg", ".webp", ".png", ".gif", ".bmp", ".tiff" }; 48 supportedVideoFormats = new string[] { "youtu.be", "youtube", "vimeo", ".mp4", ".webm" }; 49 supportedDocumentFormats = new string[] { ".pdf", ".docx", ".xlsx", ".ppt", "pptx" }; 50 allSupportedFormats = supportedImageFormats.Concat(supportedVideoFormats).Concat(supportedDocumentFormats).ToArray(); 51 52 @* Collect the assets *@ 53 var selectedAssetCategories = Model.Item.GetRawValueString("ImageAssets").Split(',').ToList(); 54 bool includeImagePatternImages = Model.Item.GetBoolean("ImagePatternImages"); 55 56 @* Needed image data collection to support both DefaultImage, ImagePatterns and Image Assets *@ 57 string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : ""; 58 IEnumerable<MediaViewModel> assetsImages = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets); 59 assetsImages = assetsImages.OrderByDescending(x => x.Value.Equals(defaultImage)); 60 IEnumerable<MediaViewModel> assetsList = new MediaViewModel[]{}; 61 62 assetsList = assetsList.Union(assetsImages); 63 assetsList = includeImagePatternImages ? assetsList.Union(product.ImagePatternImages) : assetsList; 64 assetsList = includeImagePatternImages && assetsList.Count() == 0 ? assetsList.Append(product.DefaultImage) : assetsList; 65 66 bool defaultImageFallback = Model.Item.GetBoolean("DefaultImageFallback"); 67 bool showOnlyPrimaryImage = Model.Item.GetBoolean("ShowOnlyPrimaryImage"); 68 69 int totalAssets = 0; 70 if (showOnlyPrimaryImage == false) { 71 foreach (MediaViewModel asset in assetsList) { 72 var assetValue = asset.Value.ToLower(); 73 foreach (string format in allSupportedFormats) { 74 if (assetValue.Contains(format) ) { 75 totalAssets++; 76 } 77 } 78 } 79 } 80 81 if((totalAssets == 0 && product.DefaultImage != null && selectedAssetCategories.Count() == 0) || (showOnlyPrimaryImage == true && product.DefaultImage != null)) 82 { 83 assetsList = new List<MediaViewModel>(){ product.DefaultImage }; 84 totalAssets = 1; 85 } 86 87 int videoNumber = 0; 88 89 @* Layout settings *@ 90 string spacing = Model.Item.GetRawValueString("Spacing", "p-0"); 91 spacing = spacing == "none" ? "p-0" : spacing; 92 spacing = spacing == "small" ? "p-3" : spacing; 93 spacing = spacing == "large" ? "p-5" : spacing; 94 95 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 96 97 bool hideThumbnails = Model.Item.GetBoolean("HideThumbnails"); 98 99 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 100 } 101 102 @* Get assets from selected categories or get all assets *@ 103 104 105 <div class="@spacing@(theme) item_@Model.Item.SystemName.ToLower()"> 106 @if (totalAssets != 0 && assetsList.Count() != 0) 107 { 108 if (!string.IsNullOrEmpty(Model.Item.GetString("Title")) && !Model.Item.GetBoolean("HideTitle")) 109 { 110 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "h3"); 111 112 <h3 class="@titleFontSize mb-3"> 113 @Model.Item.GetString("Title") 114 </h3> 115 } 116 117 <div class="table-responsive"> 118 <table class="table table-hover align-middle mb-0" style="table-layout: fixed;"> 119 <thead> 120 <tr> 121 @if (!hideThumbnails) 122 { 123 <th style="width:60px"> </th> 124 } 125 <th>@Translate("Name")</th> 126 <th class="text-end d-none d-lg-table-cell">@Translate("Download")</th> 127 <th class="text-end" style="width:100px">@Translate("File type")</th> 128 </tr> 129 </thead> 130 <tbody class="border-top-0"> 131 @foreach (MediaViewModel asset in assetsList) 132 { 133 var assetValue = asset.Value.ToLower(); 134 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Value.Substring( asset.Value.LastIndexOf('/') + 1); 135 136 bool isVideo = false; 137 foreach (string format in supportedVideoFormats) 138 { //Videos 139 if (assetValue.Contains(format)) 140 { 141 isVideo = true; 142 } 143 } 144 145 if (!isVideo) 146 { 147 string filePath = Dynamicweb.Context.Current.Server.MapPath(assetValue); 148 long fileSize = 0; 149 150 if (File.Exists(filePath)) { 151 fileSize = new System.IO.FileInfo(filePath) != null ? new System.IO.FileInfo(filePath).Length / 1024 : 0; 152 153 foreach (string format in allSupportedFormats) 154 { 155 if (assetValue.Contains(format)) 156 { 157 <tr class="position-relative"> 158 @if (!hideThumbnails) 159 { 160 @RenderAsset(asset) 161 } 162 <td> 163 <a href="@assetValue" class="text-decoration-none text-break" download title="@assetName"> 164 @assetName 165 </a> 166 </td> 167 <td class="text-end d-none d-lg-table-cell"> 168 <a href="@assetValue" class="text-decoration-none stretched-link" download title="@assetName"> 169 @fileSize KB <div class="icon-2" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 170 </a> 171 </td> 172 <td class="text-end">@format</td> 173 </tr> 174 } 175 } 176 } 177 } 178 else 179 { 180 string videoType = asset.Value.Contains("youtu.be") || asset.Value.Contains("youtube") ? "Youtube" : ""; 181 videoType = asset.Value.Contains("vimeo") ? "Vimeo" : videoType; 182 183 <tr data-bs-toggle="modal" data-bs-target="#modal_@(Model.ID)_@videoNumber" style="cursor: pointer"> 184 @if (!hideThumbnails) 185 { 186 @RenderAsset(asset) 187 } 188 <td> 189 @assetName 190 </td> 191 <td class="d-none d-lg-table-cell"> </td> 192 <td align="right">@videoType</td> 193 </tr> 194 195 videoNumber++; 196 } 197 } 198 </tbody> 199 </table> 200 </div> 201 202 int modalVideoNumber = 0; 203 foreach (MediaViewModel asset in assetsList) 204 { 205 var assetName = asset.Value.ToLower(); 206 207 foreach (string format in supportedVideoFormats) 208 { //Videos 209 if (assetName.Contains(format)) 210 { 211 <div class="modal fade js-video-modal" id="modal_@(Model.ID)_@modalVideoNumber" tabindex="-1" aria-labelledby="productDetailsTableModalTitle_@(Model.ID)_@modalVideoNumber" aria-hidden="true"> 212 <div class="modal-dialog modal-dialog-centered modal-xl"> 213 <div class="modal-content"> 214 <div class="modal-header visually-hidden"> 215 <h5 class="modal-title" id="productDetailsTableModalTitle_@(Model.ID)_@modalVideoNumber">@product.Title</h5> 216 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 217 </div> 218 <div class="modal-body p-2 p-lg-3 h-100"> 219 @{ @RenderVideoPlayer(asset) } 220 </div> 221 </div> 222 </div> 223 </div> 224 225 modalVideoNumber++; 226 } 227 } 228 } 229 } 230 else if (Pageview.IsVisualEditorMode) 231 { 232 RatioSettings ratioSettings = GetRatioSettings(); 233 234 <div class="h-100 @theme"> 235 <div class="alert alert-dark m-0"> 236 @Translate("No assets are available") 237 </div> 238 </div> 239 } 240 241 </div> 242 243 @helper RenderAsset(MediaViewModel asset) 244 { 245 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 246 string assetValue = asset.Value; 247 248 <td class="@(theme) px-0"> 249 @foreach (string format in supportedImageFormats) 250 { //Images 251 if (assetValue.Contains(format)) 252 { 253 @RenderImage(asset) 254 } 255 } 256 @foreach (string format in supportedVideoFormats) 257 { //Videos 258 if (assetValue.Contains(format)) 259 { 260 @RenderVideoScreendump(asset) 261 } 262 } 263 @foreach (string format in supportedDocumentFormats) 264 { //Documents 265 if (assetValue.Contains(format)) 266 { 267 @RenderDocument(asset) 268 } 269 } 270 </td> 271 } 272 273 @helper RenderImage(MediaViewModel asset) 274 { 275 string productName = product.Name; 276 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 277 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 278 string imageLinkPath = imagePath; 279 imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; 280 281 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 282 283 RatioSettings ratioSettings = GetRatioSettings(); 284 285 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download> 286 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 287 <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle itemprop="image"> 288 </div> 289 </a> 290 } 291 292 @helper RenderVideoScreendump(MediaViewModel asset) 293 { 294 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 295 296 string videoScreendumpPath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : ""; 297 string videoId = videoScreendumpPath.Substring(videoScreendumpPath.LastIndexOf('/') + 1); 298 videoScreendumpPath = videoScreendumpPath.Contains("youtu.be") || videoScreendumpPath.Contains("youtube") ? "https://img.youtube.com/vi/" + videoId + "/mqdefault.jpg" : videoScreendumpPath; 299 300 string vimeoJsClass = videoScreendumpPath.Contains("vimeo") ? "js-vimeo-video-thumbnail" : ""; 301 videoScreendumpPath = videoScreendumpPath.Contains("vimeo") ? "" : videoScreendumpPath; 302 303 string productName = product.Name; 304 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 305 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 306 307 RatioSettings ratioSettings = GetRatioSettings(); 308 309 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> 310 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 311 <div class="icon-2 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 312 @if (!videoScreendumpPath.Contains(".mp4")) { 313 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@productName" @assetTitle class="@vimeoJsClass mw-100 mh-100" data-video-id="@videoId" style="object-fit: cover;"> 314 } else { 315 string videoType = Path.GetExtension(asset.Value).ToLower(); 316 317 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 318 <source src="@asset.Value" type="video/@videoType.Replace(".", "")"> 319 </video> 320 } 321 </div> 322 </div> 323 } 324 325 @helper RenderDocument(MediaViewModel asset) 326 { 327 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 328 string productName = product.Name; 329 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 330 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 331 string imageLinkPath = imagePath; 332 imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; 333 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 334 335 RatioSettings ratioSettings = GetRatioSettings(); 336 337 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download alt="@productName"> 338 @if (asset.Value.Contains(".pdf")) { 339 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 340 <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle /> 341 </div> 342 } else { 343 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 344 <div class="icon-3 position-absolute" style="z-index: 1">@ReadFile(iconPath + "file-text.svg")</div> 345 </div> 346 } 347 </a> 348 } 349 350 @helper RenderVideoPlayer(MediaViewModel asset) 351 { 352 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Name; 353 string assetValue = asset.Value; 354 string videoId = asset.Value.Substring(asset.Value.LastIndexOf('/') + 1); 355 string type = assetValue.Contains("youtu.be") || assetValue.Contains("youtube") ? "youtube" : ""; 356 type = assetValue.Contains("vimeo") ? "vimeo" : type; 357 type = assetValue.Contains(".mp4") || assetValue.Contains(".webm") ? "selfhosted" : type; 358 359 <div class="h-100" itemscope itemtype="https://schema.org/VideoObject"> 360 <span class="visually-hidden" itemprop="name">@assetName</span> 361 <span class="visually-hidden" itemprop="contentUrl">@asset.Value</span> 362 <span class="visually-hidden" itemprop="thumbnailUrl">@asset.Value</span> 363 @if (type != "selfhosted") 364 { 365 <div id="player_@(Pageview.CurrentParagraph.ID)_@(videoId)" 366 class="plyr__video-embed" 367 data-plyr-provider="@(type)" 368 data-plyr-embed-id="@videoId" 369 style="--plyr-color-main: var(--swift-foreground-color); height: 100%"> 370 </div> 371 372 <script type="module" src="~/Files/Templates/Designs/Swift/Assets/js/plyr.js"></script> 373 <script type="module"> 374 var player = new Plyr('#player_@(Pageview.CurrentParagraph.ID)_@(videoId)', { 375 type: 'video', 376 youtube: { 377 noCookie: true, 378 showinfo: 0 379 }, 380 fullscreen: { 381 enabled: true, 382 iosNative: true, 383 } 384 }); 385 386 document.querySelectorAll('.js-video-modal').forEach(function (modal) { 387 modal.addEventListener('hidden.bs.modal', function (event) { 388 player.media.pause(); 389 }) 390 }); 391 </script> 392 } 393 else 394 { 395 string videoType = Path.GetExtension(assetValue).ToLower(); 396 397 <video preload="auto" class="h-100 w-100" style="object-fit: cover;" controls> 398 <source src="@assetValue" type="video/@videoType.Replace(".", "")"> 399 </video> 400 } 401 </div> 402 } 403
Error executing template "Designs/Swift/Paragraph/Swift_ProductDetailsMediaTable.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_e2afa107fd814b61adb7f711fa214d86.Execute() in D:\dynamicweb.net\Solutions\Dynamicweb\bomedys.staging.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_ProductDetailsMediaTable.cshtml:line 58 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[] supportedImageFormats { get; set; } 9 public string[] supportedVideoFormats { get; set; } 10 public string[] supportedDocumentFormats { get; set; } 11 public string[] allSupportedFormats { get; set; } 12 13 public class RatioSettings 14 { 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() 22 { 23 var ratioSettings = new RatioSettings(); 24 25 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 26 ratio = ratio != "0" ? ratio : ""; 27 string cssClass = ratio != "" && ratio != "fill" ? " ratio" : ""; 28 string cssVariable = ratio != "" && ratio != "fill" ? "--bs-aspect-ratio: " + ratio : ""; 29 30 ratioSettings.Ratio = ratio; 31 ratioSettings.CssClass = cssClass; 32 ratioSettings.CssVariable = cssVariable; 33 ratioSettings.Fill = ratio == "fill" ? " h-100" : ""; 34 35 return ratioSettings; 36 } 37 } 38 39 @{ 40 @* Get the product data *@ 41 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 42 { 43 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 44 } 45 46 @* Supported formats *@ 47 supportedImageFormats = new string[] { ".jpg", ".jpeg", ".webp", ".png", ".gif", ".bmp", ".tiff" }; 48 supportedVideoFormats = new string[] { "youtu.be", "youtube", "vimeo", ".mp4", ".webm" }; 49 supportedDocumentFormats = new string[] { ".pdf", ".docx", ".xlsx", ".ppt", "pptx" }; 50 allSupportedFormats = supportedImageFormats.Concat(supportedVideoFormats).Concat(supportedDocumentFormats).ToArray(); 51 52 @* Collect the assets *@ 53 var selectedAssetCategories = Model.Item.GetRawValueString("ImageAssets").Split(',').ToList(); 54 bool includeImagePatternImages = Model.Item.GetBoolean("ImagePatternImages"); 55 56 @* Needed image data collection to support both DefaultImage, ImagePatterns and Image Assets *@ 57 string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : ""; 58 IEnumerable<MediaViewModel> assetsImages = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets); 59 assetsImages = assetsImages.OrderByDescending(x => x.Value.Equals(defaultImage)); 60 IEnumerable<MediaViewModel> assetsList = new MediaViewModel[]{}; 61 62 assetsList = assetsList.Union(assetsImages); 63 assetsList = includeImagePatternImages ? assetsList.Union(product.ImagePatternImages) : assetsList; 64 assetsList = includeImagePatternImages && assetsList.Count() == 0 ? assetsList.Append(product.DefaultImage) : assetsList; 65 66 bool defaultImageFallback = Model.Item.GetBoolean("DefaultImageFallback"); 67 bool showOnlyPrimaryImage = Model.Item.GetBoolean("ShowOnlyPrimaryImage"); 68 69 int totalAssets = 0; 70 if (showOnlyPrimaryImage == false) { 71 foreach (MediaViewModel asset in assetsList) { 72 var assetValue = asset.Value.ToLower(); 73 foreach (string format in allSupportedFormats) { 74 if (assetValue.Contains(format) ) { 75 totalAssets++; 76 } 77 } 78 } 79 } 80 81 if((totalAssets == 0 && product.DefaultImage != null && selectedAssetCategories.Count() == 0) || (showOnlyPrimaryImage == true && product.DefaultImage != null)) 82 { 83 assetsList = new List<MediaViewModel>(){ product.DefaultImage }; 84 totalAssets = 1; 85 } 86 87 int videoNumber = 0; 88 89 @* Layout settings *@ 90 string spacing = Model.Item.GetRawValueString("Spacing", "p-0"); 91 spacing = spacing == "none" ? "p-0" : spacing; 92 spacing = spacing == "small" ? "p-3" : spacing; 93 spacing = spacing == "large" ? "p-5" : spacing; 94 95 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 96 97 bool hideThumbnails = Model.Item.GetBoolean("HideThumbnails"); 98 99 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 100 } 101 102 @* Get assets from selected categories or get all assets *@ 103 104 105 <div class="@spacing@(theme) item_@Model.Item.SystemName.ToLower()"> 106 @if (totalAssets != 0 && assetsList.Count() != 0) 107 { 108 if (!string.IsNullOrEmpty(Model.Item.GetString("Title")) && !Model.Item.GetBoolean("HideTitle")) 109 { 110 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "h3"); 111 112 <h3 class="@titleFontSize mb-3"> 113 @Model.Item.GetString("Title") 114 </h3> 115 } 116 117 <div class="table-responsive"> 118 <table class="table table-hover align-middle mb-0" style="table-layout: fixed;"> 119 <thead> 120 <tr> 121 @if (!hideThumbnails) 122 { 123 <th style="width:60px"> </th> 124 } 125 <th>@Translate("Name")</th> 126 <th class="text-end d-none d-lg-table-cell">@Translate("Download")</th> 127 <th class="text-end" style="width:100px">@Translate("File type")</th> 128 </tr> 129 </thead> 130 <tbody class="border-top-0"> 131 @foreach (MediaViewModel asset in assetsList) 132 { 133 var assetValue = asset.Value.ToLower(); 134 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Value.Substring( asset.Value.LastIndexOf('/') + 1); 135 136 bool isVideo = false; 137 foreach (string format in supportedVideoFormats) 138 { //Videos 139 if (assetValue.Contains(format)) 140 { 141 isVideo = true; 142 } 143 } 144 145 if (!isVideo) 146 { 147 string filePath = Dynamicweb.Context.Current.Server.MapPath(assetValue); 148 long fileSize = 0; 149 150 if (File.Exists(filePath)) { 151 fileSize = new System.IO.FileInfo(filePath) != null ? new System.IO.FileInfo(filePath).Length / 1024 : 0; 152 153 foreach (string format in allSupportedFormats) 154 { 155 if (assetValue.Contains(format)) 156 { 157 <tr class="position-relative"> 158 @if (!hideThumbnails) 159 { 160 @RenderAsset(asset) 161 } 162 <td> 163 <a href="@assetValue" class="text-decoration-none text-break" download title="@assetName"> 164 @assetName 165 </a> 166 </td> 167 <td class="text-end d-none d-lg-table-cell"> 168 <a href="@assetValue" class="text-decoration-none stretched-link" download title="@assetName"> 169 @fileSize KB <div class="icon-2" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 170 </a> 171 </td> 172 <td class="text-end">@format</td> 173 </tr> 174 } 175 } 176 } 177 } 178 else 179 { 180 string videoType = asset.Value.Contains("youtu.be") || asset.Value.Contains("youtube") ? "Youtube" : ""; 181 videoType = asset.Value.Contains("vimeo") ? "Vimeo" : videoType; 182 183 <tr data-bs-toggle="modal" data-bs-target="#modal_@(Model.ID)_@videoNumber" style="cursor: pointer"> 184 @if (!hideThumbnails) 185 { 186 @RenderAsset(asset) 187 } 188 <td> 189 @assetName 190 </td> 191 <td class="d-none d-lg-table-cell"> </td> 192 <td align="right">@videoType</td> 193 </tr> 194 195 videoNumber++; 196 } 197 } 198 </tbody> 199 </table> 200 </div> 201 202 int modalVideoNumber = 0; 203 foreach (MediaViewModel asset in assetsList) 204 { 205 var assetName = asset.Value.ToLower(); 206 207 foreach (string format in supportedVideoFormats) 208 { //Videos 209 if (assetName.Contains(format)) 210 { 211 <div class="modal fade js-video-modal" id="modal_@(Model.ID)_@modalVideoNumber" tabindex="-1" aria-labelledby="productDetailsTableModalTitle_@(Model.ID)_@modalVideoNumber" aria-hidden="true"> 212 <div class="modal-dialog modal-dialog-centered modal-xl"> 213 <div class="modal-content"> 214 <div class="modal-header visually-hidden"> 215 <h5 class="modal-title" id="productDetailsTableModalTitle_@(Model.ID)_@modalVideoNumber">@product.Title</h5> 216 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 217 </div> 218 <div class="modal-body p-2 p-lg-3 h-100"> 219 @{ @RenderVideoPlayer(asset) } 220 </div> 221 </div> 222 </div> 223 </div> 224 225 modalVideoNumber++; 226 } 227 } 228 } 229 } 230 else if (Pageview.IsVisualEditorMode) 231 { 232 RatioSettings ratioSettings = GetRatioSettings(); 233 234 <div class="h-100 @theme"> 235 <div class="alert alert-dark m-0"> 236 @Translate("No assets are available") 237 </div> 238 </div> 239 } 240 241 </div> 242 243 @helper RenderAsset(MediaViewModel asset) 244 { 245 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 246 string assetValue = asset.Value; 247 248 <td class="@(theme) px-0"> 249 @foreach (string format in supportedImageFormats) 250 { //Images 251 if (assetValue.Contains(format)) 252 { 253 @RenderImage(asset) 254 } 255 } 256 @foreach (string format in supportedVideoFormats) 257 { //Videos 258 if (assetValue.Contains(format)) 259 { 260 @RenderVideoScreendump(asset) 261 } 262 } 263 @foreach (string format in supportedDocumentFormats) 264 { //Documents 265 if (assetValue.Contains(format)) 266 { 267 @RenderDocument(asset) 268 } 269 } 270 </td> 271 } 272 273 @helper RenderImage(MediaViewModel asset) 274 { 275 string productName = product.Name; 276 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 277 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 278 string imageLinkPath = imagePath; 279 imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; 280 281 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 282 283 RatioSettings ratioSettings = GetRatioSettings(); 284 285 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download> 286 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 287 <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle itemprop="image"> 288 </div> 289 </a> 290 } 291 292 @helper RenderVideoScreendump(MediaViewModel asset) 293 { 294 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 295 296 string videoScreendumpPath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : ""; 297 string videoId = videoScreendumpPath.Substring(videoScreendumpPath.LastIndexOf('/') + 1); 298 videoScreendumpPath = videoScreendumpPath.Contains("youtu.be") || videoScreendumpPath.Contains("youtube") ? "https://img.youtube.com/vi/" + videoId + "/mqdefault.jpg" : videoScreendumpPath; 299 300 string vimeoJsClass = videoScreendumpPath.Contains("vimeo") ? "js-vimeo-video-thumbnail" : ""; 301 videoScreendumpPath = videoScreendumpPath.Contains("vimeo") ? "" : videoScreendumpPath; 302 303 string productName = product.Name; 304 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 305 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 306 307 RatioSettings ratioSettings = GetRatioSettings(); 308 309 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> 310 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 311 <div class="icon-2 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 312 @if (!videoScreendumpPath.Contains(".mp4")) { 313 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@productName" @assetTitle class="@vimeoJsClass mw-100 mh-100" data-video-id="@videoId" style="object-fit: cover;"> 314 } else { 315 string videoType = Path.GetExtension(asset.Value).ToLower(); 316 317 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 318 <source src="@asset.Value" type="video/@videoType.Replace(".", "")"> 319 </video> 320 } 321 </div> 322 </div> 323 } 324 325 @helper RenderDocument(MediaViewModel asset) 326 { 327 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 328 string productName = product.Name; 329 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 330 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 331 string imageLinkPath = imagePath; 332 imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; 333 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 334 335 RatioSettings ratioSettings = GetRatioSettings(); 336 337 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download alt="@productName"> 338 @if (asset.Value.Contains(".pdf")) { 339 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 340 <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle /> 341 </div> 342 } else { 343 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 344 <div class="icon-3 position-absolute" style="z-index: 1">@ReadFile(iconPath + "file-text.svg")</div> 345 </div> 346 } 347 </a> 348 } 349 350 @helper RenderVideoPlayer(MediaViewModel asset) 351 { 352 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Name; 353 string assetValue = asset.Value; 354 string videoId = asset.Value.Substring(asset.Value.LastIndexOf('/') + 1); 355 string type = assetValue.Contains("youtu.be") || assetValue.Contains("youtube") ? "youtube" : ""; 356 type = assetValue.Contains("vimeo") ? "vimeo" : type; 357 type = assetValue.Contains(".mp4") || assetValue.Contains(".webm") ? "selfhosted" : type; 358 359 <div class="h-100" itemscope itemtype="https://schema.org/VideoObject"> 360 <span class="visually-hidden" itemprop="name">@assetName</span> 361 <span class="visually-hidden" itemprop="contentUrl">@asset.Value</span> 362 <span class="visually-hidden" itemprop="thumbnailUrl">@asset.Value</span> 363 @if (type != "selfhosted") 364 { 365 <div id="player_@(Pageview.CurrentParagraph.ID)_@(videoId)" 366 class="plyr__video-embed" 367 data-plyr-provider="@(type)" 368 data-plyr-embed-id="@videoId" 369 style="--plyr-color-main: var(--swift-foreground-color); height: 100%"> 370 </div> 371 372 <script type="module" src="~/Files/Templates/Designs/Swift/Assets/js/plyr.js"></script> 373 <script type="module"> 374 var player = new Plyr('#player_@(Pageview.CurrentParagraph.ID)_@(videoId)', { 375 type: 'video', 376 youtube: { 377 noCookie: true, 378 showinfo: 0 379 }, 380 fullscreen: { 381 enabled: true, 382 iosNative: true, 383 } 384 }); 385 386 document.querySelectorAll('.js-video-modal').forEach(function (modal) { 387 modal.addEventListener('hidden.bs.modal', function (event) { 388 player.media.pause(); 389 }) 390 }); 391 </script> 392 } 393 else 394 { 395 string videoType = Path.GetExtension(assetValue).ToLower(); 396 397 <video preload="auto" class="h-100 w-100" style="object-fit: cover;" controls> 398 <source src="@assetValue" type="video/@videoType.Replace(".", "")"> 399 </video> 400 } 401 </div> 402 } 403
Error executing template "Designs/Swift/Paragraph/Swift_ProductDetailsMediaTable.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_e2afa107fd814b61adb7f711fa214d86.Execute() in D:\dynamicweb.net\Solutions\Dynamicweb\bomedys.staging.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_ProductDetailsMediaTable.cshtml:line 58 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[] supportedImageFormats { get; set; } 9 public string[] supportedVideoFormats { get; set; } 10 public string[] supportedDocumentFormats { get; set; } 11 public string[] allSupportedFormats { get; set; } 12 13 public class RatioSettings 14 { 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() 22 { 23 var ratioSettings = new RatioSettings(); 24 25 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 26 ratio = ratio != "0" ? ratio : ""; 27 string cssClass = ratio != "" && ratio != "fill" ? " ratio" : ""; 28 string cssVariable = ratio != "" && ratio != "fill" ? "--bs-aspect-ratio: " + ratio : ""; 29 30 ratioSettings.Ratio = ratio; 31 ratioSettings.CssClass = cssClass; 32 ratioSettings.CssVariable = cssVariable; 33 ratioSettings.Fill = ratio == "fill" ? " h-100" : ""; 34 35 return ratioSettings; 36 } 37 } 38 39 @{ 40 @* Get the product data *@ 41 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 42 { 43 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 44 } 45 46 @* Supported formats *@ 47 supportedImageFormats = new string[] { ".jpg", ".jpeg", ".webp", ".png", ".gif", ".bmp", ".tiff" }; 48 supportedVideoFormats = new string[] { "youtu.be", "youtube", "vimeo", ".mp4", ".webm" }; 49 supportedDocumentFormats = new string[] { ".pdf", ".docx", ".xlsx", ".ppt", "pptx" }; 50 allSupportedFormats = supportedImageFormats.Concat(supportedVideoFormats).Concat(supportedDocumentFormats).ToArray(); 51 52 @* Collect the assets *@ 53 var selectedAssetCategories = Model.Item.GetRawValueString("ImageAssets").Split(',').ToList(); 54 bool includeImagePatternImages = Model.Item.GetBoolean("ImagePatternImages"); 55 56 @* Needed image data collection to support both DefaultImage, ImagePatterns and Image Assets *@ 57 string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : ""; 58 IEnumerable<MediaViewModel> assetsImages = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets); 59 assetsImages = assetsImages.OrderByDescending(x => x.Value.Equals(defaultImage)); 60 IEnumerable<MediaViewModel> assetsList = new MediaViewModel[]{}; 61 62 assetsList = assetsList.Union(assetsImages); 63 assetsList = includeImagePatternImages ? assetsList.Union(product.ImagePatternImages) : assetsList; 64 assetsList = includeImagePatternImages && assetsList.Count() == 0 ? assetsList.Append(product.DefaultImage) : assetsList; 65 66 bool defaultImageFallback = Model.Item.GetBoolean("DefaultImageFallback"); 67 bool showOnlyPrimaryImage = Model.Item.GetBoolean("ShowOnlyPrimaryImage"); 68 69 int totalAssets = 0; 70 if (showOnlyPrimaryImage == false) { 71 foreach (MediaViewModel asset in assetsList) { 72 var assetValue = asset.Value.ToLower(); 73 foreach (string format in allSupportedFormats) { 74 if (assetValue.Contains(format) ) { 75 totalAssets++; 76 } 77 } 78 } 79 } 80 81 if((totalAssets == 0 && product.DefaultImage != null && selectedAssetCategories.Count() == 0) || (showOnlyPrimaryImage == true && product.DefaultImage != null)) 82 { 83 assetsList = new List<MediaViewModel>(){ product.DefaultImage }; 84 totalAssets = 1; 85 } 86 87 int videoNumber = 0; 88 89 @* Layout settings *@ 90 string spacing = Model.Item.GetRawValueString("Spacing", "p-0"); 91 spacing = spacing == "none" ? "p-0" : spacing; 92 spacing = spacing == "small" ? "p-3" : spacing; 93 spacing = spacing == "large" ? "p-5" : spacing; 94 95 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 96 97 bool hideThumbnails = Model.Item.GetBoolean("HideThumbnails"); 98 99 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 100 } 101 102 @* Get assets from selected categories or get all assets *@ 103 104 105 <div class="@spacing@(theme) item_@Model.Item.SystemName.ToLower()"> 106 @if (totalAssets != 0 && assetsList.Count() != 0) 107 { 108 if (!string.IsNullOrEmpty(Model.Item.GetString("Title")) && !Model.Item.GetBoolean("HideTitle")) 109 { 110 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "h3"); 111 112 <h3 class="@titleFontSize mb-3"> 113 @Model.Item.GetString("Title") 114 </h3> 115 } 116 117 <div class="table-responsive"> 118 <table class="table table-hover align-middle mb-0" style="table-layout: fixed;"> 119 <thead> 120 <tr> 121 @if (!hideThumbnails) 122 { 123 <th style="width:60px"> </th> 124 } 125 <th>@Translate("Name")</th> 126 <th class="text-end d-none d-lg-table-cell">@Translate("Download")</th> 127 <th class="text-end" style="width:100px">@Translate("File type")</th> 128 </tr> 129 </thead> 130 <tbody class="border-top-0"> 131 @foreach (MediaViewModel asset in assetsList) 132 { 133 var assetValue = asset.Value.ToLower(); 134 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Value.Substring( asset.Value.LastIndexOf('/') + 1); 135 136 bool isVideo = false; 137 foreach (string format in supportedVideoFormats) 138 { //Videos 139 if (assetValue.Contains(format)) 140 { 141 isVideo = true; 142 } 143 } 144 145 if (!isVideo) 146 { 147 string filePath = Dynamicweb.Context.Current.Server.MapPath(assetValue); 148 long fileSize = 0; 149 150 if (File.Exists(filePath)) { 151 fileSize = new System.IO.FileInfo(filePath) != null ? new System.IO.FileInfo(filePath).Length / 1024 : 0; 152 153 foreach (string format in allSupportedFormats) 154 { 155 if (assetValue.Contains(format)) 156 { 157 <tr class="position-relative"> 158 @if (!hideThumbnails) 159 { 160 @RenderAsset(asset) 161 } 162 <td> 163 <a href="@assetValue" class="text-decoration-none text-break" download title="@assetName"> 164 @assetName 165 </a> 166 </td> 167 <td class="text-end d-none d-lg-table-cell"> 168 <a href="@assetValue" class="text-decoration-none stretched-link" download title="@assetName"> 169 @fileSize KB <div class="icon-2" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 170 </a> 171 </td> 172 <td class="text-end">@format</td> 173 </tr> 174 } 175 } 176 } 177 } 178 else 179 { 180 string videoType = asset.Value.Contains("youtu.be") || asset.Value.Contains("youtube") ? "Youtube" : ""; 181 videoType = asset.Value.Contains("vimeo") ? "Vimeo" : videoType; 182 183 <tr data-bs-toggle="modal" data-bs-target="#modal_@(Model.ID)_@videoNumber" style="cursor: pointer"> 184 @if (!hideThumbnails) 185 { 186 @RenderAsset(asset) 187 } 188 <td> 189 @assetName 190 </td> 191 <td class="d-none d-lg-table-cell"> </td> 192 <td align="right">@videoType</td> 193 </tr> 194 195 videoNumber++; 196 } 197 } 198 </tbody> 199 </table> 200 </div> 201 202 int modalVideoNumber = 0; 203 foreach (MediaViewModel asset in assetsList) 204 { 205 var assetName = asset.Value.ToLower(); 206 207 foreach (string format in supportedVideoFormats) 208 { //Videos 209 if (assetName.Contains(format)) 210 { 211 <div class="modal fade js-video-modal" id="modal_@(Model.ID)_@modalVideoNumber" tabindex="-1" aria-labelledby="productDetailsTableModalTitle_@(Model.ID)_@modalVideoNumber" aria-hidden="true"> 212 <div class="modal-dialog modal-dialog-centered modal-xl"> 213 <div class="modal-content"> 214 <div class="modal-header visually-hidden"> 215 <h5 class="modal-title" id="productDetailsTableModalTitle_@(Model.ID)_@modalVideoNumber">@product.Title</h5> 216 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 217 </div> 218 <div class="modal-body p-2 p-lg-3 h-100"> 219 @{ @RenderVideoPlayer(asset) } 220 </div> 221 </div> 222 </div> 223 </div> 224 225 modalVideoNumber++; 226 } 227 } 228 } 229 } 230 else if (Pageview.IsVisualEditorMode) 231 { 232 RatioSettings ratioSettings = GetRatioSettings(); 233 234 <div class="h-100 @theme"> 235 <div class="alert alert-dark m-0"> 236 @Translate("No assets are available") 237 </div> 238 </div> 239 } 240 241 </div> 242 243 @helper RenderAsset(MediaViewModel asset) 244 { 245 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 246 string assetValue = asset.Value; 247 248 <td class="@(theme) px-0"> 249 @foreach (string format in supportedImageFormats) 250 { //Images 251 if (assetValue.Contains(format)) 252 { 253 @RenderImage(asset) 254 } 255 } 256 @foreach (string format in supportedVideoFormats) 257 { //Videos 258 if (assetValue.Contains(format)) 259 { 260 @RenderVideoScreendump(asset) 261 } 262 } 263 @foreach (string format in supportedDocumentFormats) 264 { //Documents 265 if (assetValue.Contains(format)) 266 { 267 @RenderDocument(asset) 268 } 269 } 270 </td> 271 } 272 273 @helper RenderImage(MediaViewModel asset) 274 { 275 string productName = product.Name; 276 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 277 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 278 string imageLinkPath = imagePath; 279 imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; 280 281 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 282 283 RatioSettings ratioSettings = GetRatioSettings(); 284 285 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download> 286 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 287 <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle itemprop="image"> 288 </div> 289 </a> 290 } 291 292 @helper RenderVideoScreendump(MediaViewModel asset) 293 { 294 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 295 296 string videoScreendumpPath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : ""; 297 string videoId = videoScreendumpPath.Substring(videoScreendumpPath.LastIndexOf('/') + 1); 298 videoScreendumpPath = videoScreendumpPath.Contains("youtu.be") || videoScreendumpPath.Contains("youtube") ? "https://img.youtube.com/vi/" + videoId + "/mqdefault.jpg" : videoScreendumpPath; 299 300 string vimeoJsClass = videoScreendumpPath.Contains("vimeo") ? "js-vimeo-video-thumbnail" : ""; 301 videoScreendumpPath = videoScreendumpPath.Contains("vimeo") ? "" : videoScreendumpPath; 302 303 string productName = product.Name; 304 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 305 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 306 307 RatioSettings ratioSettings = GetRatioSettings(); 308 309 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> 310 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 311 <div class="icon-2 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 312 @if (!videoScreendumpPath.Contains(".mp4")) { 313 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@productName" @assetTitle class="@vimeoJsClass mw-100 mh-100" data-video-id="@videoId" style="object-fit: cover;"> 314 } else { 315 string videoType = Path.GetExtension(asset.Value).ToLower(); 316 317 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 318 <source src="@asset.Value" type="video/@videoType.Replace(".", "")"> 319 </video> 320 } 321 </div> 322 </div> 323 } 324 325 @helper RenderDocument(MediaViewModel asset) 326 { 327 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 328 string productName = product.Name; 329 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 330 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 331 string imageLinkPath = imagePath; 332 imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; 333 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 334 335 RatioSettings ratioSettings = GetRatioSettings(); 336 337 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download alt="@productName"> 338 @if (asset.Value.Contains(".pdf")) { 339 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 340 <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle /> 341 </div> 342 } else { 343 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 344 <div class="icon-3 position-absolute" style="z-index: 1">@ReadFile(iconPath + "file-text.svg")</div> 345 </div> 346 } 347 </a> 348 } 349 350 @helper RenderVideoPlayer(MediaViewModel asset) 351 { 352 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Name; 353 string assetValue = asset.Value; 354 string videoId = asset.Value.Substring(asset.Value.LastIndexOf('/') + 1); 355 string type = assetValue.Contains("youtu.be") || assetValue.Contains("youtube") ? "youtube" : ""; 356 type = assetValue.Contains("vimeo") ? "vimeo" : type; 357 type = assetValue.Contains(".mp4") || assetValue.Contains(".webm") ? "selfhosted" : type; 358 359 <div class="h-100" itemscope itemtype="https://schema.org/VideoObject"> 360 <span class="visually-hidden" itemprop="name">@assetName</span> 361 <span class="visually-hidden" itemprop="contentUrl">@asset.Value</span> 362 <span class="visually-hidden" itemprop="thumbnailUrl">@asset.Value</span> 363 @if (type != "selfhosted") 364 { 365 <div id="player_@(Pageview.CurrentParagraph.ID)_@(videoId)" 366 class="plyr__video-embed" 367 data-plyr-provider="@(type)" 368 data-plyr-embed-id="@videoId" 369 style="--plyr-color-main: var(--swift-foreground-color); height: 100%"> 370 </div> 371 372 <script type="module" src="~/Files/Templates/Designs/Swift/Assets/js/plyr.js"></script> 373 <script type="module"> 374 var player = new Plyr('#player_@(Pageview.CurrentParagraph.ID)_@(videoId)', { 375 type: 'video', 376 youtube: { 377 noCookie: true, 378 showinfo: 0 379 }, 380 fullscreen: { 381 enabled: true, 382 iosNative: true, 383 } 384 }); 385 386 document.querySelectorAll('.js-video-modal').forEach(function (modal) { 387 modal.addEventListener('hidden.bs.modal', function (event) { 388 player.media.pause(); 389 }) 390 }); 391 </script> 392 } 393 else 394 { 395 string videoType = Path.GetExtension(assetValue).ToLower(); 396 397 <video preload="auto" class="h-100 w-100" style="object-fit: cover;" controls> 398 <source src="@assetValue" type="video/@videoType.Replace(".", "")"> 399 </video> 400 } 401 </div> 402 } 403
Error executing template "Designs/Swift/Paragraph/Swift_ProductDetailsMediaTable.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_e2afa107fd814b61adb7f711fa214d86.Execute() in D:\dynamicweb.net\Solutions\Dynamicweb\bomedys.staging.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_ProductDetailsMediaTable.cshtml:line 58 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[] supportedImageFormats { get; set; } 9 public string[] supportedVideoFormats { get; set; } 10 public string[] supportedDocumentFormats { get; set; } 11 public string[] allSupportedFormats { get; set; } 12 13 public class RatioSettings 14 { 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() 22 { 23 var ratioSettings = new RatioSettings(); 24 25 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 26 ratio = ratio != "0" ? ratio : ""; 27 string cssClass = ratio != "" && ratio != "fill" ? " ratio" : ""; 28 string cssVariable = ratio != "" && ratio != "fill" ? "--bs-aspect-ratio: " + ratio : ""; 29 30 ratioSettings.Ratio = ratio; 31 ratioSettings.CssClass = cssClass; 32 ratioSettings.CssVariable = cssVariable; 33 ratioSettings.Fill = ratio == "fill" ? " h-100" : ""; 34 35 return ratioSettings; 36 } 37 } 38 39 @{ 40 @* Get the product data *@ 41 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 42 { 43 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 44 } 45 46 @* Supported formats *@ 47 supportedImageFormats = new string[] { ".jpg", ".jpeg", ".webp", ".png", ".gif", ".bmp", ".tiff" }; 48 supportedVideoFormats = new string[] { "youtu.be", "youtube", "vimeo", ".mp4", ".webm" }; 49 supportedDocumentFormats = new string[] { ".pdf", ".docx", ".xlsx", ".ppt", "pptx" }; 50 allSupportedFormats = supportedImageFormats.Concat(supportedVideoFormats).Concat(supportedDocumentFormats).ToArray(); 51 52 @* Collect the assets *@ 53 var selectedAssetCategories = Model.Item.GetRawValueString("ImageAssets").Split(',').ToList(); 54 bool includeImagePatternImages = Model.Item.GetBoolean("ImagePatternImages"); 55 56 @* Needed image data collection to support both DefaultImage, ImagePatterns and Image Assets *@ 57 string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : ""; 58 IEnumerable<MediaViewModel> assetsImages = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets); 59 assetsImages = assetsImages.OrderByDescending(x => x.Value.Equals(defaultImage)); 60 IEnumerable<MediaViewModel> assetsList = new MediaViewModel[]{}; 61 62 assetsList = assetsList.Union(assetsImages); 63 assetsList = includeImagePatternImages ? assetsList.Union(product.ImagePatternImages) : assetsList; 64 assetsList = includeImagePatternImages && assetsList.Count() == 0 ? assetsList.Append(product.DefaultImage) : assetsList; 65 66 bool defaultImageFallback = Model.Item.GetBoolean("DefaultImageFallback"); 67 bool showOnlyPrimaryImage = Model.Item.GetBoolean("ShowOnlyPrimaryImage"); 68 69 int totalAssets = 0; 70 if (showOnlyPrimaryImage == false) { 71 foreach (MediaViewModel asset in assetsList) { 72 var assetValue = asset.Value.ToLower(); 73 foreach (string format in allSupportedFormats) { 74 if (assetValue.Contains(format) ) { 75 totalAssets++; 76 } 77 } 78 } 79 } 80 81 if((totalAssets == 0 && product.DefaultImage != null && selectedAssetCategories.Count() == 0) || (showOnlyPrimaryImage == true && product.DefaultImage != null)) 82 { 83 assetsList = new List<MediaViewModel>(){ product.DefaultImage }; 84 totalAssets = 1; 85 } 86 87 int videoNumber = 0; 88 89 @* Layout settings *@ 90 string spacing = Model.Item.GetRawValueString("Spacing", "p-0"); 91 spacing = spacing == "none" ? "p-0" : spacing; 92 spacing = spacing == "small" ? "p-3" : spacing; 93 spacing = spacing == "large" ? "p-5" : spacing; 94 95 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 96 97 bool hideThumbnails = Model.Item.GetBoolean("HideThumbnails"); 98 99 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 100 } 101 102 @* Get assets from selected categories or get all assets *@ 103 104 105 <div class="@spacing@(theme) item_@Model.Item.SystemName.ToLower()"> 106 @if (totalAssets != 0 && assetsList.Count() != 0) 107 { 108 if (!string.IsNullOrEmpty(Model.Item.GetString("Title")) && !Model.Item.GetBoolean("HideTitle")) 109 { 110 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "h3"); 111 112 <h3 class="@titleFontSize mb-3"> 113 @Model.Item.GetString("Title") 114 </h3> 115 } 116 117 <div class="table-responsive"> 118 <table class="table table-hover align-middle mb-0" style="table-layout: fixed;"> 119 <thead> 120 <tr> 121 @if (!hideThumbnails) 122 { 123 <th style="width:60px"> </th> 124 } 125 <th>@Translate("Name")</th> 126 <th class="text-end d-none d-lg-table-cell">@Translate("Download")</th> 127 <th class="text-end" style="width:100px">@Translate("File type")</th> 128 </tr> 129 </thead> 130 <tbody class="border-top-0"> 131 @foreach (MediaViewModel asset in assetsList) 132 { 133 var assetValue = asset.Value.ToLower(); 134 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Value.Substring( asset.Value.LastIndexOf('/') + 1); 135 136 bool isVideo = false; 137 foreach (string format in supportedVideoFormats) 138 { //Videos 139 if (assetValue.Contains(format)) 140 { 141 isVideo = true; 142 } 143 } 144 145 if (!isVideo) 146 { 147 string filePath = Dynamicweb.Context.Current.Server.MapPath(assetValue); 148 long fileSize = 0; 149 150 if (File.Exists(filePath)) { 151 fileSize = new System.IO.FileInfo(filePath) != null ? new System.IO.FileInfo(filePath).Length / 1024 : 0; 152 153 foreach (string format in allSupportedFormats) 154 { 155 if (assetValue.Contains(format)) 156 { 157 <tr class="position-relative"> 158 @if (!hideThumbnails) 159 { 160 @RenderAsset(asset) 161 } 162 <td> 163 <a href="@assetValue" class="text-decoration-none text-break" download title="@assetName"> 164 @assetName 165 </a> 166 </td> 167 <td class="text-end d-none d-lg-table-cell"> 168 <a href="@assetValue" class="text-decoration-none stretched-link" download title="@assetName"> 169 @fileSize KB <div class="icon-2" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 170 </a> 171 </td> 172 <td class="text-end">@format</td> 173 </tr> 174 } 175 } 176 } 177 } 178 else 179 { 180 string videoType = asset.Value.Contains("youtu.be") || asset.Value.Contains("youtube") ? "Youtube" : ""; 181 videoType = asset.Value.Contains("vimeo") ? "Vimeo" : videoType; 182 183 <tr data-bs-toggle="modal" data-bs-target="#modal_@(Model.ID)_@videoNumber" style="cursor: pointer"> 184 @if (!hideThumbnails) 185 { 186 @RenderAsset(asset) 187 } 188 <td> 189 @assetName 190 </td> 191 <td class="d-none d-lg-table-cell"> </td> 192 <td align="right">@videoType</td> 193 </tr> 194 195 videoNumber++; 196 } 197 } 198 </tbody> 199 </table> 200 </div> 201 202 int modalVideoNumber = 0; 203 foreach (MediaViewModel asset in assetsList) 204 { 205 var assetName = asset.Value.ToLower(); 206 207 foreach (string format in supportedVideoFormats) 208 { //Videos 209 if (assetName.Contains(format)) 210 { 211 <div class="modal fade js-video-modal" id="modal_@(Model.ID)_@modalVideoNumber" tabindex="-1" aria-labelledby="productDetailsTableModalTitle_@(Model.ID)_@modalVideoNumber" aria-hidden="true"> 212 <div class="modal-dialog modal-dialog-centered modal-xl"> 213 <div class="modal-content"> 214 <div class="modal-header visually-hidden"> 215 <h5 class="modal-title" id="productDetailsTableModalTitle_@(Model.ID)_@modalVideoNumber">@product.Title</h5> 216 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 217 </div> 218 <div class="modal-body p-2 p-lg-3 h-100"> 219 @{ @RenderVideoPlayer(asset) } 220 </div> 221 </div> 222 </div> 223 </div> 224 225 modalVideoNumber++; 226 } 227 } 228 } 229 } 230 else if (Pageview.IsVisualEditorMode) 231 { 232 RatioSettings ratioSettings = GetRatioSettings(); 233 234 <div class="h-100 @theme"> 235 <div class="alert alert-dark m-0"> 236 @Translate("No assets are available") 237 </div> 238 </div> 239 } 240 241 </div> 242 243 @helper RenderAsset(MediaViewModel asset) 244 { 245 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 246 string assetValue = asset.Value; 247 248 <td class="@(theme) px-0"> 249 @foreach (string format in supportedImageFormats) 250 { //Images 251 if (assetValue.Contains(format)) 252 { 253 @RenderImage(asset) 254 } 255 } 256 @foreach (string format in supportedVideoFormats) 257 { //Videos 258 if (assetValue.Contains(format)) 259 { 260 @RenderVideoScreendump(asset) 261 } 262 } 263 @foreach (string format in supportedDocumentFormats) 264 { //Documents 265 if (assetValue.Contains(format)) 266 { 267 @RenderDocument(asset) 268 } 269 } 270 </td> 271 } 272 273 @helper RenderImage(MediaViewModel asset) 274 { 275 string productName = product.Name; 276 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 277 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 278 string imageLinkPath = imagePath; 279 imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; 280 281 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 282 283 RatioSettings ratioSettings = GetRatioSettings(); 284 285 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download> 286 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 287 <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle itemprop="image"> 288 </div> 289 </a> 290 } 291 292 @helper RenderVideoScreendump(MediaViewModel asset) 293 { 294 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 295 296 string videoScreendumpPath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : ""; 297 string videoId = videoScreendumpPath.Substring(videoScreendumpPath.LastIndexOf('/') + 1); 298 videoScreendumpPath = videoScreendumpPath.Contains("youtu.be") || videoScreendumpPath.Contains("youtube") ? "https://img.youtube.com/vi/" + videoId + "/mqdefault.jpg" : videoScreendumpPath; 299 300 string vimeoJsClass = videoScreendumpPath.Contains("vimeo") ? "js-vimeo-video-thumbnail" : ""; 301 videoScreendumpPath = videoScreendumpPath.Contains("vimeo") ? "" : videoScreendumpPath; 302 303 string productName = product.Name; 304 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 305 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 306 307 RatioSettings ratioSettings = GetRatioSettings(); 308 309 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> 310 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 311 <div class="icon-2 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 312 @if (!videoScreendumpPath.Contains(".mp4")) { 313 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@productName" @assetTitle class="@vimeoJsClass mw-100 mh-100" data-video-id="@videoId" style="object-fit: cover;"> 314 } else { 315 string videoType = Path.GetExtension(asset.Value).ToLower(); 316 317 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 318 <source src="@asset.Value" type="video/@videoType.Replace(".", "")"> 319 </video> 320 } 321 </div> 322 </div> 323 } 324 325 @helper RenderDocument(MediaViewModel asset) 326 { 327 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 328 string productName = product.Name; 329 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 330 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 331 string imageLinkPath = imagePath; 332 imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; 333 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 334 335 RatioSettings ratioSettings = GetRatioSettings(); 336 337 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download alt="@productName"> 338 @if (asset.Value.Contains(".pdf")) { 339 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 340 <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle /> 341 </div> 342 } else { 343 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 344 <div class="icon-3 position-absolute" style="z-index: 1">@ReadFile(iconPath + "file-text.svg")</div> 345 </div> 346 } 347 </a> 348 } 349 350 @helper RenderVideoPlayer(MediaViewModel asset) 351 { 352 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Name; 353 string assetValue = asset.Value; 354 string videoId = asset.Value.Substring(asset.Value.LastIndexOf('/') + 1); 355 string type = assetValue.Contains("youtu.be") || assetValue.Contains("youtube") ? "youtube" : ""; 356 type = assetValue.Contains("vimeo") ? "vimeo" : type; 357 type = assetValue.Contains(".mp4") || assetValue.Contains(".webm") ? "selfhosted" : type; 358 359 <div class="h-100" itemscope itemtype="https://schema.org/VideoObject"> 360 <span class="visually-hidden" itemprop="name">@assetName</span> 361 <span class="visually-hidden" itemprop="contentUrl">@asset.Value</span> 362 <span class="visually-hidden" itemprop="thumbnailUrl">@asset.Value</span> 363 @if (type != "selfhosted") 364 { 365 <div id="player_@(Pageview.CurrentParagraph.ID)_@(videoId)" 366 class="plyr__video-embed" 367 data-plyr-provider="@(type)" 368 data-plyr-embed-id="@videoId" 369 style="--plyr-color-main: var(--swift-foreground-color); height: 100%"> 370 </div> 371 372 <script type="module" src="~/Files/Templates/Designs/Swift/Assets/js/plyr.js"></script> 373 <script type="module"> 374 var player = new Plyr('#player_@(Pageview.CurrentParagraph.ID)_@(videoId)', { 375 type: 'video', 376 youtube: { 377 noCookie: true, 378 showinfo: 0 379 }, 380 fullscreen: { 381 enabled: true, 382 iosNative: true, 383 } 384 }); 385 386 document.querySelectorAll('.js-video-modal').forEach(function (modal) { 387 modal.addEventListener('hidden.bs.modal', function (event) { 388 player.media.pause(); 389 }) 390 }); 391 </script> 392 } 393 else 394 { 395 string videoType = Path.GetExtension(assetValue).ToLower(); 396 397 <video preload="auto" class="h-100 w-100" style="object-fit: cover;" controls> 398 <source src="@assetValue" type="video/@videoType.Replace(".", "")"> 399 </video> 400 } 401 </div> 402 } 403
Error executing template "Designs/Swift/Paragraph/Swift_ProductDetailsMediaTable.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_e2afa107fd814b61adb7f711fa214d86.Execute() in D:\dynamicweb.net\Solutions\Dynamicweb\bomedys.staging.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_ProductDetailsMediaTable.cshtml:line 58 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[] supportedImageFormats { get; set; } 9 public string[] supportedVideoFormats { get; set; } 10 public string[] supportedDocumentFormats { get; set; } 11 public string[] allSupportedFormats { get; set; } 12 13 public class RatioSettings 14 { 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() 22 { 23 var ratioSettings = new RatioSettings(); 24 25 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 26 ratio = ratio != "0" ? ratio : ""; 27 string cssClass = ratio != "" && ratio != "fill" ? " ratio" : ""; 28 string cssVariable = ratio != "" && ratio != "fill" ? "--bs-aspect-ratio: " + ratio : ""; 29 30 ratioSettings.Ratio = ratio; 31 ratioSettings.CssClass = cssClass; 32 ratioSettings.CssVariable = cssVariable; 33 ratioSettings.Fill = ratio == "fill" ? " h-100" : ""; 34 35 return ratioSettings; 36 } 37 } 38 39 @{ 40 @* Get the product data *@ 41 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 42 { 43 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 44 } 45 46 @* Supported formats *@ 47 supportedImageFormats = new string[] { ".jpg", ".jpeg", ".webp", ".png", ".gif", ".bmp", ".tiff" }; 48 supportedVideoFormats = new string[] { "youtu.be", "youtube", "vimeo", ".mp4", ".webm" }; 49 supportedDocumentFormats = new string[] { ".pdf", ".docx", ".xlsx", ".ppt", "pptx" }; 50 allSupportedFormats = supportedImageFormats.Concat(supportedVideoFormats).Concat(supportedDocumentFormats).ToArray(); 51 52 @* Collect the assets *@ 53 var selectedAssetCategories = Model.Item.GetRawValueString("ImageAssets").Split(',').ToList(); 54 bool includeImagePatternImages = Model.Item.GetBoolean("ImagePatternImages"); 55 56 @* Needed image data collection to support both DefaultImage, ImagePatterns and Image Assets *@ 57 string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : ""; 58 IEnumerable<MediaViewModel> assetsImages = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets); 59 assetsImages = assetsImages.OrderByDescending(x => x.Value.Equals(defaultImage)); 60 IEnumerable<MediaViewModel> assetsList = new MediaViewModel[]{}; 61 62 assetsList = assetsList.Union(assetsImages); 63 assetsList = includeImagePatternImages ? assetsList.Union(product.ImagePatternImages) : assetsList; 64 assetsList = includeImagePatternImages && assetsList.Count() == 0 ? assetsList.Append(product.DefaultImage) : assetsList; 65 66 bool defaultImageFallback = Model.Item.GetBoolean("DefaultImageFallback"); 67 bool showOnlyPrimaryImage = Model.Item.GetBoolean("ShowOnlyPrimaryImage"); 68 69 int totalAssets = 0; 70 if (showOnlyPrimaryImage == false) { 71 foreach (MediaViewModel asset in assetsList) { 72 var assetValue = asset.Value.ToLower(); 73 foreach (string format in allSupportedFormats) { 74 if (assetValue.Contains(format) ) { 75 totalAssets++; 76 } 77 } 78 } 79 } 80 81 if((totalAssets == 0 && product.DefaultImage != null && selectedAssetCategories.Count() == 0) || (showOnlyPrimaryImage == true && product.DefaultImage != null)) 82 { 83 assetsList = new List<MediaViewModel>(){ product.DefaultImage }; 84 totalAssets = 1; 85 } 86 87 int videoNumber = 0; 88 89 @* Layout settings *@ 90 string spacing = Model.Item.GetRawValueString("Spacing", "p-0"); 91 spacing = spacing == "none" ? "p-0" : spacing; 92 spacing = spacing == "small" ? "p-3" : spacing; 93 spacing = spacing == "large" ? "p-5" : spacing; 94 95 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 96 97 bool hideThumbnails = Model.Item.GetBoolean("HideThumbnails"); 98 99 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 100 } 101 102 @* Get assets from selected categories or get all assets *@ 103 104 105 <div class="@spacing@(theme) item_@Model.Item.SystemName.ToLower()"> 106 @if (totalAssets != 0 && assetsList.Count() != 0) 107 { 108 if (!string.IsNullOrEmpty(Model.Item.GetString("Title")) && !Model.Item.GetBoolean("HideTitle")) 109 { 110 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "h3"); 111 112 <h3 class="@titleFontSize mb-3"> 113 @Model.Item.GetString("Title") 114 </h3> 115 } 116 117 <div class="table-responsive"> 118 <table class="table table-hover align-middle mb-0" style="table-layout: fixed;"> 119 <thead> 120 <tr> 121 @if (!hideThumbnails) 122 { 123 <th style="width:60px"> </th> 124 } 125 <th>@Translate("Name")</th> 126 <th class="text-end d-none d-lg-table-cell">@Translate("Download")</th> 127 <th class="text-end" style="width:100px">@Translate("File type")</th> 128 </tr> 129 </thead> 130 <tbody class="border-top-0"> 131 @foreach (MediaViewModel asset in assetsList) 132 { 133 var assetValue = asset.Value.ToLower(); 134 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Value.Substring( asset.Value.LastIndexOf('/') + 1); 135 136 bool isVideo = false; 137 foreach (string format in supportedVideoFormats) 138 { //Videos 139 if (assetValue.Contains(format)) 140 { 141 isVideo = true; 142 } 143 } 144 145 if (!isVideo) 146 { 147 string filePath = Dynamicweb.Context.Current.Server.MapPath(assetValue); 148 long fileSize = 0; 149 150 if (File.Exists(filePath)) { 151 fileSize = new System.IO.FileInfo(filePath) != null ? new System.IO.FileInfo(filePath).Length / 1024 : 0; 152 153 foreach (string format in allSupportedFormats) 154 { 155 if (assetValue.Contains(format)) 156 { 157 <tr class="position-relative"> 158 @if (!hideThumbnails) 159 { 160 @RenderAsset(asset) 161 } 162 <td> 163 <a href="@assetValue" class="text-decoration-none text-break" download title="@assetName"> 164 @assetName 165 </a> 166 </td> 167 <td class="text-end d-none d-lg-table-cell"> 168 <a href="@assetValue" class="text-decoration-none stretched-link" download title="@assetName"> 169 @fileSize KB <div class="icon-2" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 170 </a> 171 </td> 172 <td class="text-end">@format</td> 173 </tr> 174 } 175 } 176 } 177 } 178 else 179 { 180 string videoType = asset.Value.Contains("youtu.be") || asset.Value.Contains("youtube") ? "Youtube" : ""; 181 videoType = asset.Value.Contains("vimeo") ? "Vimeo" : videoType; 182 183 <tr data-bs-toggle="modal" data-bs-target="#modal_@(Model.ID)_@videoNumber" style="cursor: pointer"> 184 @if (!hideThumbnails) 185 { 186 @RenderAsset(asset) 187 } 188 <td> 189 @assetName 190 </td> 191 <td class="d-none d-lg-table-cell"> </td> 192 <td align="right">@videoType</td> 193 </tr> 194 195 videoNumber++; 196 } 197 } 198 </tbody> 199 </table> 200 </div> 201 202 int modalVideoNumber = 0; 203 foreach (MediaViewModel asset in assetsList) 204 { 205 var assetName = asset.Value.ToLower(); 206 207 foreach (string format in supportedVideoFormats) 208 { //Videos 209 if (assetName.Contains(format)) 210 { 211 <div class="modal fade js-video-modal" id="modal_@(Model.ID)_@modalVideoNumber" tabindex="-1" aria-labelledby="productDetailsTableModalTitle_@(Model.ID)_@modalVideoNumber" aria-hidden="true"> 212 <div class="modal-dialog modal-dialog-centered modal-xl"> 213 <div class="modal-content"> 214 <div class="modal-header visually-hidden"> 215 <h5 class="modal-title" id="productDetailsTableModalTitle_@(Model.ID)_@modalVideoNumber">@product.Title</h5> 216 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 217 </div> 218 <div class="modal-body p-2 p-lg-3 h-100"> 219 @{ @RenderVideoPlayer(asset) } 220 </div> 221 </div> 222 </div> 223 </div> 224 225 modalVideoNumber++; 226 } 227 } 228 } 229 } 230 else if (Pageview.IsVisualEditorMode) 231 { 232 RatioSettings ratioSettings = GetRatioSettings(); 233 234 <div class="h-100 @theme"> 235 <div class="alert alert-dark m-0"> 236 @Translate("No assets are available") 237 </div> 238 </div> 239 } 240 241 </div> 242 243 @helper RenderAsset(MediaViewModel asset) 244 { 245 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 246 string assetValue = asset.Value; 247 248 <td class="@(theme) px-0"> 249 @foreach (string format in supportedImageFormats) 250 { //Images 251 if (assetValue.Contains(format)) 252 { 253 @RenderImage(asset) 254 } 255 } 256 @foreach (string format in supportedVideoFormats) 257 { //Videos 258 if (assetValue.Contains(format)) 259 { 260 @RenderVideoScreendump(asset) 261 } 262 } 263 @foreach (string format in supportedDocumentFormats) 264 { //Documents 265 if (assetValue.Contains(format)) 266 { 267 @RenderDocument(asset) 268 } 269 } 270 </td> 271 } 272 273 @helper RenderImage(MediaViewModel asset) 274 { 275 string productName = product.Name; 276 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 277 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 278 string imageLinkPath = imagePath; 279 imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; 280 281 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 282 283 RatioSettings ratioSettings = GetRatioSettings(); 284 285 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download> 286 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 287 <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle itemprop="image"> 288 </div> 289 </a> 290 } 291 292 @helper RenderVideoScreendump(MediaViewModel asset) 293 { 294 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 295 296 string videoScreendumpPath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : ""; 297 string videoId = videoScreendumpPath.Substring(videoScreendumpPath.LastIndexOf('/') + 1); 298 videoScreendumpPath = videoScreendumpPath.Contains("youtu.be") || videoScreendumpPath.Contains("youtube") ? "https://img.youtube.com/vi/" + videoId + "/mqdefault.jpg" : videoScreendumpPath; 299 300 string vimeoJsClass = videoScreendumpPath.Contains("vimeo") ? "js-vimeo-video-thumbnail" : ""; 301 videoScreendumpPath = videoScreendumpPath.Contains("vimeo") ? "" : videoScreendumpPath; 302 303 string productName = product.Name; 304 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 305 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 306 307 RatioSettings ratioSettings = GetRatioSettings(); 308 309 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> 310 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 311 <div class="icon-2 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 312 @if (!videoScreendumpPath.Contains(".mp4")) { 313 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@productName" @assetTitle class="@vimeoJsClass mw-100 mh-100" data-video-id="@videoId" style="object-fit: cover;"> 314 } else { 315 string videoType = Path.GetExtension(asset.Value).ToLower(); 316 317 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 318 <source src="@asset.Value" type="video/@videoType.Replace(".", "")"> 319 </video> 320 } 321 </div> 322 </div> 323 } 324 325 @helper RenderDocument(MediaViewModel asset) 326 { 327 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 328 string productName = product.Name; 329 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 330 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 331 string imageLinkPath = imagePath; 332 imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; 333 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 334 335 RatioSettings ratioSettings = GetRatioSettings(); 336 337 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download alt="@productName"> 338 @if (asset.Value.Contains(".pdf")) { 339 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 340 <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle /> 341 </div> 342 } else { 343 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 344 <div class="icon-3 position-absolute" style="z-index: 1">@ReadFile(iconPath + "file-text.svg")</div> 345 </div> 346 } 347 </a> 348 } 349 350 @helper RenderVideoPlayer(MediaViewModel asset) 351 { 352 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Name; 353 string assetValue = asset.Value; 354 string videoId = asset.Value.Substring(asset.Value.LastIndexOf('/') + 1); 355 string type = assetValue.Contains("youtu.be") || assetValue.Contains("youtube") ? "youtube" : ""; 356 type = assetValue.Contains("vimeo") ? "vimeo" : type; 357 type = assetValue.Contains(".mp4") || assetValue.Contains(".webm") ? "selfhosted" : type; 358 359 <div class="h-100" itemscope itemtype="https://schema.org/VideoObject"> 360 <span class="visually-hidden" itemprop="name">@assetName</span> 361 <span class="visually-hidden" itemprop="contentUrl">@asset.Value</span> 362 <span class="visually-hidden" itemprop="thumbnailUrl">@asset.Value</span> 363 @if (type != "selfhosted") 364 { 365 <div id="player_@(Pageview.CurrentParagraph.ID)_@(videoId)" 366 class="plyr__video-embed" 367 data-plyr-provider="@(type)" 368 data-plyr-embed-id="@videoId" 369 style="--plyr-color-main: var(--swift-foreground-color); height: 100%"> 370 </div> 371 372 <script type="module" src="~/Files/Templates/Designs/Swift/Assets/js/plyr.js"></script> 373 <script type="module"> 374 var player = new Plyr('#player_@(Pageview.CurrentParagraph.ID)_@(videoId)', { 375 type: 'video', 376 youtube: { 377 noCookie: true, 378 showinfo: 0 379 }, 380 fullscreen: { 381 enabled: true, 382 iosNative: true, 383 } 384 }); 385 386 document.querySelectorAll('.js-video-modal').forEach(function (modal) { 387 modal.addEventListener('hidden.bs.modal', function (event) { 388 player.media.pause(); 389 }) 390 }); 391 </script> 392 } 393 else 394 { 395 string videoType = Path.GetExtension(assetValue).ToLower(); 396 397 <video preload="auto" class="h-100 w-100" style="object-fit: cover;" controls> 398 <source src="@assetValue" type="video/@videoType.Replace(".", "")"> 399 </video> 400 } 401 </div> 402 } 403
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_9441fc0705c8407ab39a4447ed394827.Execute() in D:\dynamicweb.net\Solutions\Dynamicweb\bomedys.staging.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="RelatedProductsForm_@Model.ID" data-response-target-element="RelatedProducts_@Model.ID" data-preloader="inline" data-update-url="false" class="item_@Model.Item.SystemName.ToLower()"> 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="MainProductID_@Model.ID" 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="MainProductID_@Model.ID" 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="MainProductID_@Model.ID" 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="RelatedProductsTitle_@Model.ID" 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="ParentColumnSize_@Model.ID" 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="RelatedProducts_@Model.ID" class="h-100 swift_product_slider_container"></div> 386 </div> 387 } 388 else if (product?.RelatedGroups != null) 389 { 390 @* Create multiple slider containers, if type is Product relation *@ 391 <div class="grid w-100 h-100@(generalTheme)" style="grid-row-gap: 4rem"> 392 <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)_@group.Id" class="g-col-12 h-100 swift_product_slider_container"></div> 396 } 397 </div> 398 } 399 400 @* Initialize *@ 401 if (sourceType != "related-products") 402 { 403 <script type="module"> 404 if (document.querySelector("#RelatedProducts_@Model.ID").closest("[data-col-size]")) { 405 document.querySelector("#ParentColumnSize_@Model.ID").value = document.querySelector("#RelatedProducts_@Model.ID").closest("[data-col-size]").getAttribute("data-col-size"); 406 } 407 swift.PageUpdater.Update(document.querySelector("#RelatedProductsForm_@Model.ID")).then(function () { 408 setTimeout(function() { 409 const isVisualEditor = @(Converter.ToString(Pageview.IsVisualEditorMode).ToLowerInvariant()); 410 const productSliderContainer = document.querySelector(".swift_product_slider_container"); 411 412 if (productSliderContainer && productSliderContainer.innerHTML !== "") { 413 productSliderContainer.classList.remove("d-none"); 414 } 415 else if (!isVisualEditor) { 416 productSliderContainer.closest("[class*=column]").classList.add("d-none"); 417 } 418 }, 150); 419 }); 420 </script> 421 } 422 else if (product?.RelatedGroups != null) 423 { 424 @* Create multiple sliders, if type is Product relation *@ 425 foreach (var group in product.RelatedGroups) 426 { 427 IList<string> fromProductIds = new List<string> { }; 428 429 foreach (var relatedProduct in group.Products) 430 { 431 fromProductIds.Add(relatedProduct.ProductId); 432 } 433 <script type="module"> 434 document.querySelector("#ParentColumnSize_@Model.ID").value = document.querySelector("#RelatedProducts_@(Model.ID)_@group.Id").closest("[data-col-size]").getAttribute("data-col-size"); 435 document.querySelector("#MainProductID_@Model.ID").value = "@string.Join(",", fromProductIds)"; 436 document.querySelector("#RelatedProductsTitle_@Model.ID").value = "@group.Name"; 437 document.querySelector("#RelatedProductsForm_@Model.ID").setAttribute("data-response-target-element", "RelatedProducts_@(Model.ID)_@group.Id"); 438 439 swift.PageUpdater.Update(document.querySelector("#RelatedProductsForm_@Model.ID")); 440 </script> 441 } 442 } 443 } 444
Sorry. There is nothing to view here