Error executing template "Designs/Swift/eCom/ProductCatalog/ProductViewDetail.cshtml" System.NullReferenceException: Object reference not set to an instance of an object. at CompiledRazorTemplates.Dynamic.RazorEngine_a99c809981484f13b2d9e766df1397ff.ExecuteAsync() at RazorEngine.Templating.TemplateBase.Run(ExecuteContext context, TextWriter reader) at RazorEngine.Templating.RazorEngineCore.RunTemplate(ICompiledTemplate template, TextWriter writer, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.DynamicWrapperService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass23_0.<Run>b__0(TextWriter writer) at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter) at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, Type modelType, Object model, DynamicViewBag viewBag) at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template) at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template) at Dynamicweb.Rendering.Template.RenderRazorTemplate()
1 @inherits ViewModelTemplate<ProductViewModel> 2 @using Dynamicweb.Rendering 3 @using Dynamicweb.Ecommerce.ProductCatalog 4 @using Dynamicweb.Core 5 @using Dynamicweb.Environment 6 7 @{ 8 string metaDescription = string.IsNullOrEmpty(Model.MetaDescription) ? Model.Name : Model.MetaDescription; 9 10 Pageview.Meta.AddTag($"<meta property=\"og:image\" content=\"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{Model.DefaultImage.Value}\">"); 11 Pageview.Meta.AddTag($"<meta property=\"og:image:alt\" content=\"{Model.Name}\">"); 12 Pageview.Meta.AddTag($"<meta property=\"og:description\" content=\"{metaDescription}\">"); 13 14 Pageview.Meta.AddTag("twitter:image", Model.DefaultImage.Value); 15 Pageview.Meta.AddTag("twitter:image:alt", Model.Name); 16 Pageview.Meta.AddTag("twitter:description", metaDescription); 17 } 18 19 @{ 20 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 21 { 22 Dynamicweb.Context.Current.Items["ProductDetails"] = Model; 23 } 24 else 25 { 26 Dynamicweb.Context.Current.Items.Add("ProductDetails", Model); 27 } 28 29 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]); 30 if (isLazyLoadingForProductInfoEnabled) 31 { 32 string showPricesWithVat = Pageview.Area.EcomPricesWithVat.ToLower(); 33 bool neverShowVat = string.IsNullOrEmpty(showPricesWithVat); 34 bool hasVariantId = !string.IsNullOrEmpty(Model.VariantId); 35 string variantIdParam = hasVariantId ? $"/{Model.VariantId}" : ""; 36 string priceFilledProperties = $"Price,PriceFormatted{(showPricesWithVat == "false" && !neverShowVat ? ",PriceWithVat,PriceWithVatFormatted" : "")}"; 37 string productInfoFeed = $@"/dwapi/ecommerce/products/{Model.Id}{variantIdParam} 38 ?UserId={Converter.ToString(Pageview.User?.ID)} 39 &LanguageId={Pageview.Area.EcomLanguageId}&CurrencyCode={Pageview.Area.EcomCurrencyId}&CountryCode={Pageview.Area.EcomCountryCode}&ShopId={Pageview.Area.EcomShopId} 40 &FilledProperties=Id,Price,PriceBeforeDiscount,StockLevel,VariantInfo,NeverOutOfstock,Prices 41 &PriceSettings.ShowPricesWithVat={Pageview.Area.EcomPricesWithVat} 42 &PriceSettings.FilledProperties={priceFilledProperties} 43 &getproductinfo=true"; 44 Dynamicweb.Context.Current.Items["ProductInfoFeed"] = productInfoFeed; 45 46 <script type="module"> 47 swift.LiveProductInfo.init(); 48 </script> 49 } 50 51 string googleTagManagerID = Pageview.AreaSettings.GetString("GoogleTagManagerID"); 52 string googleAnalyticsMeasurementID = Pageview.AreaSettings.GetString("GoogleAnalyticsMeasurementID"); 53 54 bool allowTracking = true; 55 if (CookieManager.IsCookieManagementActive) 56 { 57 var cookieOptInLevel = CookieManager.GetCookieOptInLevel(); 58 allowTracking = cookieOptInLevel == CookieOptInLevel.All || (cookieOptInLevel == CookieOptInLevel.Functional && CookieManager.GetCookieOptInCategories().Contains("Statistical")); 59 } 60 61 if ((!string.IsNullOrWhiteSpace(googleAnalyticsMeasurementID) || !string.IsNullOrWhiteSpace(googleTagManagerID)) && allowTracking) 62 { 63 <script> 64 gtag("event", "view_item", { 65 currency: "@Model.Price.CurrencyCode", 66 value: @PriceViewModelExtensions.ToStringInvariant(Model.Price), 67 items: [ 68 { 69 item_id: "@Model.Number", 70 item_name: "@Dynamicweb.Core.Encoders.HtmlEncoder.JavaScriptStringEncode(Model.Name)", 71 currency: "@Model.Price.CurrencyCode", 72 price: @PriceViewModelExtensions.ToStringInvariant(Model.Price) 73 } 74 ] 75 }); 76 </script> 77 } 78 } 79 80 <script> 81 window.addEventListener('load', function (event) { 82 swift.Video.init(); 83 }); 84 </script> 85
Error executing template "Designs/Swift/Paragraph/Swift_ProductDetailsImage.cshtml" System.ArgumentNullException: Value cannot be null. (Parameter 'source') at System.Linq.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument) at System.Linq.Enumerable.Where[TSource](IEnumerable`1 source, Func`2 predicate) at CompiledRazorTemplates.Dynamic.RazorEngine_2f1f9743f3f043f1aa590280f4c187cc.ExecuteAsync() at RazorEngine.Templating.TemplateBase.Run(ExecuteContext context, TextWriter reader) at RazorEngine.Templating.RazorEngineCore.RunTemplate(ICompiledTemplate template, TextWriter writer, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.DynamicWrapperService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass23_0.<Run>b__0(TextWriter writer) at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter) at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, Type modelType, Object model, DynamicViewBag viewBag) at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template) at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template) at Dynamicweb.Rendering.Template.RenderRazorTemplate()
1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.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 { 16 public string Ratio { get; set; } 17 public string CssClass { get; set; } 18 public string CssVariable { get; set; } 19 public string Fill { get; set; } 20 } 21 22 public RatioSettings GetRatioSettings(string size = "desktop") 23 { 24 var ratioSettings = new RatioSettings(); 25 26 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 27 ratio = ratio != "0" ? ratio : ""; 28 string cssClass = ratio != "" && ratio != "fill" ? " ratio" : ""; 29 string cssVariable = ratio != "" && ratio != "fill" ? "--bs-aspect-ratio: " + ratio : ""; 30 cssClass = ratio == "fill" && size == "mobile" ? " ratio" : cssClass; 31 cssVariable = ratio == "fill" && size == "mobile" ? "--bs-aspect-ratio: 66%" : cssVariable; 32 33 ratioSettings.Ratio = ratio; 34 ratioSettings.CssClass = cssClass; 35 ratioSettings.CssVariable = cssVariable; 36 ratioSettings.Fill = ratio == "fill" ? " h-100" : ""; 37 38 return ratioSettings; 39 } 40 41 public string GetArrowsColor() 42 { 43 var invertColor = Model.Item.GetBoolean("InvertModalArrowsColor"); 44 var arrowsColor = invertColor ? " carousel-dark" : string.Empty; 45 return arrowsColor; 46 } 47 48 public string GetThumbnailPlacement() 49 { 50 return Model.Item.GetRawValueString("ThumbnailPlacement", "bottom"); 51 } 52 53 public string GetThumbnailRowSettingCss() 54 { 55 switch (GetThumbnailPlacement()) 56 { 57 case "bottom": 58 return "d-flex flex-wrap"; 59 case "left": 60 return "d-flex flex-column order-first"; 61 case "right": 62 return "d-flex flex-column order-last"; 63 default: 64 return "d-flex flex-wrap"; 65 } 66 } 67 68 } 69 70 @{ 71 ProductViewModel product = null; 72 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 73 { 74 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 75 } 76 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 77 { 78 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 79 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 80 81 if (productList?.Products is object) 82 { 83 product = productList.Products[0]; 84 } 85 } 86 } 87 88 @if (product is object) 89 { 90 @* Supported formats *@ 91 supportedImageFormats = new string[] { ".jpg", ".jpeg", ".webp", ".png", ".gif", ".bmp", ".tiff" }; 92 supportedVideoFormats = new string[] { "youtu.be", "youtube", "vimeo", ".mp4", ".webm" }; 93 supportedDocumentFormats = new string[] { ".pdf", ".docx", ".xlsx", ".ppt", "pptx" }; 94 allSupportedFormats = supportedImageFormats.Concat(supportedVideoFormats).Concat(supportedDocumentFormats).ToArray(); 95 96 @* Collect the assets *@ 97 var selectedAssetCategories = Model.Item.GetList("ImageAssets")?.GetRawValue().OfType<string>(); 98 bool includeImagePatternImages = Model.Item.GetBoolean("ImagePatternImages"); 99 100 @* Needed image data collection to support both DefaultImage, ImagePatterns and Image Assets *@ 101 string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : ""; 102 IEnumerable<MediaViewModel> assetsImages = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets); 103 assetsImages = assetsImages.OrderByDescending(x => x.Value.Equals(defaultImage)); 104 IEnumerable<MediaViewModel> assetsList = new MediaViewModel[] { }; 105 assetsList = assetsList.Union(assetsImages); 106 assetsList = includeImagePatternImages ? assetsList.Union(product.ImagePatternImages) : assetsList; 107 assetsList = includeImagePatternImages && assetsList.Count() == 0 ? assetsList.Append(product.DefaultImage) : assetsList; 108 109 bool defaultImageFallback = Model.Item.GetBoolean("DefaultImageFallback"); 110 bool showOnlyPrimaryImage = Model.Item.GetBoolean("ShowOnlyPrimaryImage"); 111 112 int totalAssets = 0; 113 if (showOnlyPrimaryImage == false) 114 { 115 foreach (MediaViewModel asset in assetsList) 116 { 117 var assetValue = asset.Value; 118 foreach (string format in allSupportedFormats) 119 { 120 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 121 { 122 totalAssets++; 123 } 124 } 125 } 126 } 127 128 if ((totalAssets == 0 && product.DefaultImage != null && selectedAssetCategories.Count() == 0) || (showOnlyPrimaryImage == true && product.DefaultImage != null) || totalAssets == 0 && defaultImageFallback) 129 { 130 assetsList = new List<MediaViewModel>() { product.DefaultImage }; 131 totalAssets = 1; 132 } 133 134 @* Theme settings *@ 135 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 136 137 var badgeParms = new Dictionary<string, object>(); 138 badgeParms.Add("size", "h5"); 139 badgeParms.Add("saleBadgeType", Model.Item.GetRawValue("SaleBadgeType")); 140 badgeParms.Add("saleBadgeCssClassName", Model.Item.GetRawValue("SaleBadgeDesign")); 141 badgeParms.Add("newBadgeCssClassName", Model.Item.GetRawValue("NewBadgeDesign")); 142 badgeParms.Add("newPublicationDays", Model.Item.GetInt32("NewPublicationDays")); 143 badgeParms.Add("campaignBadgesValues", Model.Item.GetList("CampaignBadges")?.GetRawValue().OfType<string>().ToList()); 144 145 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("SaleBadgeDesign")) && Model.Item.GetRawValueString("SaleBadgeDesign") != "none" ? true : false; 146 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("NewBadgeDesign")) && Model.Item.GetRawValueString("NewBadgeDesign") != "none" ? true : false; 147 DateTime createdDate = product.Created.Value; 148 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false; 149 showBadges = (newBadgeEnabled && Model.Item.GetInt32("NewPublicationDays") == 0) || (newBadgeEnabled && (createdDate.AddDays(Model.Item.GetInt32("NewPublicationDays")) > DateTime.Now)) ? true : showBadges; 150 showBadges = !string.IsNullOrEmpty(Model.Item.GetRawValueString("CampaignBadges")) ? true : showBadges; 151 152 @* Get assets from selected categories or get all assets *@ 153 if (totalAssets != 0) 154 { 155 int assetNumber = 0; 156 int thumbnailNumber = 0; 157 int modalAssetNumber = 0; 158 string thumbnailAxisCss = GetThumbnailPlacement() == "bottom" ? "flex-column" : string.Empty; 159 160 <div class="d-flex gap-3 h-100 @(thumbnailAxisCss) @(theme) item_@Model.Item.SystemName.ToLower()"> 161 <div id="SmallScreenImages_@Model.ID" class="carousel@(GetArrowsColor()) col position-relative" data-bs-ride="carousel"> 162 <div class="carousel-inner h-100"> 163 @foreach (MediaViewModel asset in assetsList) 164 { 165 var assetValue = asset.Value; 166 foreach (string format in allSupportedFormats) 167 { 168 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 169 { 170 string activeSlide = assetNumber == 0 ? "active" : ""; 171 172 <div class="carousel-item @activeSlide" data-bs-interval="99999"> 173 @{ 174 string size = "mobile"; 175 176 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 177 178 179 <div class="h-100 @(imageTheme)"> 180 @foreach (string imageFormat in supportedImageFormats) 181 { //Images 182 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 183 { 184 if (product is object) 185 { 186 string productName = product.Name; 187 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 188 string imageLinkPath = Dynamicweb.Context.Current.Server.UrlEncode(imagePath); 189 190 RatioSettings ratioSettings = GetRatioSettings(size); 191 192 var parms = new Dictionary<string, object>(); 193 parms.Add("alt", productName + asset.Keywords); 194 parms.Add("itemprop", "image"); 195 parms.Add("columns", Model.GridRowColumnCount); 196 parms.Add("eagerLoadNewImages", Model.Item.GetBoolean("DisableLazyLoading")); 197 parms.Add("doNotUseGetimage", Model.Item.GetBoolean("DisableGetImage")); 198 if (!string.IsNullOrEmpty(asset.DisplayName)) 199 { 200 parms.Add("title", asset.DisplayName); 201 } 202 203 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") 204 { 205 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 206 } 207 else 208 { 209 parms.Add("cssClass", "mw-100 mh-100"); 210 } 211 212 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 213 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@assetNumber"> 214 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 215 </div> 216 </a> 217 } 218 } 219 } 220 @foreach (string videoFormat in supportedVideoFormats) 221 { //Videos 222 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 223 { 224 if (Model.Item.GetString("OpenVideoInModal") == "true") 225 { 226 if (product is object) 227 { 228 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 229 230 string videoScreendumpPath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : ""; 231 string videoId = videoScreendumpPath.Substring(videoScreendumpPath.LastIndexOf('/') + 1); 232 videoScreendumpPath = videoScreendumpPath.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || videoScreendumpPath.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "https://img.youtube.com/vi/" + videoId + "/maxresdefault.jpg" : videoScreendumpPath; 233 234 string vimeoJsClass = videoScreendumpPath.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "js-vimeo-video-thumbnail" : ""; 235 videoScreendumpPath = videoScreendumpPath.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "" : videoScreendumpPath; 236 237 string productName = product.Name; 238 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 239 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 240 241 RatioSettings ratioSettings = GetRatioSettings(size); 242 243 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable); cursor: pointer" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 244 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@assetNumber"> 245 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 246 @if (videoScreendumpPath.IndexOf(".mp4", StringComparison.OrdinalIgnoreCase) < 0) 247 { 248 <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)"> 249 } 250 else 251 { 252 string videoType = Path.GetExtension(asset.Value).ToLower(); 253 string videoPathEncoded = System.Uri.EscapeDataString(asset.Value); 254 255 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 256 <source src="@(videoPathEncoded)#t=0.001" type="video/@videoType.Replace(".", "")"> 257 </video> 258 } 259 </div> 260 </div> 261 262 <script> 263 function CheckIfVideoThumbnailExist(image) { 264 if (image.width == 120) { 265 const lowQualityImage = "https://img.youtube.com/vi/@(videoId)/hqdefault.jpg" 266 image.src = lowQualityImage; 267 } 268 } 269 </script> 270 } 271 } 272 else 273 { 274 if (product is object) 275 { 276 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Name; 277 string videoId = asset.Value.Substring(asset.Value.LastIndexOf('/') + 1); 278 string type = assetValue.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "youtube" : ""; 279 type = assetValue.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "vimeo" : type; 280 type = assetValue.IndexOf(".mp4", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf(".webm", StringComparison.OrdinalIgnoreCase) >= 0 ? "selfhosted" : type; 281 282 string openInModal = Model.Item.GetString("OpenVideoInModal"); 283 bool autoPlay = Model.Item.GetBoolean("VideoAutoPlay"); 284 285 <div class="h-100" itemscope itemtype="https://schema.org/VideoObject"> 286 <span class="visually-hidden" itemprop="name">@assetName</span> 287 <span class="visually-hidden" itemprop="contentUrl">@asset.Value</span> 288 <span class="visually-hidden" itemprop="thumbnailUrl">@asset.Value</span> 289 290 @if (type != "selfhosted") 291 { 292 <div id="player_@(Pageview.CurrentParagraph.ID)_@(videoId)_@size" 293 class="plyr__video-embed" 294 data-plyr-provider="@(type)" 295 data-plyr-embed-id="@videoId" 296 style="--plyr-color-main: var(--swift-foreground-color); height: 100%"> 297 </div> 298 299 <script type="module" src="/Files/Templates/Designs/Swift/Assets/js/plyr.js"></script> 300 301 <script type="module"> 302 var player = new Plyr('#player_@(Pageview.CurrentParagraph.ID)_@(videoId)_@size', { 303 type: 'video', 304 youtube: { 305 noCookie: true, 306 showinfo: 0 307 }, 308 fullscreen: { 309 enabled: true, 310 iosNative: true, 311 } 312 }); 313 314 @if (autoPlay && openInModal == "false") 315 { 316 <text> 317 player.config.autoplay = true; 318 player.config.muted = true; 319 player.config.volume = 0; 320 player.media.loop = true; 321 322 player.on('ready', function() { 323 if (player.config.autoplay === true) { 324 player.media.play(); 325 } 326 }); 327 </text> 328 } 329 330 @if (openInModal == "true") 331 { 332 <text> 333 var productDetailsGalleryModal = document.querySelector('#modal_@Model.ID') 334 productDetailsGalleryModal.addEventListener('hidden.bs.modal', function (event) { 335 player.media.pause(); 336 }) 337 </text> 338 } 339 </script> 340 } 341 else 342 { 343 string autoPlayAttributes = (autoPlay && openInModal == "false") ? "loop autoplay muted playsinline" : ""; 344 string videoType = Path.GetExtension(assetValue).ToLower(); 345 string videoPathEncoded = System.Uri.EscapeDataString(assetValue); 346 347 <video preload="auto" @autoPlayAttributes class="h-100 w-100" style="object-fit: cover;" controls> 348 <source src="@(videoPathEncoded)#t=0.001" type="video/@videoType.Replace(".", "")"> 349 </video> 350 } 351 </div> 352 } 353 } 354 } 355 } 356 @foreach (string documentFormat in supportedDocumentFormats) 357 { //Documents 358 if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) 359 { 360 if (product is object) 361 { 362 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 363 364 string productName = product.Name; 365 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 366 string imageLinkPath = imagePath; 367 368 RatioSettings ratioSettings = GetRatioSettings(size); 369 370 var parms = new Dictionary<string, object>(); 371 parms.Add("alt", productName + asset.Keywords); 372 parms.Add("itemprop", "image"); 373 parms.Add("fullwidth", true); 374 parms.Add("columns", Model.GridRowColumnCount); 375 if (!string.IsNullOrEmpty(asset.DisplayName)) 376 { 377 parms.Add("title", asset.DisplayName); 378 } 379 380 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") 381 { 382 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 383 } 384 else 385 { 386 parms.Add("cssClass", "mw-100 mh-100"); 387 } 388 389 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download alt="@Translate("Download")"> 390 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 391 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 392 @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) 393 { 394 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 395 } 396 </div> 397 </a> 398 } 399 400 } 401 } 402 </div> 403 } 404 405 406 </div> 407 assetNumber++; 408 } 409 } 410 } 411 </div> 412 @if (showBadges) 413 { 414 <div class="position-absolute top-0 left-0 p-2 p-lg-3"> 415 @{@RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms)} 416 </div> 417 } 418 419 </div> 420 421 @if (totalAssets > 1) 422 { 423 <div class="@(GetThumbnailRowSettingCss()) gap-3" id="SmallScreenImagesThumbnails_@Model.ID"> 424 @foreach (MediaViewModel asset in assetsList) 425 { 426 var assetValue = asset.Value; 427 string assetName = asset.Name; 428 assetName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 429 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 430 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 431 432 string imagePath = Dynamicweb.Context.Current.Server.UrlEncode(assetValue); 433 imagePath = assetValue.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "https://img.youtube.com/vi/" + assetValue.Substring(assetValue.LastIndexOf('/') + 1) + "/mqdefault.jpg" : imagePath; 434 string imagePathThumb = assetValue.StartsWith("/Files/", StringComparison.OrdinalIgnoreCase) ? imagePath.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) < 0 && imagePath.IndexOf(".mp4", StringComparison.OrdinalIgnoreCase) < 0 ? $"/Admin/Public/GetImage.ashx?image={imagePath}&width=180&format=webp" : imagePath : assetValue; 435 436 RatioSettings ratioSettings = GetRatioSettings("desktop"); 437 438 <div class="border outline-none @(ratioSettings.CssClass)" style="@(ratioSettings.CssVariable); cursor: pointer; min-width: 7rem; max-width: 8rem;" data-bs-target="#SmallScreenImages_@Model.ID" data-bs-slide-to="@thumbnailNumber"> 439 @foreach (string imageFormat in supportedImageFormats) 440 { //Images 441 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 442 { 443 <img src="@imagePathThumb" alt="@assetName" @assetTitle class="p-0 p-lg-1 w-100 h-100" style="object-fit: contain;"> 444 445 thumbnailNumber++; 446 } 447 } 448 449 @foreach (string videoFormat in supportedVideoFormats) 450 { //Images 451 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 452 { 453 string videoScreendumpPath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : ""; 454 string videoId = videoScreendumpPath.Substring(videoScreendumpPath.LastIndexOf('/') + 1); 455 videoScreendumpPath = videoScreendumpPath.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || videoScreendumpPath.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "https://img.youtube.com/vi/" + videoId + "/maxresdefault.jpg" : videoScreendumpPath; 456 457 string vimeoJsClass = videoScreendumpPath.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "js-vimeo-video-thumbnail" : ""; 458 videoScreendumpPath = videoScreendumpPath.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "" : videoScreendumpPath; 459 460 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 461 462 if (videoScreendumpPath.IndexOf(".mp4", StringComparison.OrdinalIgnoreCase) < 0) 463 { 464 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@assetTitle" @assetTitle class="@vimeoJsClass mw-100 mh-100" data-video-id="@videoId" style="object-fit: cover;" onload="CheckIfVideoThumbnailExist(this)"> 465 } 466 else 467 { 468 string videoType = Path.GetExtension(asset.Value).ToLower(); 469 string videoPathEncoded = System.Uri.EscapeDataString(asset.Value); 470 471 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 472 <source src="@(videoPathEncoded)#t=0.001" type="video/@videoType.Replace(".", "")"> 473 </video> 474 } 475 476 thumbnailNumber++; 477 } 478 } 479 480 @foreach (string documentFormat in supportedDocumentFormats) 481 { //Images 482 if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) 483 { 484 <a href="@assetValue" class="ratio ratio-4x3 border outline-none" style="cursor: pointer; min-width: 7rem; max-width: 8rem;" download title="@asset.Value"> 485 @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) 486 { 487 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 488 <div class="icon-3 position-absolute text-light" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 489 </div> 490 <img src="@imagePathThumb" alt="@assetName" @assetTitle class="p-0 p-lg-1 mw-100 mh-100" style="object-fit: cover;"> 491 } 492 else 493 { 494 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 495 <div class="icon-3 position-absolute" style="z-index: 1">@ReadFile(iconPath + "file-text.svg")</div> 496 </div> 497 } 498 </a> 499 500 thumbnailNumber++; 501 } 502 } 503 </div> 504 } 505 </div> 506 } 507 </div> 508 509 @* Modal with slides *@ 510 <div class="modal fade swift_products-details-images-modal" id="modal_@Model.ID" tabindex="-1" aria-labelledby="productDetailsGalleryModalTitle_@Model.ID" aria-hidden="true"> 511 <div class="modal-dialog modal-dialog-centered modal-xl"> 512 <div class="modal-content"> 513 <div class="modal-header visually-hidden"> 514 <h5 class="modal-title" id="productDetailsGalleryModalTitle_@Model.ID">@product.Title</h5> 515 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 516 </div> 517 <div class="modal-body p-2 p-lg-3 h-100"> 518 <div id="ModalCarousel_@Model.ID" class="carousel@(GetArrowsColor()) h-100" data-bs-ride="carousel"> 519 <div class="carousel-inner h-100 @theme"> 520 @foreach (MediaViewModel asset in assetsList) 521 { 522 var assetValue = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 523 foreach (string supportedFormat in supportedImageFormats.Concat(supportedVideoFormats).ToArray()) 524 { 525 if (assetValue.IndexOf(supportedFormat, StringComparison.OrdinalIgnoreCase) >= 0) 526 { 527 string imagePath = assetValue; 528 string activeSlide = modalAssetNumber == 0 ? "active" : ""; 529 530 var parms = new Dictionary<string, object>(); 531 parms.Add("cssClass", "d-block mw-100 mh-100 m-auto"); 532 parms.Add("fullwidth", true); 533 parms.Add("columns", Model.GridRowColumnCount); 534 535 <div class="carousel-item @activeSlide h-100" data-bs-interval="99999"> 536 @foreach (string imageFormat in supportedImageFormats) 537 { //Images 538 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 539 { 540 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 541 } 542 } 543 544 @foreach (string videoFormat in supportedVideoFormats) 545 { //Videos 546 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 547 { 548 if (product is object) 549 { 550 string videoPlayerSize = "modal"; 551 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Name; 552 assetValue = asset.Value; 553 string videoId = asset.Value.Substring(asset.Value.LastIndexOf('/') + 1); 554 string type = assetValue.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "youtube" : ""; 555 type = assetValue.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "vimeo" : type; 556 type = assetValue.IndexOf(".mp4", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf(".webm", StringComparison.OrdinalIgnoreCase) >= 0 ? "selfhosted" : type; 557 558 string openInModal = Model.Item.GetString("OpenVideoInModal"); 559 bool autoPlay = Model.Item.GetBoolean("VideoAutoPlay"); 560 561 <div class="h-100" itemscope itemtype="https://schema.org/VideoObject"> 562 <span class="visually-hidden" itemprop="name">@assetName</span> 563 <span class="visually-hidden" itemprop="contentUrl">@asset.Value</span> 564 <span class="visually-hidden" itemprop="thumbnailUrl">@asset.Value</span> 565 566 @if (type != "selfhosted") 567 { 568 <div id="player_@(Pageview.CurrentParagraph.ID)_@(videoId)_@videoPlayerSize" 569 class="plyr__video-embed" 570 data-plyr-provider="@(type)" 571 data-plyr-embed-id="@videoId" 572 style="--plyr-color-main: var(--swift-foreground-color); height: 100%"> 573 </div> 574 575 <script type="module" src="/Files/Templates/Designs/Swift/Assets/js/plyr.js"></script> 576 577 <script type="module"> 578 var player = new Plyr('#player_@(Pageview.CurrentParagraph.ID)_@(videoId)_@videoPlayerSize', { 579 type: 'video', 580 youtube: { 581 noCookie: true, 582 showinfo: 0 583 }, 584 fullscreen: { 585 enabled: true, 586 iosNative: true, 587 } 588 }); 589 590 @if (autoPlay && openInModal == "false") 591 { 592 <text> 593 player.config.autoplay = true; 594 player.config.muted = true; 595 player.config.volume = 0; 596 player.media.loop = true; 597 598 player.on('ready', function() { 599 if (player.config.autoplay === true) { 600 player.media.play(); 601 } 602 }); 603 </text> 604 } 605 606 @if (openInModal == "true") 607 { 608 <text> 609 var productDetailsGalleryModal = document.querySelector('#modal_@Model.ID') 610 productDetailsGalleryModal.addEventListener('hidden.bs.modal', function (event) { 611 player.media.pause(); 612 }) 613 </text> 614 } 615 </script> 616 } 617 else 618 { 619 string autoPlayAttributes = (autoPlay && openInModal == "false") ? "loop autoplay muted playsinline" : ""; 620 string videoType = Path.GetExtension(assetValue).ToLower(); 621 string videoPathEncoded = System.Uri.EscapeDataString(assetValue); 622 623 <video preload="auto" @autoPlayAttributes class="h-100 w-100" style="object-fit: cover;" controls> 624 <source src="@(videoPathEncoded)#t=0.001" type="video/@videoType.Replace(".", "")"> 625 </video> 626 } 627 </div> 628 } 629 } 630 } 631 </div> 632 modalAssetNumber++; 633 } 634 } 635 } 636 <button class="carousel-control-prev carousel-control-area" type="button" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide="prev"> 637 <span class="carousel-control-prev-icon" aria-hidden="true"></span> 638 <span class="visually-hidden">@Translate("Previous")</span> 639 </button> 640 <button class="carousel-control-next carousel-control-area" type="button" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide="next"> 641 <span class="carousel-control-next-icon" aria-hidden="true"></span> 642 <span class="visually-hidden">@Translate("Next")</span> 643 </button> 644 </div> 645 </div> 646 </div> 647 </div> 648 </div> 649 </div> 650 } 651 else if (Pageview.IsVisualEditorMode) 652 { 653 RatioSettings ratioSettings = GetRatioSettings("desktop"); 654 655 <div class="h-100 @theme"> 656 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> 657 <img src="/Files/Images/missing_image.jpg" loading="lazy" decoding="async" class="mh-100 mw-100" style="object-fit: cover;"> 658 </div> 659 </div> 660 } 661 } 662 else if (Pageview.IsVisualEditorMode) 663 { 664 <div class="alert alert-dark m-0">@Translate("No products available")</div> 665 } 666 667 668 669
Sorry. There is nothing to view here