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

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_e7dde302f0d542b3909bb5e8c1658163.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.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.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 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.GetList("CampaignBadges")?.GetRawValue().OfType<string>().ToList()); 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.GetList("CampaignBadges")?.GetRawValue().OfType<string>().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_ProductPrice.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_0128d688efa242d3b252a2044a5be428.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 4 @{ 5 ProductViewModel product = null; 6 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 7 { 8 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 9 } 10 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 11 { 12 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 13 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 14 15 if (productList?.Products is object) 16 { 17 product = productList.Products[0]; 18 } 19 } 20 21 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", ""); 22 bool anonymousUser = Pageview.User == null; 23 bool isErpConnectionDown = !Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsWebServiceConnectionAvailable"]); 24 bool hidePrice = anonymousUsersLimitations.Contains("price") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHidePrices") && isErpConnectionDown; 25 26 bool productIsDiscontinued = product is object && product.Discontinued; 27 bool doNotShowPriceIfProductIsDiscontinued = Model.Item.GetBoolean("DoNotShowPriceIfProductIsDiscontinued"); 28 var isDiscontinued = productIsDiscontinued && doNotShowPriceIfProductIsDiscontinued; 29 } 30 31 @if (product is object && !hidePrice && !isDiscontinued) { 32 bool showInformativePrice = Model.Item.GetBoolean("ShowInformativePrice"); 33 string unitId = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("UnitId")) ? Dynamicweb.Context.Current.Request.Form.Get("UnitId") : string.Empty; 34 35 string priceFontSize = Model.Item.GetRawValueString("PriceSize", "fs-2"); 36 string horizontalAlign = Model.Item.GetRawValueString("HorizontalAlignment", ""); 37 string layout = Model.Item.GetRawValueString("Layout", "horizontal"); 38 string textAlign = horizontalAlign == "center" ? "text-center" : string.Empty; 39 textAlign = horizontalAlign == "end" ? "text-end" : textAlign; 40 41 horizontalAlign = horizontalAlign == "center" && layout == "horizontal" ? "justify-content-center" : horizontalAlign; 42 horizontalAlign = horizontalAlign == "end" && layout == "horizontal" ? "justify-content-end" : horizontalAlign; 43 horizontalAlign = horizontalAlign == "center" && layout == "vertical" ? "align-items-center" : horizontalAlign; 44 horizontalAlign = horizontalAlign == "end" && layout == "vertical" ? "align-items-end" : horizontalAlign; 45 46 string flexDirection = layout == "horizontal" ? string.Empty : "flex-column"; 47 string flexGap = layout == "horizontal" ? "gap-3" : string.Empty; 48 string order = layout == "horizontal" ? string.Empty : "order-2"; 49 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? "theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 50 theme = GetViewParameter("theme") != null ? GetViewParameterString("theme") : theme; 51 52 string contentPadding = Model.Item.GetRawValueString("ContentPadding", ""); 53 contentPadding = contentPadding == "none" ? "p-0" : contentPadding; 54 contentPadding = contentPadding == "small" ? "p-1 px-md-2 py-md-1" : contentPadding; 55 contentPadding = contentPadding == "large" ? "p-2 px-md-3 py-md-2" : contentPadding; 56 57 string showPricesWithVat = Pageview.Area.EcomPricesWithVat.ToLower(); 58 bool neverShowVat = string.IsNullOrEmpty(showPricesWithVat); 59 60 string priceMin = ""; 61 string priceMax = ""; 62 63 string liveInfoClass = ""; 64 string productInfoFeed = ""; 65 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]); 66 if (isLazyLoadingForProductInfoEnabled) 67 { 68 if (Dynamicweb.Context.Current.Items.Contains("ProductInfoFeed")) 69 { 70 productInfoFeed = Dynamicweb.Context.Current.Items["ProductInfoFeed"]?.ToString(); 71 if (!string.IsNullOrEmpty(productInfoFeed)) 72 { 73 productInfoFeed = $"data-product-info-feed=\"{productInfoFeed}\""; 74 } 75 } 76 liveInfoClass = "js-live-info"; 77 } 78 79 <div class="@textAlign @liveInfoClass item_@Model.Item.SystemName.ToLower()" data-product-id="@product.Id" data-variant-id="@product.VariantId" @productInfoFeed> 80 @if (showInformativePrice && product.PriceInformative.Price != 0) 81 { 82 <div class="opacity-50"> 83 <span>@Translate("RRP") </span> 84 <span class="text-decoration-line-through text-price">@product.PriceInformative.PriceFormatted</span> 85 </div> 86 } 87 <div class="@priceFontSize m-0 d-flex flex-wrap @flexDirection @flexGap @horizontalAlign" style="row-gap: 0 !important" itemprop="offers" itemscope itemtype="https://schema.org/Offer"> 88 <span itemprop="priceCurrency" content="@product.Price.CurrencyCode" class="d-none"></span> 89 90 91 @if (showPricesWithVat == "false" && !neverShowVat) 92 { 93 if (isLazyLoadingForProductInfoEnabled && !Pageview.IsVisualEditorMode) 94 { 95 <span itemprop="price" content="" class="d-none"></span> 96 <span class="text-decoration-line-through js-text-decoration-line-through opacity-75 me-3 text-price js-text-price d-none" data-show-if="LiveProductInfo.product.Price.Price != LiveProductInfo.product.PriceBeforeDiscount.Price"></span> 97 } 98 else 99 { 100 string beforePrice = !string.IsNullOrEmpty(unitId) ? product.GetPrice(unitId).PriceBeforeDiscount.PriceWithoutVatFormatted : product.PriceBeforeDiscount.PriceWithoutVatFormatted; 101 102 <span itemprop="price" content="@product.Price.PriceWithoutVat.ToString("0.00", System.Globalization.CultureInfo.InvariantCulture)" class="d-none"></span> 103 if (product.Price.Price != product.PriceBeforeDiscount.Price) 104 { 105 <span class="text-decoration-line-through opacity-75 @order">@beforePrice</span> 106 } 107 } 108 } 109 else 110 { 111 if (isLazyLoadingForProductInfoEnabled && !Pageview.IsVisualEditorMode) 112 { 113 <span itemprop="price" content="" class="d-none"></span> 114 <span class="text-decoration-line-through js-text-decoration-line-through opacity-75 me-3 text-price js-text-price d-none" data-show-if="LiveProductInfo.product.Price.Price != LiveProductInfo.product.PriceBeforeDiscount.Price"></span> 115 } 116 else 117 { 118 string beforePrice = !string.IsNullOrEmpty(unitId) ? product.GetPrice(unitId).PriceBeforeDiscount.PriceFormatted : product.PriceBeforeDiscount.PriceFormatted; 119 120 <span itemprop="price" content="@product.Price.Price.ToString("0.00", System.Globalization.CultureInfo.InvariantCulture)" class="d-none"></span> 121 122 if (product.Price.Price != product.PriceBeforeDiscount.Price) 123 { 124 <span class="text-decoration-line-through opacity-75 @order"> 125 <span class="text-price">@beforePrice</span> 126 </span> 127 } 128 } 129 } 130 131 @if (showPricesWithVat == "false" && !neverShowVat) 132 { 133 if (isLazyLoadingForProductInfoEnabled && !Pageview.IsVisualEditorMode) 134 { 135 <span class="text-price js-text-price"> 136 <span class="spinner-border" role="status"></span> 137 </span> 138 } 139 else 140 { 141 string price = !string.IsNullOrEmpty(unitId) ? product.GetPrice(unitId).Price.PriceWithoutVatFormatted : product.Price.PriceWithoutVatFormatted; 142 143 if (product?.VariantInfo?.VariantInfo != null) 144 { 145 priceMin = product?.VariantInfo?.PriceMin?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithoutVatFormatted : ""; 146 priceMax = product?.VariantInfo?.PriceMax?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithoutVatFormatted : ""; 147 } 148 if (priceMin != priceMax) 149 { 150 price = priceMin + " - " + priceMax; 151 } 152 <span class="@theme @contentPadding"> 153 <span class="text-price">@price</span> 154 </span> 155 } 156 } 157 else 158 { 159 if (isLazyLoadingForProductInfoEnabled && !Pageview.IsVisualEditorMode) 160 { 161 <span class="text-price js-text-price"> 162 <span class="spinner-border" role="status"></span> 163 </span> 164 } 165 else 166 { 167 string price = !string.IsNullOrEmpty(unitId) ? product.GetPrice(unitId).Price.PriceFormatted : product.Price.PriceFormatted; 168 169 if (product?.VariantInfo?.VariantInfo != null) 170 { 171 priceMin = product?.VariantInfo?.PriceMin?.PriceFormatted != null ? product.VariantInfo.PriceMin.PriceFormatted : ""; 172 priceMax = product?.VariantInfo?.PriceMax?.PriceFormatted != null ? product.VariantInfo.PriceMax.PriceFormatted : ""; 173 } 174 if (priceMin != priceMax) 175 { 176 price = priceMin + " - " + priceMax; 177 } 178 <span class="@theme @contentPadding"> 179 <span class="text-price">@price</span> 180 </span> 181 } 182 } 183 184 @* Stock state for Schema.org, start *@ 185 @{ 186 Uri url = Dynamicweb.Context.Current.Request.Url; 187 } 188 189 <link itemprop="url" href="@url"> 190 191 @{ 192 bool IsNeverOutOfStock = product.NeverOutOfstock; 193 } 194 195 @if (IsNeverOutOfStock) 196 { 197 <span itemprop="availability" class="d-none">@Translate("Available in stock")</span> 198 } 199 else 200 { 201 if (product.StockLevel > 0) 202 { 203 <span itemprop="availability" class="d-none">InStock</span> 204 } 205 else 206 { 207 <span itemprop="availability" class="d-none">OutOfStock</span> 208 } 209 } 210 @* Stock state for Schema.org, stop *@ 211 212 </div> 213 214 @if (showPricesWithVat == "false" && !neverShowVat) 215 { 216 if (isLazyLoadingForProductInfoEnabled && !Pageview.IsVisualEditorMode) 217 { 218 <small class="opacity-85 fst-normal js-text-price-with-vat d-none" data-suffix="@Translate("Incl. VAT")"></small> 219 } 220 else 221 { 222 string price = !string.IsNullOrEmpty(unitId) ? product.GetPrice(unitId).Price.PriceWithVatFormatted : product.Price.PriceWithVatFormatted; 223 224 if (product?.VariantInfo?.VariantInfo != null) 225 { 226 priceMin = product?.VariantInfo?.PriceMin?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithVatFormatted : ""; 227 priceMax = product?.VariantInfo?.PriceMax?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithVatFormatted : ""; 228 } 229 if (priceMin != priceMax) 230 { 231 price = priceMin + " - " + priceMax; 232 } 233 <small class="opacity-85 fst-normal">@price @Translate("Incl. VAT")</small> 234 } 235 } 236 </div> 237 } 238 else if (Pageview.IsVisualEditorMode) 239 { 240 <div class="alert alert-dark m-0" role="alert"> 241 <span>@Translate("No products available")</span> 242 </div> 243 } 244
Error executing template "Designs/Swift/Paragraph/Swift_ProductAddToCart.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_dc413dc045e94df1bfe44063a948b416.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.Core.Encoders 4 @using System.Globalization 5 6 @functions { 7 string DoubleToString(double? value) 8 { 9 if (value.HasValue) 10 { 11 return value.Value.ToString(CultureInfo.InvariantCulture); 12 } 13 return null; 14 } 15 } 16 17 @{ 18 ProductViewModel product = null; 19 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 20 { 21 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 22 } 23 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 24 { 25 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 26 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 27 28 if (productList?.Products is object) 29 { 30 product = productList.Products[0]; 31 } 32 } 33 34 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", ""); 35 bool anonymousUser = Pageview.User == null; 36 bool isErpConnectionDown = !Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsWebServiceConnectionAvailable"]); 37 bool hideAddToCart = anonymousUsersLimitations.Contains("cart") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHideAddToCart") && isErpConnectionDown; 38 hideAddToCart = Pageview.IsVisualEditorMode ? false : hideAddToCart; 39 } 40 41 @if (product is object && !hideAddToCart) 42 { 43 string horizontalAlign = Model.Item.GetRawValueString("HorizontalAlignment", ""); 44 horizontalAlign = horizontalAlign == "center" ? "justify-content-center" : horizontalAlign; 45 horizontalAlign = horizontalAlign == "end" ? "justify-content-end" : horizontalAlign; 46 horizontalAlign = horizontalAlign == "full" ? "" : horizontalAlign; 47 48 bool favoritesSelector = !string.IsNullOrEmpty(Model.Item.GetString("ShowAddToFavorites")) ? Model.Item.GetBoolean("ShowAddToFavorites") : false; 49 bool quantitySelector = !string.IsNullOrEmpty(Model.Item.GetString("ShowQuantitySelector")) ? Model.Item.GetBoolean("ShowQuantitySelector") : false; 50 bool unitsSelector = !string.IsNullOrEmpty(Model.Item.GetString("ShowUnitsSelector")) ? Model.Item.GetBoolean("ShowUnitsSelector") : false; 51 bool hideInventory = !string.IsNullOrEmpty(Model.Item.GetString("HideInventory")) ? Model.Item.GetBoolean("HideInventory") : false; 52 bool hideStockState = !string.IsNullOrEmpty(Model.Item.GetString("HideStockState")) ? Model.Item.GetBoolean("HideStockState") : false; 53 54 string buttonSize = Model.Item.GetRawValueString("ButtonSize", "regular"); 55 string inputSize = string.Empty; 56 57 switch (buttonSize) 58 { 59 case "small": 60 inputSize = " input-group-sm"; 61 buttonSize = " btn-sm"; 62 break; 63 case "regular": 64 buttonSize = string.Empty; 65 break; 66 case "large": 67 inputSize = " input-group-lg"; 68 buttonSize = " btn-lg"; 69 break; 70 } 71 72 string iconPath = "/Files/icons/"; 73 string url = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("CartService")); 74 if (!url.Contains("LayoutTemplate")) 75 { 76 url += url.Contains("?") ? "&LayoutTemplate=Swift_MiniCart.cshtml" : "?LayoutTemplate=Swift_MiniCart.cshtml"; 77 } 78 79 string whenVariantsExist = Model.Item.GetRawValueString("WhenVariantsExist", "hide"); 80 string flexFill = Model.Item.GetRawValueString("HorizontalAlignment", "") == "full" ? "flex-fill" : ""; 81 string fullWidth = Model.Item.GetRawValueString("HorizontalAlignment", "") == "full" ? "w-100" : ""; 82 string addToCartIcon = Model.Item.GetRawValueString("Icon", iconPath + "shopping-cart.svg"); 83 string addToCartLabel = !addToCartIcon.Contains("_none") ? $"<span class=\"icon-2\">{ReadFile(addToCartIcon)}</span>" : ""; 84 addToCartLabel += !addToCartIcon.Contains("_none") && !Model.Item.GetBoolean("HideButtonText") ? " " : ""; 85 addToCartLabel += !Model.Item.GetBoolean("HideButtonText") ? $"<span class=\"d-none d-md-inline\">{Translate("Add to cart")}</span><span class=\"d-inline d-md-none\">{Translate("Add")}</span>" : ""; 86 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]); 87 88 bool userHasPendingQuote = Dynamicweb.Ecommerce.Common.Context.Cart != null && Dynamicweb.Ecommerce.Common.Context.Cart.IsQuote; 89 90 if (product.VariantInfo.VariantInfo == null || whenVariantsExist == "disable") 91 { 92 string unitId = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("UnitId")) ? Dynamicweb.Context.Current.Request.Form.Get("UnitId") : product.DefaultUnitId; 93 if (string.IsNullOrEmpty(unitId) && product?.UnitOptions != null) 94 { 95 if (product.UnitOptions.FirstOrDefault<UnitOptionViewModel>() != null) 96 { 97 unitId = product.UnitOptions.FirstOrDefault<UnitOptionViewModel>().Id; 98 } 99 } 100 101 double? stepQty = product.PurchaseQuantityStep > 0 ? product.PurchaseQuantityStep : 1; 102 double? minQty = product.PurchaseMinimumQuantity > 0 ? product.PurchaseMinimumQuantity : 1; 103 double? valueQty = minQty > stepQty ? minQty : stepQty; 104 string disableAddToCart = null; 105 double? maxQty = null; 106 107 if (product.ProductType == Dynamicweb.Ecommerce.Products.ProductType.Stock && !product.NeverOutOfstock) 108 { 109 disableAddToCart = (product.StockLevel <= 0) || (!product.NeverOutOfstock && isLazyLoadingForProductInfoEnabled) ? "disabled" : disableAddToCart; 110 maxQty = product.StockLevel; 111 } 112 113 disableAddToCart = whenVariantsExist == "disable" && product.VariantInfo.VariantInfo != null && string.IsNullOrEmpty(product.VariantId) ? "disabled" : disableAddToCart; 114 disableAddToCart = product.Discontinued ? "disabled" : disableAddToCart; 115 116 if (unitsSelector && product.UnitOptions.Count > 0) 117 { 118 <form method="post" action="/Default.aspx?ID=@(Pageview.Page.ID)&ProductId=@product.Id" id="UnitSelectorForm_@(product.Id)_@(product.VariantId.Replace(".", "_"))_@Model.ID"> 119 <input type="hidden" name="redirect" value="false"> 120 <input type="hidden" name="VariantID" value="@product.VariantId"> 121 <input type="hidden" name="UnitID" class="js-unit-id" value="@unitId"> 122 </form> 123 } 124 125 <div class="d-flex @horizontalAlign @fullWidth js-input-group item_@Model.Item.SystemName.ToLower()"> 126 @if (!anonymousUser && favoritesSelector) 127 { 128 @RenderPartial("Components/ToggleFavorite.cshtml", product) 129 } 130 131 <form method="post" action="@url" class="@fullWidth" style="z-index: 1"> 132 <input type="hidden" name="redirect" value="false"> 133 <input type="hidden" name="ProductId" value="@product.Id"> 134 <input type="hidden" name="ProductName" value="@HtmlEncoder.HtmlEncode(product.Name)"> 135 <input type="hidden" name="ProductVariantName" value="@product.VariantName"> 136 <input type="hidden" name="ProductCurrency" value="@Dynamicweb.Ecommerce.Common.Context.Currency.Code"> 137 <input type="hidden" name="ProductPrice" value="@product.Price.ToStringInvariant()"> 138 <input type="hidden" name="ProductDiscount" value="@product.Discount.ToStringInvariant()"> 139 <input type="hidden" name="ProductReferer" value="component_ProductAddToCart"> 140 <input type="hidden" name="cartcmd" value="add"> 141 <input type="submit" class="d-none" onclick="event.preventDefault(); swift.Cart.Update(event)"> @* Fix for enterKey should not redirect to minicart page *@ 142 143 @if (!string.IsNullOrEmpty(product.VariantId)) 144 { 145 <input type="hidden" name="VariantId" value="@product.VariantId"> 146 } 147 148 <template class="js-step-quantity-warning"> 149 <div class="modal-header"> 150 <h1 class="modal-title fs-5">@Translate("The quantity is not valid")</h1> 151 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 152 </div> 153 <div class="modal-body"> 154 @Translate("Please select a quantity that is dividable by") @stepQty 155 </div> 156 </template> 157 158 159 <template class="js-min-quantity-warning"> 160 <div class="modal-header"> 161 <h1 class="modal-title fs-5">@Translate("The product could not be added to the cart")</h1> 162 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 163 </div> 164 <div class="modal-body"> 165 @Translate("The quantity is not valid. You must buy at least") @product.PurchaseMinimumQuantity 166 </div> 167 </template> 168 169 <template class="js-value-missing-warning"> 170 <div class="modal-header"> 171 <h1 class="modal-title fs-5">@Translate("No amount specified")</h1> 172 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 173 </div> 174 <div class="modal-body"> 175 @Translate("Specify an amount to add to the cart") 176 </div> 177 </template> 178 179 180 @if (userHasPendingQuote) 181 { 182 <input type="hidden" name="PendingQuote" value="true"> 183 184 <template class="js-pending-quote-notice"> 185 <div class="modal-header"> 186 <h1 class="modal-title fs-5">@Translate("Pending Quote")</h1> 187 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="@Translate("Close")"></button> 188 </div> 189 <div class="modal-body"> 190 @Translate("You need to complete your current quote or empty the cart before adding this product to cart.") 191 </div> 192 </template> 193 } 194 195 @if (quantitySelector || (!anonymousUser && product.VariantInfo.VariantInfo != null) || (!anonymousUser && favoritesSelector)) 196 { 197 <input type="hidden" id="Unit_@(product.Id)_@product.VariantId.Replace(".", "_")" name="UnitID" value="@unitId" /> 198 } 199 200 <div class="d-flex flex-row w-100"> 201 @if (!quantitySelector) 202 { 203 <input id="Quantity_@(product.Id)_@product.VariantId.Replace(".", "_")" class="swift_quantity_field" name="Quantity" value="@valueQty" type="hidden" @disableAddToCart> 204 } 205 206 @if (unitsSelector && product.UnitOptions.Count > 0) 207 { 208 string selectedUnitName = !string.IsNullOrEmpty(unitId) && product?.UnitOptions != null ? unitId : product.UnitOptions.FirstOrDefault<UnitOptionViewModel>().Name; 209 210 foreach (var unitOption in product.UnitOptions) 211 { 212 if (unitOption.Id == unitId) 213 { 214 selectedUnitName = unitOption.Name; 215 } 216 } 217 218 <div class="d-flex flex-column gap-2 w-100"> 219 <div class="input-group input-primary-button-group flex-nowrap@(inputSize)"> 220 221 @if (quantitySelector) 222 { 223 <input id="Quantity_@(product.Id)_@product.VariantId.Replace(".", "_")" name="Quantity" value="@DoubleToString(valueQty)" step="@DoubleToString(stepQty)" min="@DoubleToString(minQty)" max="@DoubleToString(maxQty)" class="form-control swift_quantity-field" style="min-width: 60px; max-width: 100px; z-index: 1" type="number" @disableAddToCart> 224 } 225 226 <button class="btn btn-secondary @flexFill dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false"> 227 @selectedUnitName 228 </button> 229 230 <ul class="dropdown-menu swift_unit-field"> 231 @foreach (var unitOption in product.UnitOptions) 232 { 233 var selectedUnit = unitOption.Id == unitId ? "selected" : ""; 234 235 <li> 236 <button type="button" class="btn dropdown-item" data-value="@unitOption.Id" onclick="document.querySelector('#UnitSelectorForm_@(product.Id)_@(product.VariantId.Replace(".", "_"))_@Model.ID').querySelector('.js-unit-id').value = this.getAttribute('data-value'); 237 document.querySelector('#Unit_@(product.Id)_@product.VariantId.Replace(".", "_")').value = this.getAttribute('data-value'); 238 swift.PageUpdater.Update(document.querySelector('#UnitSelectorForm_@(product.Id)_@(product.VariantId.Replace(".", "_"))_@Model.ID'))"> 239 <span>@unitOption.Name</span> 240 <span> 241 @if (unitOption.StockLevel > 0 || unitOption.NeverOutOfStock) 242 { 243 if (!Model.Item.GetBoolean("HideInventory") && !unitOption.NeverOutOfStock) 244 { 245 <span class="small text-success">@unitOption.StockLevel @Translate("In stock")</span> 246 } 247 else 248 { 249 <span class="small text-success">@Translate("In stock")</span> 250 } 251 } 252 else 253 { 254 <span class="small text-danger">@Translate("Out of Stock")</span> 255 } 256 </span> 257 </button> 258 </li> 259 } 260 </ul> 261 </div> 262 <button type="button" onclick="swift.Cart.Update(event)" class="btn btn-primary @(buttonSize) js-add-to-cart-button" style="white-space: nowrap" @disableAddToCart title="@Translate("Add to cart")" id="AddToCartButton@(product.Id)_@Pageview.CurrentParagraph.ID"> 263 @if (!Model.Item.GetBoolean("HideButtonText")) 264 { 265 <span class="text-nowrap d-flex align-items-center justify-content-center gap-2"> 266 @addToCartLabel 267 </span> 268 } 269 else 270 { 271 @addToCartLabel 272 } 273 </button> 274 </div> 275 } 276 else 277 { 278 <div class="input-group input-primary-button-group flex-nowrap@(inputSize)"> 279 @if (quantitySelector) 280 { 281 <input id="Quantity_@(product.Id)_@product.VariantId.Replace(".", "_")" name="Quantity" value="@DoubleToString(valueQty)" step="@DoubleToString(stepQty)" min="@DoubleToString(minQty)" max="@DoubleToString(maxQty)" class="form-control swift_quantity-field" style="min-width: 60px; max-width: 100px; z-index: 1" type="number" @disableAddToCart> 282 } 283 284 <button type="button" onclick="swift.Cart.Update(event)" class="btn btn-primary @(buttonSize) @flexFill js-add-to-cart-button" style="white-space: nowrap" @disableAddToCart title="@Translate("Add to cart")" id="AddToCartButton@(product.Id)_@Pageview.CurrentParagraph.ID"> 285 @if (!Model.Item.GetBoolean("HideButtonText")) 286 { 287 <span class="text-nowrap d-flex align-items-center justify-content-center gap-2"> 288 @addToCartLabel 289 </span> 290 } 291 else 292 { 293 @addToCartLabel 294 } 295 </button> 296 </div> 297 } 298 </div> 299 </form> 300 </div> 301 } 302 else if (whenVariantsExist == "modal") 303 { 304 string ButtonShape = Model.Item.GetRawValueString("VariantButtonShape", "square"); 305 string buttonAspectRatio = Model.Item.GetRawValueString("VariantImageAspectRatio", "56%"); 306 307 string buttonText = Translate("Select"); 308 string variantId = !string.IsNullOrWhiteSpace(product.VariantId) ? product.VariantId : product.DefaultVariantId; 309 310 string variantSelectorServicePageId = !string.IsNullOrEmpty(Model.Item.GetString("VariantSelectorServicePageId")) ? Model.Item.GetLink("VariantSelectorServicePageId").PageId.ToString() : ""; 311 variantSelectorServicePageId = variantSelectorServicePageId != "" ? variantSelectorServicePageId : GetPageIdByNavigationTag("VariantSelectorService").ToString(); 312 313 <div class="d-flex @horizontalAlign w-100 item_@Model.Item.SystemName.ToLower()"> 314 @if (!anonymousUser && favoritesSelector) 315 { 316 @RenderPartial("Components/ToggleFavorite.cshtml", product) 317 } 318 <form action="/Default.aspx?ID=@variantSelectorServicePageId" data-response-target-element="DynamicModalContent" data-preloader="inline" style="z-index: 1" class="@fullWidth"> 319 <input type="hidden" name="ProductID" value="@product.Id"> 320 <input type="hidden" name="VariantID" value="@variantId"> 321 <input type="hidden" name="QuantitySelector" value="@quantitySelector.ToString()"> 322 <input type="hidden" name="HideInventory" value="@hideInventory.ToString()"> 323 <input type="hidden" name="HideStockState" value="@hideStockState.ToString()"> 324 <input type="hidden" name="ButtonLayout" value="@ButtonShape"> 325 <input type="hidden" name="ButtonAspectRatio" value="@buttonAspectRatio"> 326 <input type="hidden" name="VariantSelectorServicePage" value="@variantSelectorServicePageId"> 327 <input type="hidden" name="ViewType" value="ModalContent"> 328 @if (isLazyLoadingForProductInfoEnabled) 329 { 330 @* If lazy loading is enabled, bypass it because we're loading a modal window, so render everything as if it was server-side *@ 331 <input type="hidden" name="getproductinfo" value="true"> 332 } 333 <button type="button" onclick="swift.PageUpdater.Update(event)" class="btn btn-primary@(buttonSize) @fullWidth" title="@Translate("Select")" data-bs-toggle="modal" data-bs-target="#DynamicModal" id="OpenVariantSelectorModal@(product.Id)_@Pageview.CurrentParagraph.ID">@buttonText</button> 334 </form> 335 </div> 336 } 337 } 338 else if (Pageview.IsVisualEditorMode) 339 { 340 <div class="alert alert-dark m-0">@Translate("No products available")</div> 341 } 342
Out of stock
Error executing template "Designs/Swift/Paragraph/Swift_ProductDetailsGallery.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_0edd2477279e4c8f946795e946e2c330.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 @using System.Text.RegularExpressions; 6 7 @functions { 8 public ProductViewModel product { get; set; } = new ProductViewModel(); 9 public string galleryLayout { get; set; } 10 public string[] supportedImageFormats { get; set; } 11 public string[] supportedVideoFormats { get; set; } 12 public string[] supportedDocumentFormats { get; set; } 13 public string[] allSupportedFormats { get; set; } 14 15 public class RatioSettings { 16 public string Ratio { get; set; } 17 public string CssClass { get; set; } 18 public string CssVariable { get; set; } 19 public string Fill { get; set; } 20 } 21 22 public RatioSettings GetRatioSettings(string size = "desktop") { 23 var ratioSettings = new RatioSettings(); 24 25 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 26 ratio = ratio != "0" ? ratio : ""; 27 string cssClass = ratio != "" && ratio != "fill" ? " ratio" : ""; 28 string cssVariable = ratio != "" && ratio != "fill" ? "--bs-aspect-ratio: " + ratio : ""; 29 cssClass = ratio != "" && ratio == "fill" && size == "mobile" ? " ratio" : cssClass; 30 cssVariable = ratio != "" && ratio == "fill" && size == "mobile" ? "--bs-aspect-ratio: 66%" : cssVariable; 31 32 ratioSettings.Ratio = ratio; 33 ratioSettings.CssClass = cssClass; 34 ratioSettings.CssVariable = cssVariable; 35 ratioSettings.Fill = ratio == "fill" ? " h-100" : ""; 36 37 return ratioSettings; 38 } 39 40 public string GetColumnClass(int total, int assetNumber) { 41 string colClass = total > 1 ? "g-col-lg-6" : "g-col-12"; 42 colClass = galleryLayout == "full-first" && assetNumber == 0 ? "g-col-12" : colClass; 43 colClass = galleryLayout == "full-last" && assetNumber == (total - 1) ? "g-col-12" : colClass; 44 colClass = galleryLayout == "advanced-grid" && assetNumber > 1 ? "g-col-4" : colClass; 45 46 colClass = galleryLayout == "advanced-grid" && total == 1 ? "g-col-12" : colClass; 47 colClass = galleryLayout == "advanced-grid" && total == 3 && assetNumber == 2 ? "g-col-12" : colClass; 48 colClass = galleryLayout == "advanced-grid" && total == 4 && assetNumber == 2 ? "g-col-6" : colClass; 49 colClass = galleryLayout == "advanced-grid" && total == 4 && assetNumber == 3 ? "g-col-6" : colClass; 50 colClass = galleryLayout == "advanced-grid" && total == 6 && assetNumber == 5 ? "g-col-12" : colClass; 51 colClass = galleryLayout == "advanced-grid" && total == 7 && assetNumber == 5 ? "g-col-6" : colClass; 52 colClass = galleryLayout == "advanced-grid" && total == 7 && assetNumber == 6 ? "g-col-6" : colClass; 53 colClass = galleryLayout == "advanced-grid" && total == 9 && assetNumber == 8 ? "g-col-12" : colClass; 54 55 return colClass; 56 } 57 58 public string GetArrowsColor() 59 { 60 var invertColor = Model.Item.GetBoolean("InvertModalArrowsColor"); 61 var arrowsColor = invertColor ? " carousel-dark" : string.Empty; 62 return arrowsColor; 63 } 64 65 public Dictionary<string, object> GetVideoParams(MediaViewModel asset, string size) 66 { 67 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Name; 68 string type = GetVideoType(asset.Value); 69 bool openInModal = Model.Item.GetString("OpenVideoInModal") == "true" ? true : false; 70 bool autoPlay = Model.Item.GetBoolean("VideoAutoPlay"); 71 72 var videoParams = new Dictionary<string, object>(); 73 videoParams.Add("AssetName", asset.Name); 74 videoParams.Add("AssetVideoType", type); 75 videoParams.Add("AssetDisplayName", asset.DisplayName); 76 videoParams.Add("OpenVideoInModal", openInModal); 77 videoParams.Add("VideoAutoPlay", autoPlay); 78 videoParams.Add("Size", size); 79 videoParams.Add("Id", Model.ID); 80 return videoParams; 81 } 82 83 public string GetVideoType(string assetValue) 84 { 85 string type = assetValue.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "youtube" : string.Empty; 86 type = assetValue.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "vimeo" : type; 87 type = string.IsNullOrEmpty(type) ? "selfhosted" : type; 88 return type; 89 } 90 91 public string GetYoutubeScreenDump(string assetValue, string quality) 92 { 93 var regex = new Regex(@"(?:youtube\.com\/.*[\?&]v=|youtu\.be\/|youtube\.com\/embed\/)([\w-]+)(?:\?.*)?"); 94 Match match = regex.Match(assetValue); 95 string videoId = match.Success ? match.Groups[1].Value : string.Empty; 96 string youtubeThumbnail = $"https://img.youtube.com/vi/{videoId}/{quality}.jpg"; 97 return youtubeThumbnail; 98 } 99 } 100 101 @{ 102 @* Get the product data *@ 103 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 104 { 105 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 106 } 107 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 108 { 109 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 110 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 111 112 if (productList?.Products is object) 113 { 114 product = productList.Products[0]; 115 } 116 } 117 } 118 119 @if (product is object) 120 { 121 @* Supported formats *@ 122 supportedImageFormats = new string[] { ".jpg", ".jpeg", ".webp", ".png", ".gif", ".bmp", ".tiff" }; 123 supportedVideoFormats = new string[] { "youtu.be", "youtube", "vimeo", ".mp4", ".webm" }; 124 supportedDocumentFormats = new string[] { ".pdf", ".docx", ".xlsx", ".ppt", "pptx" }; 125 allSupportedFormats = supportedImageFormats.Concat(supportedVideoFormats).Concat(supportedDocumentFormats).ToArray(); 126 127 @* Collect the assets *@ 128 var selectedAssetCategories = Model.Item.GetList("ImageAssets")?.GetRawValue().OfType<string>(); 129 bool includeImagePatternImages = Model.Item.GetBoolean("ImagePatternImages"); 130 131 @* Needed image data collection to support both DefaultImage, ImagePatterns and Image Assets *@ 132 string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : ""; 133 IEnumerable<MediaViewModel> assetsImages = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets); 134 assetsImages = assetsImages.OrderByDescending(x => x.Value.Equals(defaultImage)); 135 IEnumerable<MediaViewModel> assetsList = new MediaViewModel[]{}; 136 assetsList = assetsList.Union(assetsImages); 137 assetsList = includeImagePatternImages ? assetsList.Union(product.ImagePatternImages) : assetsList; 138 assetsList = includeImagePatternImages && assetsList.Count() == 0 ? assetsList.Append(product.DefaultImage) : assetsList; 139 140 bool defaultImageFallback = Model.Item.GetBoolean("DefaultImageFallback"); 141 142 int totalAssets = 0; 143 foreach (MediaViewModel asset in assetsList) { 144 var assetValue = asset.Value; 145 foreach (string format in allSupportedFormats) { 146 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 147 totalAssets++; 148 } 149 } 150 } 151 152 if (totalAssets == 0) 153 { 154 if (defaultImageFallback) { 155 assetsList = new List<MediaViewModel>(){ product.DefaultImage }; 156 totalAssets = 1; 157 } else { 158 assetsList = new List<MediaViewModel>(){ }; 159 totalAssets = 0; 160 } 161 } 162 163 @* Layout settings *@ 164 string spacing = Model.Item.GetRawValueString("Spacing", "4"); 165 spacing = spacing == "none" ? "gap-0" : spacing; 166 spacing = spacing == "small" ? "gap-3" : spacing; 167 spacing = spacing == "large" ? "gap-4" : spacing; 168 169 galleryLayout = Model.Item.GetRawValueString("Layout", "grid"); 170 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 171 172 var badgeParms = new Dictionary<string, object>(); 173 badgeParms.Add("size", "h5"); 174 badgeParms.Add("saleBadgeType", Model.Item.GetRawValue("SaleBadgeType")); 175 badgeParms.Add("saleBadgeCssClassName", Model.Item.GetRawValue("SaleBadgeDesign")); 176 badgeParms.Add("newBadgeCssClassName", Model.Item.GetRawValue("NewBadgeDesign")); 177 badgeParms.Add("newPublicationDays", Model.Item.GetInt32("NewPublicationDays")); 178 badgeParms.Add("campaignBadgesValues", Model.Item.GetList("CampaignBadges")?.GetRawValue().OfType<string>().ToList()); 179 180 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("SaleBadgeDesign")) && Model.Item.GetRawValueString("SaleBadgeDesign") != "none" ? true : false; 181 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("NewBadgeDesign")) && Model.Item.GetRawValueString("NewBadgeDesign") != "none" ? true : false; 182 DateTime createdDate = product.Created.Value; 183 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false; 184 showBadges = (newBadgeEnabled && Model.Item.GetInt32("NewPublicationDays") == 0) || (newBadgeEnabled && (createdDate.AddDays(Model.Item.GetInt32("NewPublicationDays")) > DateTime.Now)) ? true : showBadges; 185 showBadges = !string.IsNullOrEmpty(Model.Item.GetRawValueString("CampaignBadges")) ? true : showBadges; 186 187 188 @* Get assets from selected categories or get all assets *@ 189 if (totalAssets != 0 && assetsList.Count() != 0) { 190 int desktopAssetNumber = 0; 191 int mobileAssetNumber = 0; 192 int mobileAssetThumbnailNumber = 0; 193 int modalAssetNumber = 0; 194 195 @* Desktop: Show the gallery on large screens *@ 196 <div class="d-none d-lg-block h-100 position-relative @theme item_@Model.Item.SystemName.ToLower() desktop"> 197 <div class="grid @spacing"> 198 @foreach (MediaViewModel asset in assetsList) { 199 var assetName = asset.Value; 200 foreach (string format in allSupportedFormats) { 201 if (assetName.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 202 string size = "desktop"; 203 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 204 string assetValue = asset.Value; 205 206 <div class="@GetColumnClass(totalAssets, desktopAssetNumber)"> 207 <div class="h-100 @(imageTheme)"> 208 @foreach (string imageFormat in supportedImageFormats) 209 { //Images 210 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 211 { 212 string productName = product.Name; 213 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 214 string imageLinkPath = Uri.EscapeDataString(imagePath); 215 216 RatioSettings ratioSettings = GetRatioSettings(size); 217 218 var parms = new Dictionary<string, object>(); 219 parms.Add("alt", productName); 220 parms.Add("itemprop", "image"); 221 if (totalAssets > 1) 222 { 223 parms.Add("columns", 2); 224 } 225 else 226 { 227 parms.Add("columns", 1); 228 } 229 parms.Add("eagerLoadNewImages", Model.Item.GetBoolean("DisableLazyLoading")); 230 parms.Add("doNotUseGetimage", Model.Item.GetBoolean("DisableGetImage")); 231 232 if (!string.IsNullOrEmpty(asset.DisplayName)) 233 { 234 parms.Add("title", asset.DisplayName); 235 } 236 237 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") 238 { 239 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 240 } 241 else 242 { 243 parms.Add("cssClass", "mw-100 mh-100"); 244 } 245 246 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 247 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@desktopAssetNumber"> 248 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 249 </div> 250 </a> 251 } 252 } 253 @foreach (string videoFormat in supportedVideoFormats) 254 { //Videos 255 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 256 { 257 if (Model.Item.GetString("OpenVideoInModal") == "true") 258 { 259 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 260 261 string type = GetVideoType(asset.Value); 262 string videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeUriString(asset.Value) : string.Empty; 263 videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value, "maxresdefault") : videoScreendumpPath; 264 string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : string.Empty; 265 266 string productName = product.Name; 267 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 268 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 269 270 RatioSettings ratioSettings = GetRatioSettings(size); 271 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable); cursor: pointer" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 272 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@desktopAssetNumber"> 273 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 274 @if (type != "selfhosted") 275 { 276 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@productName" @assetTitle class="@videoJsClass mw-100 mh-100" data-asset-value="@asset.Value" style="object-fit: cover;" /> 277 } 278 else 279 { 280 string videoType = Path.GetExtension(asset.Value).ToLower(); 281 282 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 283 <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> 284 </video> 285 } 286 </div> 287 </div> 288 289 } 290 else 291 { 292 var videoParams = GetVideoParams(asset, size); 293 @RenderPartial("Components/VideoPlayer.cshtml", new FileViewModel { Path = asset.Value }, videoParams); 294 } 295 } 296 } 297 @foreach (string documentFormat in supportedDocumentFormats) 298 { //Documents 299 if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) 300 { 301 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 302 303 string productName = product.Name; 304 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 305 string imageLinkPath = Uri.EscapeDataString(imagePath); 306 307 RatioSettings ratioSettings = GetRatioSettings(size); 308 309 var parms = new Dictionary<string, object>(); 310 parms.Add("alt", productName); 311 parms.Add("itemprop", "image"); 312 parms.Add("fullwidth", true); 313 parms.Add("columns", Model.GridRowColumnCount); 314 if (!string.IsNullOrEmpty(asset.DisplayName)) 315 { 316 parms.Add("title", asset.DisplayName); 317 } 318 319 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") 320 { 321 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 322 } 323 else 324 { 325 parms.Add("cssClass", "mw-100 mh-100"); 326 } 327 328 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download title="@Translate("Download")"> 329 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 330 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 331 @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) 332 { 333 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 334 } 335 else 336 { 337 338 } 339 </div> 340 </a> 341 } 342 } 343 </div> 344 </div> 345 desktopAssetNumber++; 346 } 347 } 348 } 349 </div> 350 351 @if (showBadges) { 352 <div class="position-absolute top-0 left-0 p-2 p-lg-3 w-100"> 353 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms) 354 </div> 355 } 356 </div> 357 358 @* Mobile: Show the thumbs on small screens *@ 359 <div class="d-block d-lg-none mx-lg-0 position-relative @theme item_@Model.Item.SystemName.ToLower() mobile"> 360 <div id="SmallScreenImages_@Model.ID" class="carousel@(GetArrowsColor())" data-bs-ride="carousel"> 361 <div class="carousel-inner h-100"> 362 @foreach (MediaViewModel asset in assetsList) { 363 var assetValue = asset.Value; 364 foreach (string format in allSupportedFormats) { 365 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 366 string activeSlide = mobileAssetNumber == 0 ? "active" : ""; 367 string size = "mobile"; 368 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 369 370 <div class="carousel-item @activeSlide" data-bs-interval="99999"> 371 <div class="h-100 @(imageTheme)"> 372 @foreach (string imageFormat in supportedImageFormats) 373 { //Images 374 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 375 { 376 string productName = product.Name; 377 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 378 string imageLinkPath = Uri.EscapeDataString(imagePath); 379 380 RatioSettings ratioSettings = GetRatioSettings(size); 381 382 var parms = new Dictionary<string, object>(); 383 parms.Add("alt", productName); 384 parms.Add("itemprop", "image"); 385 if (totalAssets > 1) 386 { 387 parms.Add("columns", 2); 388 } 389 else 390 { 391 parms.Add("columns", 1); 392 } 393 parms.Add("eagerLoadNewImages", Model.Item.GetBoolean("DisableLazyLoading")); 394 parms.Add("doNotUseGetimage", Model.Item.GetBoolean("DisableGetImage")); 395 396 if (!string.IsNullOrEmpty(asset.DisplayName)) 397 { 398 parms.Add("title", asset.DisplayName); 399 } 400 401 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") 402 { 403 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 404 } 405 else 406 { 407 parms.Add("cssClass", "mw-100 mh-100"); 408 } 409 410 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 411 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@mobileAssetNumber"> 412 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 413 </div> 414 </a> 415 } 416 } 417 @foreach (string videoFormat in supportedVideoFormats) 418 { //Videos 419 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 420 { 421 if (Model.Item.GetString("OpenVideoInModal") == "true") 422 { 423 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 424 425 string type = GetVideoType(asset.Value); 426 427 string videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeUriString(asset.Value) : string.Empty; 428 videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value, "maxresdefault") : videoScreendumpPath; 429 string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : ""; 430 431 string productName = product.Name; 432 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 433 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 434 435 RatioSettings ratioSettings = GetRatioSettings(size); 436 437 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable); cursor: pointer" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 438 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@desktopAssetNumber"> 439 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 440 @if (type != "selfhosted") 441 { 442 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@productName" @assetTitle class="@videoJsClass mw-100 mh-100" data-asset-value="@asset.Value" style="object-fit: cover;" > 443 } 444 else 445 { 446 string videoType = Path.GetExtension(asset.Value).ToLower(); 447 448 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 449 <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> 450 </video> 451 } 452 </div> 453 </div> 454 } 455 else 456 { 457 Dictionary<string, object> videoParams = GetVideoParams(asset, size); 458 @RenderPartial("Components/VideoPlayer.cshtml", new FileViewModel { Path = asset.Value }, videoParams); 459 } 460 } 461 } 462 </div> 463 </div> 464 mobileAssetNumber++; 465 } 466 } 467 } 468 </div> 469 </div> 470 471 @if (totalAssets > 1) { 472 <div id="SmallScreenImagesThumbnails_@Model.ID" class="d-flex flex-nowrap gap-2 overflow-x-auto my-3"> 473 @foreach (MediaViewModel asset in assetsList) { 474 var assetValue = asset.Value; 475 foreach (string format in allSupportedFormats) { 476 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 477 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 478 string type = GetVideoType(asset.Value); 479 480 string videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value, "maxresdefault") : string.Empty; 481 videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeUriString(asset.Value) : videoScreendumpPath; 482 string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : string.Empty; 483 484 string productName = product.Name; 485 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 486 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 487 488 <div class="ratio ratio-4x3 border outline-none" style="flex:0 0 80px" data-bs-target="#SmallScreenImages_@Model.ID" data-bs-slide-to="@mobileAssetThumbnailNumber"> 489 @foreach (string imageFormat in supportedImageFormats) 490 { //Images 491 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 492 { 493 string imagePath = !string.IsNullOrEmpty(asset.Value) ? $"/Admin/Public/GetImage.ashx?image={asset.Value}&width=180&format=webp" : string.Empty; 494 <img src="@imagePath" class="p-1 mw-100 mh-100" style="object-fit: cover;" alt="@productName" @assetTitle > 495 } 496 } 497 @foreach (string videoFormat in supportedVideoFormats) 498 { //Videos 499 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 500 { 501 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 502 <div class="icon-3 position-absolute text-light" style="z-index: 1">@ReadFile(iconPath+"play-circle.svg")</div> 503 </div> 504 if (type != "selfhosted") 505 { 506 507 <img src="@(videoScreendumpPath)" class="p-1 @videoJsClass mw-100 mh-100" data-asset-value="@asset.Value" style="object-fit: cover;" alt="@productName" @assetTitle> 508 509 } 510 else 511 { 512 string videoType = Path.GetExtension(asset.Value).ToLower(); 513 514 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 515 <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> 516 </video> 517 } 518 } 519 } 520 @foreach (string documentFormat in supportedDocumentFormats) 521 { //Documents 522 if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) 523 { 524 string imagePath = !string.IsNullOrEmpty(asset.Value) ? $"/Admin/Public/GetImage.ashx?image={asset.Value}&width=180&format=webp" : string.Empty; 525 526 <a href="@Uri.EscapeDataString(assetValue)" style="cursor: pointer; min-width: 7rem; max-width: 8rem;" download title="@asset.Value"> 527 @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) 528 { 529 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 530 <div class="icon-3 position-absolute text-light" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 531 </div> 532 <img src="@imagePath" alt="@productName" @assetTitle class="p-0 p-lg-1 mw-100 mh-100" style="object-fit: cover;"> 533 } 534 else 535 { 536 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 537 <div class="icon-3 position-absolute" style="z-index: 1">@ReadFile(iconPath + "file-text.svg")</div> 538 </div> 539 } 540 </a> 541 } 542 } 543 </div> 544 545 mobileAssetThumbnailNumber++; 546 } 547 } 548 } 549 </div> 550 } 551 552 @if (showBadges) { 553 <div class="position-absolute top-0 left-0 p-2 p-lg-3"> 554 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms) 555 </div> 556 } 557 </div> 558 559 @* Modal with slides *@ 560 <div class="modal fade swift_products-details-images-modal" id="modal_@Model.ID" tabindex="-1" aria-labelledby="productDetailsGalleryModalTitle_@Model.ID" aria-hidden="true"> 561 <div class="modal-dialog modal-dialog-centered modal-xl"> 562 <div class="modal-content"> 563 <div class="modal-header visually-hidden"> 564 <h5 class="modal-title" id="productDetailsGalleryModalTitle_@Model.ID">@product.Title</h5> 565 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 566 </div> 567 <div class="modal-body p-2 p-lg-3 h-100"> 568 <div id="ModalCarousel_@Model.ID" class="carousel@(GetArrowsColor()) h-100" data-bs-ride="carousel"> 569 <div class="carousel-inner h-100 @theme"> 570 @foreach (MediaViewModel asset in assetsList) { 571 var assetValue = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 572 foreach (string format in allSupportedFormats) { 573 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 574 string imagePath = assetValue; 575 string activeSlide = modalAssetNumber == 0 ? "active" : ""; 576 577 var parms = new Dictionary<string, object>(); 578 parms.Add("cssClass", "d-block mw-100 mh-100 m-auto"); 579 parms.Add("columns", Model.GridRowColumnCount); 580 parms.Add("eagerLoadNewImages", Model.Item.GetBoolean("DisableLazyLoading")); 581 parms.Add("doNotUseGetimage", Model.Item.GetBoolean("DisableGetImage")); 582 583 <div class="carousel-item @activeSlide h-100" data-bs-interval="99999"> 584 @foreach (string imageFormat in supportedImageFormats) { 585 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) { 586 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 587 } 588 } 589 590 @foreach (string videoFormat in supportedVideoFormats) { 591 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) { 592 593 Dictionary<string, object> videoParams = GetVideoParams(asset, "modal"); 594 @RenderPartial("Components/VideoPlayer.cshtml", new FileViewModel { Path = asset.Value }, videoParams); 595 596 } 597 } 598 </div> 599 600 modalAssetNumber++; 601 } 602 } 603 } 604 <button class="carousel-control-prev carousel-control-area" type="button" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide="prev"> 605 <span class="carousel-control-prev-icon" aria-hidden="true"></span> 606 <span class="visually-hidden">@Translate("Previous")</span> 607 </button> 608 <button class="carousel-control-next carousel-control-area" type="button" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide="next"> 609 <span class="carousel-control-next-icon" aria-hidden="true"></span> 610 <span class="visually-hidden">@Translate("Next")</span> 611 </button> 612 </div> 613 </div> 614 </div> 615 </div> 616 </div> 617 </div> 618 } else if (Pageview.IsVisualEditorMode) { 619 RatioSettings ratioSettings = GetRatioSettings("desktop"); 620 621 <div class="h-100 @theme"> 622 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> 623 <img src="/Files/Images/missing_image.jpg" loading="lazy" decoding="async" class="mh-100 mw-100" style="object-fit: cover;" alt="@Translate("Missing image")"> 624 </div> 625 </div> 626 } 627 } 628
Error executing template "Designs/Swift/Paragraph/Swift_Accordion.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_e9846f716ed5469fb728ae88e7ae0ada.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.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.DynamicWrapperService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.RunCompile(IRazorEngineService service, String name, 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 RazorEngine.Templating.RazorEngineServiceExtensions.RunCompile(IRazorEngineService service, String name, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.RunCompile(IRazorEngineService service, String templateSource, 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 3 @functions 4 { 5 private string RenderCustomCSS(IEnumerable<string> decorations) 6 { 7 var cssClasses = new List<string>(); 8 9 foreach (var itemId in decorations) 10 { 11 var item = Dynamicweb.Content.Services.Items.GetItem("Swift_Css", itemId); 12 item.TryGetValue("Class", out object classes); 13 14 if (classes is null) 15 { 16 continue; 17 } 18 19 var cssString = (string)classes; 20 21 if (cssString.StartsWith("[")) 22 { 23 var cssArray = Dynamicweb.Core.Converter.Deserialize<string[]>(cssString); 24 cssClasses.AddRange(cssArray); 25 } 26 else 27 { 28 cssClasses.Add(cssString.Replace(",", " ")); 29 } 30 } 31 32 return string.Join(" ", cssClasses).Trim(); 33 } 34 } 35 36 @{ 37 string title = Model.Item.GetString("Title"); 38 var accordionItems = Model.Item?.GetItems("Accordion_Items") ?? Enumerable.Empty<Dynamicweb.Frontend.ItemViewModel>().ToList(); 39 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 40 var decorations = Model.Item?.GetList("CssDecorations")?.GetRawValue().OfType<string>() ?? Enumerable.Empty<string>(); 41 string css = RenderCustomCSS(decorations); 42 string contentPadding = Model.Item.GetRawValueString("ContentPadding", "none"); 43 contentPadding = contentPadding == "none" ? "p-0" : contentPadding; 44 contentPadding = contentPadding == "small" ? "p-3 p-md-3" : contentPadding; 45 contentPadding = contentPadding == "large" ? "p-5 p-md-5" : contentPadding; 46 } 47 48 <div class="accordion @(theme) @(css) @(contentPadding) h-100 position-relative item_@Model.Item.SystemName.ToLower()" id="accordion-@Pageview.CurrentParagraph.ID"> 49 @if (!string.IsNullOrEmpty(Model.Item.GetString("Title")) && !Model.Item.GetBoolean("HideTitle")) 50 { 51 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "h2"); 52 string headingLevel = Model.Item.GetString("HeadingLevel", "h2"); 53 string headingLevelStart = $"<{headingLevel} class=\"{titleFontSize} mb-4\">"; 54 string headingLevelStop = $"</{headingLevel}>"; 55 56 @headingLevelStart 57 @Model.Item.GetString("Title") 58 @headingLevelStop 59 } 60 @foreach (var item in accordionItems) 61 { 62 <div class="accordion-item"> 63 <h2 class="accordion-header" id="heading-@Pageview.CurrentParagraph.ID-@item.Id"> 64 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-@Pageview.CurrentParagraph.ID-@item.Id" aria-expanded="false" aria-controls="collapse-@Pageview.CurrentParagraph.ID-@item.Id"> 65 @item.GetString("Title") 66 </button> 67 </h2> 68 <div id="collapse-@Pageview.CurrentParagraph.ID-@item.Id" class="accordion-collapse collapse" aria-labelledby="heading-@Pageview.CurrentParagraph.ID-@item.Id" data-bs-parent="#accordion-@Pageview.CurrentParagraph.ID"> 69 <div class="accordion-body mb-0-last-child opacity-75"> 70 @item.GetRawValueString("Content") 71 </div> 72 </div> 73 </div> 74 } 75 <div id="@Model.ID" class="user-select-none position-absolute top-0" style="scroll-margin-top:var(--header-height,150px)"></div> 76 </div> 77
Error executing template "Designs/Swift/Paragraph/Swift_ProductDetailsGallery.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_0edd2477279e4c8f946795e946e2c330.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 @using System.Text.RegularExpressions; 6 7 @functions { 8 public ProductViewModel product { get; set; } = new ProductViewModel(); 9 public string galleryLayout { get; set; } 10 public string[] supportedImageFormats { get; set; } 11 public string[] supportedVideoFormats { get; set; } 12 public string[] supportedDocumentFormats { get; set; } 13 public string[] allSupportedFormats { get; set; } 14 15 public class RatioSettings { 16 public string Ratio { get; set; } 17 public string CssClass { get; set; } 18 public string CssVariable { get; set; } 19 public string Fill { get; set; } 20 } 21 22 public RatioSettings GetRatioSettings(string size = "desktop") { 23 var ratioSettings = new RatioSettings(); 24 25 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 26 ratio = ratio != "0" ? ratio : ""; 27 string cssClass = ratio != "" && ratio != "fill" ? " ratio" : ""; 28 string cssVariable = ratio != "" && ratio != "fill" ? "--bs-aspect-ratio: " + ratio : ""; 29 cssClass = ratio != "" && ratio == "fill" && size == "mobile" ? " ratio" : cssClass; 30 cssVariable = ratio != "" && ratio == "fill" && size == "mobile" ? "--bs-aspect-ratio: 66%" : cssVariable; 31 32 ratioSettings.Ratio = ratio; 33 ratioSettings.CssClass = cssClass; 34 ratioSettings.CssVariable = cssVariable; 35 ratioSettings.Fill = ratio == "fill" ? " h-100" : ""; 36 37 return ratioSettings; 38 } 39 40 public string GetColumnClass(int total, int assetNumber) { 41 string colClass = total > 1 ? "g-col-lg-6" : "g-col-12"; 42 colClass = galleryLayout == "full-first" && assetNumber == 0 ? "g-col-12" : colClass; 43 colClass = galleryLayout == "full-last" && assetNumber == (total - 1) ? "g-col-12" : colClass; 44 colClass = galleryLayout == "advanced-grid" && assetNumber > 1 ? "g-col-4" : colClass; 45 46 colClass = galleryLayout == "advanced-grid" && total == 1 ? "g-col-12" : colClass; 47 colClass = galleryLayout == "advanced-grid" && total == 3 && assetNumber == 2 ? "g-col-12" : colClass; 48 colClass = galleryLayout == "advanced-grid" && total == 4 && assetNumber == 2 ? "g-col-6" : colClass; 49 colClass = galleryLayout == "advanced-grid" && total == 4 && assetNumber == 3 ? "g-col-6" : colClass; 50 colClass = galleryLayout == "advanced-grid" && total == 6 && assetNumber == 5 ? "g-col-12" : colClass; 51 colClass = galleryLayout == "advanced-grid" && total == 7 && assetNumber == 5 ? "g-col-6" : colClass; 52 colClass = galleryLayout == "advanced-grid" && total == 7 && assetNumber == 6 ? "g-col-6" : colClass; 53 colClass = galleryLayout == "advanced-grid" && total == 9 && assetNumber == 8 ? "g-col-12" : colClass; 54 55 return colClass; 56 } 57 58 public string GetArrowsColor() 59 { 60 var invertColor = Model.Item.GetBoolean("InvertModalArrowsColor"); 61 var arrowsColor = invertColor ? " carousel-dark" : string.Empty; 62 return arrowsColor; 63 } 64 65 public Dictionary<string, object> GetVideoParams(MediaViewModel asset, string size) 66 { 67 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Name; 68 string type = GetVideoType(asset.Value); 69 bool openInModal = Model.Item.GetString("OpenVideoInModal") == "true" ? true : false; 70 bool autoPlay = Model.Item.GetBoolean("VideoAutoPlay"); 71 72 var videoParams = new Dictionary<string, object>(); 73 videoParams.Add("AssetName", asset.Name); 74 videoParams.Add("AssetVideoType", type); 75 videoParams.Add("AssetDisplayName", asset.DisplayName); 76 videoParams.Add("OpenVideoInModal", openInModal); 77 videoParams.Add("VideoAutoPlay", autoPlay); 78 videoParams.Add("Size", size); 79 videoParams.Add("Id", Model.ID); 80 return videoParams; 81 } 82 83 public string GetVideoType(string assetValue) 84 { 85 string type = assetValue.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "youtube" : string.Empty; 86 type = assetValue.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "vimeo" : type; 87 type = string.IsNullOrEmpty(type) ? "selfhosted" : type; 88 return type; 89 } 90 91 public string GetYoutubeScreenDump(string assetValue, string quality) 92 { 93 var regex = new Regex(@"(?:youtube\.com\/.*[\?&]v=|youtu\.be\/|youtube\.com\/embed\/)([\w-]+)(?:\?.*)?"); 94 Match match = regex.Match(assetValue); 95 string videoId = match.Success ? match.Groups[1].Value : string.Empty; 96 string youtubeThumbnail = $"https://img.youtube.com/vi/{videoId}/{quality}.jpg"; 97 return youtubeThumbnail; 98 } 99 } 100 101 @{ 102 @* Get the product data *@ 103 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 104 { 105 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 106 } 107 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 108 { 109 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 110 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 111 112 if (productList?.Products is object) 113 { 114 product = productList.Products[0]; 115 } 116 } 117 } 118 119 @if (product is object) 120 { 121 @* Supported formats *@ 122 supportedImageFormats = new string[] { ".jpg", ".jpeg", ".webp", ".png", ".gif", ".bmp", ".tiff" }; 123 supportedVideoFormats = new string[] { "youtu.be", "youtube", "vimeo", ".mp4", ".webm" }; 124 supportedDocumentFormats = new string[] { ".pdf", ".docx", ".xlsx", ".ppt", "pptx" }; 125 allSupportedFormats = supportedImageFormats.Concat(supportedVideoFormats).Concat(supportedDocumentFormats).ToArray(); 126 127 @* Collect the assets *@ 128 var selectedAssetCategories = Model.Item.GetList("ImageAssets")?.GetRawValue().OfType<string>(); 129 bool includeImagePatternImages = Model.Item.GetBoolean("ImagePatternImages"); 130 131 @* Needed image data collection to support both DefaultImage, ImagePatterns and Image Assets *@ 132 string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : ""; 133 IEnumerable<MediaViewModel> assetsImages = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets); 134 assetsImages = assetsImages.OrderByDescending(x => x.Value.Equals(defaultImage)); 135 IEnumerable<MediaViewModel> assetsList = new MediaViewModel[]{}; 136 assetsList = assetsList.Union(assetsImages); 137 assetsList = includeImagePatternImages ? assetsList.Union(product.ImagePatternImages) : assetsList; 138 assetsList = includeImagePatternImages && assetsList.Count() == 0 ? assetsList.Append(product.DefaultImage) : assetsList; 139 140 bool defaultImageFallback = Model.Item.GetBoolean("DefaultImageFallback"); 141 142 int totalAssets = 0; 143 foreach (MediaViewModel asset in assetsList) { 144 var assetValue = asset.Value; 145 foreach (string format in allSupportedFormats) { 146 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 147 totalAssets++; 148 } 149 } 150 } 151 152 if (totalAssets == 0) 153 { 154 if (defaultImageFallback) { 155 assetsList = new List<MediaViewModel>(){ product.DefaultImage }; 156 totalAssets = 1; 157 } else { 158 assetsList = new List<MediaViewModel>(){ }; 159 totalAssets = 0; 160 } 161 } 162 163 @* Layout settings *@ 164 string spacing = Model.Item.GetRawValueString("Spacing", "4"); 165 spacing = spacing == "none" ? "gap-0" : spacing; 166 spacing = spacing == "small" ? "gap-3" : spacing; 167 spacing = spacing == "large" ? "gap-4" : spacing; 168 169 galleryLayout = Model.Item.GetRawValueString("Layout", "grid"); 170 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 171 172 var badgeParms = new Dictionary<string, object>(); 173 badgeParms.Add("size", "h5"); 174 badgeParms.Add("saleBadgeType", Model.Item.GetRawValue("SaleBadgeType")); 175 badgeParms.Add("saleBadgeCssClassName", Model.Item.GetRawValue("SaleBadgeDesign")); 176 badgeParms.Add("newBadgeCssClassName", Model.Item.GetRawValue("NewBadgeDesign")); 177 badgeParms.Add("newPublicationDays", Model.Item.GetInt32("NewPublicationDays")); 178 badgeParms.Add("campaignBadgesValues", Model.Item.GetList("CampaignBadges")?.GetRawValue().OfType<string>().ToList()); 179 180 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("SaleBadgeDesign")) && Model.Item.GetRawValueString("SaleBadgeDesign") != "none" ? true : false; 181 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("NewBadgeDesign")) && Model.Item.GetRawValueString("NewBadgeDesign") != "none" ? true : false; 182 DateTime createdDate = product.Created.Value; 183 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false; 184 showBadges = (newBadgeEnabled && Model.Item.GetInt32("NewPublicationDays") == 0) || (newBadgeEnabled && (createdDate.AddDays(Model.Item.GetInt32("NewPublicationDays")) > DateTime.Now)) ? true : showBadges; 185 showBadges = !string.IsNullOrEmpty(Model.Item.GetRawValueString("CampaignBadges")) ? true : showBadges; 186 187 188 @* Get assets from selected categories or get all assets *@ 189 if (totalAssets != 0 && assetsList.Count() != 0) { 190 int desktopAssetNumber = 0; 191 int mobileAssetNumber = 0; 192 int mobileAssetThumbnailNumber = 0; 193 int modalAssetNumber = 0; 194 195 @* Desktop: Show the gallery on large screens *@ 196 <div class="d-none d-lg-block h-100 position-relative @theme item_@Model.Item.SystemName.ToLower() desktop"> 197 <div class="grid @spacing"> 198 @foreach (MediaViewModel asset in assetsList) { 199 var assetName = asset.Value; 200 foreach (string format in allSupportedFormats) { 201 if (assetName.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 202 string size = "desktop"; 203 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 204 string assetValue = asset.Value; 205 206 <div class="@GetColumnClass(totalAssets, desktopAssetNumber)"> 207 <div class="h-100 @(imageTheme)"> 208 @foreach (string imageFormat in supportedImageFormats) 209 { //Images 210 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 211 { 212 string productName = product.Name; 213 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 214 string imageLinkPath = Uri.EscapeDataString(imagePath); 215 216 RatioSettings ratioSettings = GetRatioSettings(size); 217 218 var parms = new Dictionary<string, object>(); 219 parms.Add("alt", productName); 220 parms.Add("itemprop", "image"); 221 if (totalAssets > 1) 222 { 223 parms.Add("columns", 2); 224 } 225 else 226 { 227 parms.Add("columns", 1); 228 } 229 parms.Add("eagerLoadNewImages", Model.Item.GetBoolean("DisableLazyLoading")); 230 parms.Add("doNotUseGetimage", Model.Item.GetBoolean("DisableGetImage")); 231 232 if (!string.IsNullOrEmpty(asset.DisplayName)) 233 { 234 parms.Add("title", asset.DisplayName); 235 } 236 237 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") 238 { 239 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 240 } 241 else 242 { 243 parms.Add("cssClass", "mw-100 mh-100"); 244 } 245 246 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 247 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@desktopAssetNumber"> 248 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 249 </div> 250 </a> 251 } 252 } 253 @foreach (string videoFormat in supportedVideoFormats) 254 { //Videos 255 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 256 { 257 if (Model.Item.GetString("OpenVideoInModal") == "true") 258 { 259 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 260 261 string type = GetVideoType(asset.Value); 262 string videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeUriString(asset.Value) : string.Empty; 263 videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value, "maxresdefault") : videoScreendumpPath; 264 string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : string.Empty; 265 266 string productName = product.Name; 267 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 268 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 269 270 RatioSettings ratioSettings = GetRatioSettings(size); 271 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable); cursor: pointer" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 272 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@desktopAssetNumber"> 273 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 274 @if (type != "selfhosted") 275 { 276 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@productName" @assetTitle class="@videoJsClass mw-100 mh-100" data-asset-value="@asset.Value" style="object-fit: cover;" /> 277 } 278 else 279 { 280 string videoType = Path.GetExtension(asset.Value).ToLower(); 281 282 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 283 <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> 284 </video> 285 } 286 </div> 287 </div> 288 289 } 290 else 291 { 292 var videoParams = GetVideoParams(asset, size); 293 @RenderPartial("Components/VideoPlayer.cshtml", new FileViewModel { Path = asset.Value }, videoParams); 294 } 295 } 296 } 297 @foreach (string documentFormat in supportedDocumentFormats) 298 { //Documents 299 if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) 300 { 301 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 302 303 string productName = product.Name; 304 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 305 string imageLinkPath = Uri.EscapeDataString(imagePath); 306 307 RatioSettings ratioSettings = GetRatioSettings(size); 308 309 var parms = new Dictionary<string, object>(); 310 parms.Add("alt", productName); 311 parms.Add("itemprop", "image"); 312 parms.Add("fullwidth", true); 313 parms.Add("columns", Model.GridRowColumnCount); 314 if (!string.IsNullOrEmpty(asset.DisplayName)) 315 { 316 parms.Add("title", asset.DisplayName); 317 } 318 319 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") 320 { 321 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 322 } 323 else 324 { 325 parms.Add("cssClass", "mw-100 mh-100"); 326 } 327 328 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download title="@Translate("Download")"> 329 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 330 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 331 @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) 332 { 333 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 334 } 335 else 336 { 337 338 } 339 </div> 340 </a> 341 } 342 } 343 </div> 344 </div> 345 desktopAssetNumber++; 346 } 347 } 348 } 349 </div> 350 351 @if (showBadges) { 352 <div class="position-absolute top-0 left-0 p-2 p-lg-3 w-100"> 353 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms) 354 </div> 355 } 356 </div> 357 358 @* Mobile: Show the thumbs on small screens *@ 359 <div class="d-block d-lg-none mx-lg-0 position-relative @theme item_@Model.Item.SystemName.ToLower() mobile"> 360 <div id="SmallScreenImages_@Model.ID" class="carousel@(GetArrowsColor())" data-bs-ride="carousel"> 361 <div class="carousel-inner h-100"> 362 @foreach (MediaViewModel asset in assetsList) { 363 var assetValue = asset.Value; 364 foreach (string format in allSupportedFormats) { 365 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 366 string activeSlide = mobileAssetNumber == 0 ? "active" : ""; 367 string size = "mobile"; 368 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 369 370 <div class="carousel-item @activeSlide" data-bs-interval="99999"> 371 <div class="h-100 @(imageTheme)"> 372 @foreach (string imageFormat in supportedImageFormats) 373 { //Images 374 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 375 { 376 string productName = product.Name; 377 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 378 string imageLinkPath = Uri.EscapeDataString(imagePath); 379 380 RatioSettings ratioSettings = GetRatioSettings(size); 381 382 var parms = new Dictionary<string, object>(); 383 parms.Add("alt", productName); 384 parms.Add("itemprop", "image"); 385 if (totalAssets > 1) 386 { 387 parms.Add("columns", 2); 388 } 389 else 390 { 391 parms.Add("columns", 1); 392 } 393 parms.Add("eagerLoadNewImages", Model.Item.GetBoolean("DisableLazyLoading")); 394 parms.Add("doNotUseGetimage", Model.Item.GetBoolean("DisableGetImage")); 395 396 if (!string.IsNullOrEmpty(asset.DisplayName)) 397 { 398 parms.Add("title", asset.DisplayName); 399 } 400 401 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") 402 { 403 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 404 } 405 else 406 { 407 parms.Add("cssClass", "mw-100 mh-100"); 408 } 409 410 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 411 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@mobileAssetNumber"> 412 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 413 </div> 414 </a> 415 } 416 } 417 @foreach (string videoFormat in supportedVideoFormats) 418 { //Videos 419 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 420 { 421 if (Model.Item.GetString("OpenVideoInModal") == "true") 422 { 423 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 424 425 string type = GetVideoType(asset.Value); 426 427 string videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeUriString(asset.Value) : string.Empty; 428 videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value, "maxresdefault") : videoScreendumpPath; 429 string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : ""; 430 431 string productName = product.Name; 432 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 433 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 434 435 RatioSettings ratioSettings = GetRatioSettings(size); 436 437 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable); cursor: pointer" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 438 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@desktopAssetNumber"> 439 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 440 @if (type != "selfhosted") 441 { 442 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@productName" @assetTitle class="@videoJsClass mw-100 mh-100" data-asset-value="@asset.Value" style="object-fit: cover;" > 443 } 444 else 445 { 446 string videoType = Path.GetExtension(asset.Value).ToLower(); 447 448 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 449 <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> 450 </video> 451 } 452 </div> 453 </div> 454 } 455 else 456 { 457 Dictionary<string, object> videoParams = GetVideoParams(asset, size); 458 @RenderPartial("Components/VideoPlayer.cshtml", new FileViewModel { Path = asset.Value }, videoParams); 459 } 460 } 461 } 462 </div> 463 </div> 464 mobileAssetNumber++; 465 } 466 } 467 } 468 </div> 469 </div> 470 471 @if (totalAssets > 1) { 472 <div id="SmallScreenImagesThumbnails_@Model.ID" class="d-flex flex-nowrap gap-2 overflow-x-auto my-3"> 473 @foreach (MediaViewModel asset in assetsList) { 474 var assetValue = asset.Value; 475 foreach (string format in allSupportedFormats) { 476 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 477 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 478 string type = GetVideoType(asset.Value); 479 480 string videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value, "maxresdefault") : string.Empty; 481 videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeUriString(asset.Value) : videoScreendumpPath; 482 string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : string.Empty; 483 484 string productName = product.Name; 485 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 486 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 487 488 <div class="ratio ratio-4x3 border outline-none" style="flex:0 0 80px" data-bs-target="#SmallScreenImages_@Model.ID" data-bs-slide-to="@mobileAssetThumbnailNumber"> 489 @foreach (string imageFormat in supportedImageFormats) 490 { //Images 491 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 492 { 493 string imagePath = !string.IsNullOrEmpty(asset.Value) ? $"/Admin/Public/GetImage.ashx?image={asset.Value}&width=180&format=webp" : string.Empty; 494 <img src="@imagePath" class="p-1 mw-100 mh-100" style="object-fit: cover;" alt="@productName" @assetTitle > 495 } 496 } 497 @foreach (string videoFormat in supportedVideoFormats) 498 { //Videos 499 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 500 { 501 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 502 <div class="icon-3 position-absolute text-light" style="z-index: 1">@ReadFile(iconPath+"play-circle.svg")</div> 503 </div> 504 if (type != "selfhosted") 505 { 506 507 <img src="@(videoScreendumpPath)" class="p-1 @videoJsClass mw-100 mh-100" data-asset-value="@asset.Value" style="object-fit: cover;" alt="@productName" @assetTitle> 508 509 } 510 else 511 { 512 string videoType = Path.GetExtension(asset.Value).ToLower(); 513 514 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 515 <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> 516 </video> 517 } 518 } 519 } 520 @foreach (string documentFormat in supportedDocumentFormats) 521 { //Documents 522 if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) 523 { 524 string imagePath = !string.IsNullOrEmpty(asset.Value) ? $"/Admin/Public/GetImage.ashx?image={asset.Value}&width=180&format=webp" : string.Empty; 525 526 <a href="@Uri.EscapeDataString(assetValue)" style="cursor: pointer; min-width: 7rem; max-width: 8rem;" download title="@asset.Value"> 527 @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) 528 { 529 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 530 <div class="icon-3 position-absolute text-light" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 531 </div> 532 <img src="@imagePath" alt="@productName" @assetTitle class="p-0 p-lg-1 mw-100 mh-100" style="object-fit: cover;"> 533 } 534 else 535 { 536 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 537 <div class="icon-3 position-absolute" style="z-index: 1">@ReadFile(iconPath + "file-text.svg")</div> 538 </div> 539 } 540 </a> 541 } 542 } 543 </div> 544 545 mobileAssetThumbnailNumber++; 546 } 547 } 548 } 549 </div> 550 } 551 552 @if (showBadges) { 553 <div class="position-absolute top-0 left-0 p-2 p-lg-3"> 554 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms) 555 </div> 556 } 557 </div> 558 559 @* Modal with slides *@ 560 <div class="modal fade swift_products-details-images-modal" id="modal_@Model.ID" tabindex="-1" aria-labelledby="productDetailsGalleryModalTitle_@Model.ID" aria-hidden="true"> 561 <div class="modal-dialog modal-dialog-centered modal-xl"> 562 <div class="modal-content"> 563 <div class="modal-header visually-hidden"> 564 <h5 class="modal-title" id="productDetailsGalleryModalTitle_@Model.ID">@product.Title</h5> 565 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 566 </div> 567 <div class="modal-body p-2 p-lg-3 h-100"> 568 <div id="ModalCarousel_@Model.ID" class="carousel@(GetArrowsColor()) h-100" data-bs-ride="carousel"> 569 <div class="carousel-inner h-100 @theme"> 570 @foreach (MediaViewModel asset in assetsList) { 571 var assetValue = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 572 foreach (string format in allSupportedFormats) { 573 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 574 string imagePath = assetValue; 575 string activeSlide = modalAssetNumber == 0 ? "active" : ""; 576 577 var parms = new Dictionary<string, object>(); 578 parms.Add("cssClass", "d-block mw-100 mh-100 m-auto"); 579 parms.Add("columns", Model.GridRowColumnCount); 580 parms.Add("eagerLoadNewImages", Model.Item.GetBoolean("DisableLazyLoading")); 581 parms.Add("doNotUseGetimage", Model.Item.GetBoolean("DisableGetImage")); 582 583 <div class="carousel-item @activeSlide h-100" data-bs-interval="99999"> 584 @foreach (string imageFormat in supportedImageFormats) { 585 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) { 586 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 587 } 588 } 589 590 @foreach (string videoFormat in supportedVideoFormats) { 591 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) { 592 593 Dictionary<string, object> videoParams = GetVideoParams(asset, "modal"); 594 @RenderPartial("Components/VideoPlayer.cshtml", new FileViewModel { Path = asset.Value }, videoParams); 595 596 } 597 } 598 </div> 599 600 modalAssetNumber++; 601 } 602 } 603 } 604 <button class="carousel-control-prev carousel-control-area" type="button" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide="prev"> 605 <span class="carousel-control-prev-icon" aria-hidden="true"></span> 606 <span class="visually-hidden">@Translate("Previous")</span> 607 </button> 608 <button class="carousel-control-next carousel-control-area" type="button" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide="next"> 609 <span class="carousel-control-next-icon" aria-hidden="true"></span> 610 <span class="visually-hidden">@Translate("Next")</span> 611 </button> 612 </div> 613 </div> 614 </div> 615 </div> 616 </div> 617 </div> 618 } else if (Pageview.IsVisualEditorMode) { 619 RatioSettings ratioSettings = GetRatioSettings("desktop"); 620 621 <div class="h-100 @theme"> 622 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> 623 <img src="/Files/Images/missing_image.jpg" loading="lazy" decoding="async" class="mh-100 mw-100" style="object-fit: cover;" alt="@Translate("Missing image")"> 624 </div> 625 </div> 626 } 627 } 628
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_a5279e288fb14b7d8c00ea464b295e88.ExecuteAsync()
   at RazorEngine.Templating.TemplateBase.Run(ExecuteContext context, TextWriter reader)
   at RazorEngine.Templating.RazorEngineCore.RunTemplate(ICompiledTemplate template, TextWriter writer, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.DynamicWrapperService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass23_0.<Run>b__0(TextWriter writer)
   at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, Type modelType, Object model, DynamicViewBag viewBag)
   at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template)
   at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template)
   at Dynamicweb.Rendering.Template.RenderRazorTemplate()

1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Core 3 @using Dynamicweb.Ecommerce.ProductCatalog 4 5 @{ 6 ProductViewModel product = null; 7 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 8 { 9 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 10 } 11 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 12 { 13 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 14 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 15 16 if (productList?.Products is object) 17 { 18 product = productList.Products[0]; 19 } 20 } 21 22 string title = Model?.Item?.GetRawValueString("Title", Translate("Products")); 23 string campaignValues = string.Join(",", Model.Item.GetList("CampaignBadges")?.GetRawValue().OfType<string>().ToList()); 24 25 //Styling 26 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "h3"); 27 string subtitleFontSize = Model.Item.GetRawValueString("SubtitleFontSize", "fs-5"); 28 string buttonStyle = Model.Item.GetRawValueString("ButtonStyle", ""); 29 buttonStyle = buttonStyle == "primary" ? " btn-primary" : buttonStyle; 30 buttonStyle = buttonStyle == "secondary" ? " btn-secondary" : buttonStyle; 31 buttonStyle = buttonStyle == "link" ? " btn-link" : buttonStyle; 32 string maxWidth = Model.Item.GetRawValueString("TextReadability", ""); 33 maxWidth = maxWidth == "max-width-on" ? " mw-75ch" : maxWidth; 34 maxWidth = maxWidth == "max-width-off" ? "" : maxWidth; 35 36 string generalTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("GeneralTheme")) ? " theme " + Model.Item.GetRawValueString("GeneralTheme").Replace(" ", "").Trim().ToLower() : ""; 37 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 38 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 39 40 //Link generation 41 string pageId = !string.IsNullOrEmpty(Model.Item.GetRawValueString("ProductSliderServicePage")) ? Model.Item.GetLink("ProductSliderServicePage").PageId.ToString() : ""; 42 if (string.IsNullOrEmpty(pageId)) 43 { 44 pageId = GetPageIdByNavigationTag("ProductSliderService").ToString(); 45 } 46 47 string url = "/Default.aspx?ID=" + pageId; 48 if (!url.Contains("LayoutTemplate", StringComparison.OrdinalIgnoreCase)) 49 { 50 url += url.Contains("?") ? "&LayoutTemplate=Designs/Swift/Swift_PageClean.cshtml" : "?LayoutTemplate=Designs/Swift/Swift_PageClean.cshtml"; 51 } 52 if (Pageview.IsVisualEditorMode) 53 { 54 url += "&VisualEdit=True"; 55 } 56 57 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]); 58 if (isLazyLoadingForProductInfoEnabled) 59 { 60 url += "&getproductinfo=true"; 61 } 62 63 //Source type 64 string sourceType = Model.Item.GetRawValueString("RelationType", "trending"); 65 IList<string> relateFromGroupIds = new List<string> { }; 66 IList<string> relateFromProductVariantIds = new List<string> { }; 67 IList<string> relateFromProductIds = new List<string> { }; 68 bool hasVariants = false; 69 70 //--- VARIANTS --- 71 if (sourceType == "variants" && Model.Item.GetValue<ProductListViewModel>("ProductsToRelateToVariants") is ProductListViewModel productsToRelateToVariants) 72 { 73 foreach (var productSelection in productsToRelateToVariants.Products) 74 { 75 relateFromProductIds.Add(productSelection.Id); 76 } 77 } 78 79 //--- MOST SOLD --- 80 if (sourceType == "most-sold" && Model.Item.GetValue<IList<ProductGroupViewModel>>("GroupsToRelateToMostSold") is IList<ProductGroupViewModel> groupsToRelateToMostSold) 81 { 82 foreach (var fromGroup in groupsToRelateToMostSold) 83 { 84 relateFromGroupIds.Add(fromGroup.Id); 85 } 86 } 87 88 //--- TRENDING --- 89 if (sourceType == "trending" && Model.Item.GetValue<IList<ProductGroupViewModel>>("GroupsToRelateToTrending") is IList<ProductGroupViewModel> groupsToRelateToTrending) 90 { 91 foreach (var fromGroup in groupsToRelateToTrending) 92 { 93 relateFromGroupIds.Add(fromGroup.Id); 94 } 95 } 96 97 //--- LATEST --- 98 if (sourceType == "latest" && Model.Item.GetValue<IList<ProductGroupViewModel>>("GroupsToRelateToLatest") is IList<ProductGroupViewModel> groupsToRelateToLatest) 99 { 100 foreach (var fromGroup in groupsToRelateToLatest) 101 { 102 relateFromGroupIds.Add(fromGroup.Id); 103 } 104 } 105 106 //--- FREQUENTLY BOUGHT --- 107 if (sourceType == "frequently" && Model.Item.GetValue<ProductListViewModel>("ProductsToRelateTo") is ProductListViewModel productsToRelateTo) 108 { 109 foreach (var fromProduct in productsToRelateTo.Products) 110 { 111 relateFromProductIds.Add(fromProduct.Id); 112 } 113 } 114 115 //--- SELECTED PRODUCTS --- 116 if ((sourceType == "selected" || sourceType == "frequently") && Model.Item.GetValue<ProductListViewModel>("Products") is ProductListViewModel products) 117 { 118 hasVariants = products.Products.Any(p => !string.IsNullOrEmpty(p.VariantId)); 119 foreach (var productSelection in products.Products) 120 { 121 if (hasVariants) 122 { 123 if (!string.IsNullOrEmpty(productSelection.VariantId)) 124 { 125 relateFromProductVariantIds.Add($"{productSelection.Id} {productSelection.VariantId}"); 126 } 127 else 128 { 129 relateFromProductVariantIds.Add($"{productSelection.Id}"); 130 } 131 } 132 relateFromProductIds.Add($"{productSelection.Id}"); 133 } 134 } 135 136 //--- RELATED PRODUCTS --- 137 if (sourceType == "related-products" && Model.Item.GetValue<ProductListViewModel>("ProductsToRelateTo2") is ProductListViewModel selectedRelationProduct) 138 { 139 if (selectedRelationProduct.Products.Any()) 140 { 141 product = selectedRelationProduct.Products.FirstOrDefault(); 142 } 143 144 if (product?.RelatedGroups != null) 145 { 146 foreach (var group in product.RelatedGroups) 147 { 148 foreach (var relatedProduct in group.Products) 149 { 150 if (!string.IsNullOrEmpty(relatedProduct.VariantId)) 151 { 152 relateFromProductVariantIds.Add($"{relatedProduct.ProductId} {relatedProduct.VariantId}"); 153 } 154 else 155 { 156 relateFromProductVariantIds.Add($"{relatedProduct.ProductId}"); 157 } 158 } 159 } 160 } 161 } 162 163 //Create group id collection and products id collection strings 164 string groupIds = product is object ? product.PrimaryOrDefaultGroup.Id : string.Join(",", relateFromGroupIds); 165 string productVariantIds = relateFromProductVariantIds.Count > 0 ? string.Join(",", relateFromProductVariantIds) : ""; 166 string productIds = product is object && relateFromProductIds.Count == 0 ? product.Id : string.Join(",", relateFromProductIds); 167 168 //Set the parameters to the url 169 string linkParameters = ""; 170 linkParameters += sourceType != "related-products" && sourceType != "frequently" && sourceType != "selected" ? "&GroupId=" + groupIds : ""; 171 linkParameters += !string.IsNullOrEmpty(productIds) && sourceType != "most-sold" && sourceType != "trending" && sourceType != "latest" && sourceType != "frequently" && sourceType != "related-products" ? "&MainProductId=" + productIds : ""; 172 linkParameters += !string.IsNullOrEmpty(productVariantIds) && sourceType == "related-products" ? "&ProductVariantId=" + productVariantIds : ""; 173 linkParameters += sourceType == "variants" ? "&IsVariant=true" : ""; 174 linkParameters += sourceType == "latest" ? "&SortBy=Created" : ""; 175 linkParameters += sourceType == "most-sold" ? "&SortBy=OrderCount" : ""; 176 linkParameters += sourceType == "trending" ? "&SortBy=OrderCountGrowth" : ""; 177 linkParameters += !string.IsNullOrEmpty(productIds) && sourceType == "frequently" ? $"&BoughtWithProductIds=[{productIds}]" : ""; 178 var productListPageId = GetPageIdByNavigationTag("Shop"); 179 string link = "/Default.aspx?ID=" + productListPageId + linkParameters; 180 181 // Slider settings (documentation: swiffyslider.com/configuration) 182 string navigationStyle = $"{Model.Item.GetRawValueString("NavigationStyle", "slider-nav-round")}"; 183 string navigationPlacement = $"{Model.Item.GetRawValueString("NavigationPlacement", "slider-nav-on-slides")}"; 184 string indicatorStyle = $"{Model.Item.GetRawValueString("IndicatorStyle", "slider-indicators-hidden")}"; 185 string revealSlides = Model.Item.GetRawValueString("RevealSlides", "no-reveal") == "reveal" ? "slider-item-reveal" : string.Empty; 186 string navigationAlwaysVisible = (Model.Item.GetBoolean("NavigationAlwaysVisible")) ? "slider-nav-visible" : string.Empty; 187 string navigationVisibleOnTouch = (Model.Item.GetBoolean("NavigationVisibleOnTouch")) ? "slider-nav-touch" : string.Empty; 188 string navigationShowScrollbar = (Model.Item.GetBoolean("NavigationShowScrollbar")) ? "slider-nav-scrollbar" : string.Empty; 189 string navigationSmall = (Model.Item.GetBoolean("NavigationSmall")) ? "slider-nav-sm" : string.Empty; 190 string navigationInvertColors = (Model.Item.GetBoolean("NavigationInvertColors")) ? "slider-nav-dark" : string.Empty; 191 string navigationSlideEntirePage = (Model.Item.GetBoolean("NavigationSlideEntirePage")) ? "slider-nav-page" : string.Empty; 192 string navigationNoLoop = (Model.Item.GetBoolean("NavigationNoLoop")) ? "slider-nav-noloop" : string.Empty; 193 string indicatorsOutsideSlider = (Model.Item.GetBoolean("IndicatorsOutsideSlider") && indicatorStyle != string.Empty) ? "slider-indicators-outside" : string.Empty; 194 string indicatorsHighlightActive = (Model.Item.GetBoolean("IndicatorsHighlightActive")) ? "slider-indicators-highlight" : string.Empty; 195 string indicatorsInvertColors = (Model.Item.GetBoolean("IndicatorsInvertedColors")) ? "slider-indicators-dark" : string.Empty; 196 string indicatorsVisibleOnSmallDevices = (Model.Item.GetBoolean("IndicatorsVisibleOnSmallDevices")) ? "slider-indicators-sm" : string.Empty; 197 198 bool productsFound = true; 199 if (string.IsNullOrEmpty(groupIds) && string.IsNullOrEmpty(productIds) && string.IsNullOrEmpty(productVariantIds)) 200 { 201 if (Pageview.IsVisualEditorMode) 202 { 203 productIds = product.Id; 204 sourceType = "selected"; 205 } 206 else 207 { 208 productsFound = false; 209 } 210 } 211 } 212 213 @*Container element for the request*@ 214 @if (productsFound) 215 { 216 <form method="post" action="@url" id="RelatedProductsForm_@Model.ID" data-response-target-element="RelatedProducts_@Model.ID" data-preloader="inline" data-update-url="false" class="item_@Model.Item.SystemName.ToLower()"> 217 <input type="hidden" name="ModelID" value="@Model.ID"> 218 <input type="hidden" name="SourceType" value="@sourceType"> 219 220 @*--- SLIDER SETTINGS ---*@ 221 <input type="hidden" name="NavigationStyle" value="@navigationStyle"> 222 <input type="hidden" name="NavigationPlacement" value="@navigationPlacement"> 223 <input type="hidden" name="IndicatorStyle" value="@indicatorStyle"> 224 <input type="hidden" name="RevealSlides" value="@revealSlides"> 225 <input type="hidden" name="NavigationAlwaysVisible" value="@(navigationAlwaysVisible)"> 226 <input type="hidden" name="NavigationVisibleOnTouch" value="@(navigationVisibleOnTouch)"> 227 <input type="hidden" name="NavigationShowScrollbar" value="@(navigationShowScrollbar)"> 228 <input type="hidden" name="NavigationSmall" value="@(navigationSmall)"> 229 <input type="hidden" name="NavigationInvertColors" value="@(navigationInvertColors)"> 230 <input type="hidden" name="NavigationNoLoop" value="@(navigationNoLoop)"> 231 <input type="hidden" name="NavigationSlideEntirePage" value="@(navigationSlideEntirePage)"> 232 <input type="hidden" name="IndicatorsOutsideSlider" value="@(indicatorsOutsideSlider)"> 233 <input type="hidden" name="IndicatorsHighlightActive" value="@(indicatorsHighlightActive)"> 234 <input type="hidden" name="IndicatorsInvertColors" value="@(indicatorsInvertColors)"> 235 <input type="hidden" name="IndicatorsVisibleOnSmallDevices" value="@(indicatorsVisibleOnSmallDevices)"> 236 237 @*--- VARIANTS ---*@ 238 @if (sourceType == "variants") 239 { 240 <input type="hidden" name="isVariant" value="true"> 241 <input type="hidden" name="MainProductID" id="MainProductID_@Model.ID" value="@productIds"> 242 } 243 244 @*--- MOST SOLD ---*@ 245 @if (sourceType == "most-sold") 246 { 247 <input type="hidden" name="SortBy" value="OrderCount"> 248 if (groupIds != "") 249 { 250 <input type="hidden" name="GroupId" value="@groupIds"> 251 } 252 } 253 254 @*--- TRENDING ---*@ 255 @if (sourceType == "trending") 256 { 257 <input type="hidden" name="SortBy" value="OrderCountGrowth"> 258 if (groupIds != "") 259 { 260 <input type="hidden" name="GroupId" value="@groupIds"> 261 } 262 } 263 264 @*--- FREQUENTLY BOUGHT ---*@ 265 @if (sourceType == "frequently" && !string.IsNullOrEmpty(productIds)) 266 { 267 <input type="hidden" name="BoughtWithProductIds" value="[@productIds]"> 268 } 269 @if (sourceType != "frequently" && hasVariants) 270 { 271 <input type="hidden" name="ProductVariantId" value="@productVariantIds"> 272 } 273 274 @*--- LATEST ---*@ 275 @if (sourceType == "latest") 276 { 277 <input type="hidden" name="SortBy" value="Created"> 278 <input type="hidden" name="GroupId" value="@groupIds"> 279 } 280 281 @*--- SELECTED PRODUCTS ---*@ 282 @if (sourceType == "selected" && !string.IsNullOrEmpty(productIds) && !hasVariants) 283 { 284 <input type="hidden" name="MainProductId" id="MainProductID_@Model.ID" value="@productIds"> 285 } 286 @if (sourceType == "selected" && hasVariants) 287 { 288 <input type="hidden" name="ProductVariantId" value="@productVariantIds"> 289 } 290 291 @*--- RELATED PRODUCTS ---*@ 292 @if (sourceType == "related-products") 293 { 294 <input type="hidden" name="ProductVariantId" id="MainProductID_@Model.ID" value="@productVariantIds"> 295 } 296 297 @* General parameters *@ 298 <input type="hidden" name="Link" value="@link"> 299 <input type="hidden" name="HideTitle" value="@Model.Item.GetString("HideTitle")"> 300 301 @if (Model.Item.GetInt32("ProductsCount") != 0) 302 { 303 <input type="hidden" name="PageSize" value="@Model.Item.GetInt32("ProductsCount")"> 304 } 305 <input type="hidden" name="HeadingTitle" id="RelatedProductsTitle_@Model.ID" value="@title"> 306 @if (!string.IsNullOrEmpty(Model.Item.GetString("Subtitle"))) 307 { 308 <input type="hidden" name="Subtitle" value="@Model.Item.GetString("Subtitle")"> 309 } 310 @if (!string.IsNullOrEmpty(Model.Item.GetString("LinkText"))) 311 { 312 <input type="hidden" name="LinkText" value="@Model.Item.GetString("LinkText")"> 313 } 314 @if (!string.IsNullOrEmpty(Model.Item.GetString("ImageAspectRatio"))) 315 { 316 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 317 ratio = ratio != "0" ? ratio : ""; 318 <input type="hidden" name="ImageAspectRatio" value="@ratio"> 319 } 320 @if (!string.IsNullOrEmpty(Model.Item.GetString("Layout"))) 321 { 322 <input type="hidden" name="Layout" value="@Model.Item.GetRawValueString("Layout")"> 323 } 324 @if (titleFontSize != "") 325 { 326 <input type="hidden" name="TitleFontSize" value="@titleFontSize"> 327 } 328 @if (subtitleFontSize != "") 329 { 330 <input type="hidden" name="SubtitleFontSize" value="@subtitleFontSize"> 331 } 332 @if (buttonStyle != "") 333 { 334 <input type="hidden" name="ButtonStyle" value="@buttonStyle"> 335 } 336 @if (generalTheme != "") 337 { 338 <input type="hidden" name="GeneralTheme" value="@generalTheme"> 339 } 340 @if (theme != "") 341 { 342 <input type="hidden" name="Theme" value="@theme"> 343 } 344 @if (imageTheme != "") 345 { 346 <input type="hidden" name="ImageTheme" value="@imageTheme"> 347 } 348 @if (!string.IsNullOrEmpty(Model.Item.GetString("ContentPadding"))) 349 { 350 string contentPadding = Model.Item.GetRawValueString("ContentPadding"); 351 <input type="hidden" name="ContentPadding" value="@contentPadding"> 352 } 353 <input type="hidden" name="TextReadability" value="@maxWidth"> 354 <input type="hidden" name="ParentColumnSize" id="ParentColumnSize_@Model.ID" value="12"> 355 356 <input type="hidden" name="SaleBadgeType" value="@Model.Item.GetRawValue("SaleBadgeType")"> 357 <input type="hidden" name="SaleBadgeCssClassName" value="@Model.Item.GetRawValue("SaleBadgeDesign")"> 358 <input type="hidden" name="NewBadgeCssClassName" value="@Model.Item.GetRawValue("NewBadgeDesign")"> 359 <input type="hidden" name="NewPublicationDays" value="@Model.Item.GetInt32("NewPublicationDays")"> 360 361 @if (campaignValues != string.Empty) 362 { 363 <input type="hidden" name="CampaignBadgesValues" value="@campaignValues"> 364 } 365 </form> 366 367 <script type="module" src="/Files/Templates/Designs/Swift/Assets/js/swiffy-slider.js"></script> 368 <script> 369 window.addEventListener("load", () => { 370 swift.AssetLoader.Load('/Files/Templates/Designs/Swift/Assets/css/swiffy-slider.min.css', 'css'); 371 }); 372 </script> 373 374 if (Pageview.IsVisualEditorMode) 375 { 376 <div class="alert alert-info" role="alert"> 377 <span>@Translate("Product slider: Edit this column to configure")</span> 378 </div> 379 } 380 381 if (sourceType != "related-products") 382 { 383 <div class="w-100 h-100"> 384 <div id="@Model.ID" class="user-select-none" style="scroll-margin-top:var(--header-height,150px)"></div> 385 <div id="RelatedProducts_@Model.ID" class="h-100 swift_product_slider_container"></div> 386 </div> 387 } 388 else if (product?.RelatedGroups != null) 389 { 390 @* Create multiple slider containers, if type is Product relation *@ 391 <div class="grid w-100 h-100@(generalTheme)" style="grid-row-gap: 4rem"> 392 <div id="@Model.ID" class="user-select-none" style="scroll-margin-top:var(--header-height,150px)"></div> 393 @foreach (var group in product.RelatedGroups) 394 { 395 <div id="RelatedProducts_@(Model.ID)_@group.Id" class="g-col-12 h-100 swift_product_slider_container"></div> 396 } 397 </div> 398 } 399 400 @* Initialize *@ 401 if (sourceType != "related-products") 402 { 403 <script type="module"> 404 if (document.querySelector("#RelatedProducts_@Model.ID").closest("[data-col-size]")) { 405 document.querySelector("#ParentColumnSize_@Model.ID").value = document.querySelector("#RelatedProducts_@Model.ID").closest("[data-col-size]").getAttribute("data-col-size"); 406 } 407 swift.PageUpdater.Update(document.querySelector("#RelatedProductsForm_@Model.ID")).then(function () { 408 setTimeout(function() { 409 const isVisualEditor = @(Converter.ToString(Pageview.IsVisualEditorMode).ToLowerInvariant()); 410 const productSliderContainer = document.querySelector(".swift_product_slider_container"); 411 412 if (productSliderContainer && productSliderContainer.innerHTML !== "") { 413 productSliderContainer.classList.remove("d-none"); 414 } 415 else if (!isVisualEditor) { 416 productSliderContainer.closest("[class*=column]").classList.add("d-none"); 417 } 418 }, 150); 419 }); 420 </script> 421 } 422 else if (product?.RelatedGroups != null) 423 { 424 @* Create multiple sliders, if type is Product relation *@ 425 foreach (var group in product.RelatedGroups) 426 { 427 IList<string> fromProductIds = new List<string> { }; 428 429 foreach (var relatedProduct in group.Products) 430 { 431 if (!string.IsNullOrEmpty(relatedProduct.VariantId)) 432 { 433 fromProductIds.Add($"{relatedProduct.ProductId} {relatedProduct.VariantId}"); 434 } 435 else 436 { 437 fromProductIds.Add($"{relatedProduct.ProductId}"); 438 } 439 } 440 <script type="module"> 441 document.querySelector("#ParentColumnSize_@Model.ID").value = document.querySelector("#RelatedProducts_@(Model.ID)_@group.Id").closest("[data-col-size]").getAttribute("data-col-size"); 442 document.querySelector("#MainProductID_@Model.ID").value = "@string.Join(",", fromProductIds)"; 443 document.querySelector("#RelatedProductsTitle_@Model.ID").value = "@group.Name"; 444 document.querySelector("#RelatedProductsForm_@Model.ID").setAttribute("data-response-target-element", "RelatedProducts_@(Model.ID)_@group.Id"); 445 446 swift.PageUpdater.Update(document.querySelector("#RelatedProductsForm_@Model.ID")); 447 </script> 448 } 449 } 450 } 451
By clicking 'Accept All' you consent that we may collect information about you for various purposes, including: Statistics and Marketing