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_f7e4225418434e8ba4d813d5c23ccd41.ExecuteAsync()
   at RazorEngine.Templating.TemplateBase.Run(ExecuteContext context, TextWriter reader)
   at RazorEngine.Templating.RazorEngineCore.RunTemplate(ICompiledTemplate template, TextWriter writer, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.DynamicWrapperService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass23_0.<Run>b__0(TextWriter writer)
   at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, Type modelType, Object model, DynamicViewBag viewBag)
   at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template)
   at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template)
   at Dynamicweb.Rendering.Template.RenderRazorTemplate()

1 @inherits ViewModelTemplate<ProductViewModel> @using Dynamicweb.Rendering @using Dynamicweb.Ecommerce.ProductCatalog @using Dynamicweb.Core @using Dynamicweb.Environment @using System.Web @{ string metaDescription = string.IsNullOrEmpty(Model.MetaDescription) ? Model.Name : Model.MetaDescription; string klaviyoID = Pageview.AreaSettings.GetString("KlaviyoPublicKey"); string baseUrl = Dynamicweb.Context.Current.Request.Url.Scheme + "://" + Dynamicweb.Context.Current.Request.Url.Host; Pageview.Meta.AddTag($"<meta property=\"og:image\" content=\"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{Model.DefaultImage.Value}\">"); Pageview.Meta.AddTag($"<meta property=\"og:image:alt\" content=\"{Model.Name}\">"); Pageview.Meta.AddTag($"<meta property=\"og:description\" content=\"{metaDescription}\">"); Pageview.Meta.AddTag("twitter:image", Model.DefaultImage.Value); Pageview.Meta.AddTag("twitter:image:alt", Model.Name); Pageview.Meta.AddTag("twitter:description", metaDescription); } @{ if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) { Dynamicweb.Context.Current.Items["ProductDetails"] = Model; } else { Dynamicweb.Context.Current.Items.Add("ProductDetails", Model); } bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]); if (isLazyLoadingForProductInfoEnabled) { string showPricesWithVat = Pageview.Area.EcomPricesWithVat.ToLower(); bool neverShowVat = string.IsNullOrEmpty(showPricesWithVat); bool hasVariantId = !string.IsNullOrEmpty(Model.VariantId); string variantIdParam = hasVariantId ? $"/{Model.VariantId}" : ""; string priceFilledProperties = $"Price,PriceFormatted{(showPricesWithVat == "false" && !neverShowVat ? ",PriceWithVat,PriceWithVatFormatted" : "")}"; string productInfoFeed = $@"/dwapi/ecommerce/products/{Model.Id}{variantIdParam} ?UserId={Converter.ToString(Pageview.User?.ID)} &LanguageId={Pageview.Area.EcomLanguageId}&CurrencyCode={Pageview.Area.EcomCurrencyId}&CountryCode={Pageview.Area.EcomCountryCode}&ShopId={Pageview.Area.EcomShopId} &FilledProperties=Id,Price,PriceBeforeDiscount,StockLevel,VariantInfo,NeverOutOfstock,Prices &PriceSettings.ShowPricesWithVat={Pageview.Area.EcomPricesWithVat} &PriceSettings.FilledProperties={priceFilledProperties} &getproductinfo=true"; Dynamicweb.Context.Current.Items["ProductInfoFeed"] = productInfoFeed; <script type="module"> swift.LiveProductInfo.init(); </script> } } <script> gtag("event", "view_item", { currency: "@Model.Price.CurrencyCode", value: @Model.Price.ToStringInvariant(), items: [ { item_id: "@Model.Number", item_name: "@Dynamicweb.Core.Encoders.HtmlEncoder.JavaScriptStringEncode(Model.Name)", currency: "@Model.Price.CurrencyCode", price: @Model.Price.ToStringInvariant(), discount: @Model.Discount.ToStringInvariant() } ] }); </script> @if ((!string.IsNullOrWhiteSpace(klaviyoID))) { ProductViewModelSettings productSetting = new ProductViewModelSettings { LanguageId = Dynamicweb.Ecommerce.Common.Context.LanguageID, CurrencyCode = Dynamicweb.Ecommerce.Common.Context.Currency.Code, CountryCode = Dynamicweb.Ecommerce.Common.Context.Country.Code2, ShopId = Pageview.Area.EcomShopId }; var productViewModel = ViewModelFactory.CreateView(productSetting, Model.Id, Model.VariantId); string productLink = baseUrl + "/" + productViewModel.GetProductLink(GetPageIdByNavigationTag("Shop"), false); string defaultProductImage = productViewModel.DefaultImage.Value; defaultProductImage = baseUrl + "/" + defaultProductImage; string categories = ""; var i=0; var count = Model.ProductCategories.Count(); foreach (var category in Model.ProductCategories){ categories += category.Value.Name; if (++i != count){ categories += ", "; } } var groups = Model.ProductCategories; string brandName = ""; foreach (var group in groups) { foreach (var field in group.Value.Fields){ if (field.Value.Name == "Brand"){ if (field.Value.Value != null){ brandName = field.Value.Value.ToString(); } } } } if ((!string.IsNullOrWhiteSpace(Converter.ToString(Pageview.User?.Email)))) { <script> klaviyo.identify({ '$email' : '@(Converter.ToString(Pageview.User?.Email))', '$first_name' : '@(Converter.ToString(Pageview.User?.Name))' }); </script> } <script type="text/javascript"> var item = { "ProductName": "@Dynamicweb.Core.Encoders.HtmlEncoder.JavaScriptStringEncode(Model.Name)", "ProductID": "@Model.Number", "Categories": "@(categories)", "ImageURL": "@defaultProductImage", "URL": "@(productLink)", "Brand": "@(brandName)", "Price": @PriceViewModelExtensions.ToStringInvariant(Model.Price) }; klaviyo.push(["track", "Viewed Product", item]); </script> <script> klaviyo.push(["trackViewedItem", { "Title": "@Dynamicweb.Core.Encoders.HtmlEncoder.JavaScriptStringEncode(Model.Name)", "ItemId": "@Model.Number", "Categories": "@(categories)", "ImageUrl": "@defaultProductImage", "Url": "@(productLink)", "Metadata": { "Price": @PriceViewModelExtensions.ToStringInvariant(Model.Price) } }]); </script> } <script> window.addEventListener('load', function (event) { swift.Video.init(); }); </script>
Error executing template "Designs/Swift/Paragraph/Swift_ProductDetailsImage.cshtml"
System.ArgumentNullException: Value cannot be null. (Parameter 'source')
   at System.Linq.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument)
   at System.Linq.Enumerable.Where[TSource](IEnumerable`1 source, Func`2 predicate)
   at CompiledRazorTemplates.Dynamic.RazorEngine_587152c644c8409a80b3432c3f172164.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> @using Dynamicweb.Ecommerce.ProductCatalog @using Dynamicweb.Frontend @using System.IO @using System.Text.RegularExpressions; @functions { public ProductViewModel product { get; set; } = new ProductViewModel(); public string galleryLayout { get; set; } public string[] supportedImageFormats { get; set; } public string[] supportedVideoFormats { get; set; } public string[] supportedDocumentFormats { get; set; } public string[] allSupportedFormats { get; set; } public class RatioSettings { public string Ratio { get; set; } public string CssClass { get; set; } public string CssVariable { get; set; } public string Fill { get; set; } } public RatioSettings GetRatioSettings(string size = "desktop") { var ratioSettings = new RatioSettings(); string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); ratio = ratio != "0" ? ratio : ""; string cssClass = ratio != "" && ratio != "fill" ? " ratio" : ""; string cssVariable = ratio != "" && ratio != "fill" ? "--bs-aspect-ratio: " + ratio : ""; cssClass = ratio == "fill" && size == "mobile" ? " ratio" : cssClass; cssVariable = ratio == "fill" && size == "mobile" ? "--bs-aspect-ratio: 66%" : cssVariable; ratioSettings.Ratio = ratio; ratioSettings.CssClass = cssClass; ratioSettings.CssVariable = cssVariable; ratioSettings.Fill = ratio == "fill" ? " h-100" : ""; return ratioSettings; } public string GetArrowsColor() { var invertColor = Model.Item.GetBoolean("InvertModalArrowsColor"); var arrowsColor = invertColor ? " carousel-dark" : string.Empty; return arrowsColor; } public string GetThumbnailPlacement() { return Model.Item.GetRawValueString("ThumbnailPlacement", "bottom"); } public string GetThumbnailRowSettingCss() { switch (GetThumbnailPlacement()) { case "bottom": return "d-flex flex-wrap"; case "left": return "d-flex flex-column order-first"; case "right": return "d-flex flex-column order-last"; default: return "d-flex flex-wrap"; } } public Dictionary<string, object> GetVideoParams(MediaViewModel asset, string size) { string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Name; string type = GetVideoType(asset.Value); bool openInModal = Model.Item.GetString("OpenVideoInModal") == "true" ? true : false; bool autoPlay = Model.Item.GetBoolean("VideoAutoPlay"); var videoParams = new Dictionary<string, object>(); videoParams.Add("AssetName", asset.Name); videoParams.Add("AssetVideoType", type); videoParams.Add("AssetDisplayName", asset.DisplayName); videoParams.Add("OpenVideoInModal", openInModal); videoParams.Add("VideoAutoPlay", autoPlay); videoParams.Add("Size", size); videoParams.Add("Id", Model.ID); return videoParams; } public string GetVideoType(string assetValue) { string type = assetValue.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "youtube" : string.Empty; type = assetValue.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "vimeo" : type; type = string.IsNullOrEmpty(type) ? "selfhosted" : type; return type; } public string GetYoutubeScreenDump(string assetValue, string quality) { var regex = new Regex(@"(?:youtube\.com\/.*[\?&]v=|youtu\.be\/|youtube\.com\/embed\/)([\w-]+)(?:\?.*)?"); Match match = regex.Match(assetValue); string videoId = match.Success ? match.Groups[1].Value : string.Empty; string youtubeThumbnail = $"https://img.youtube.com/vi/{videoId}/{quality}.jpg"; return youtubeThumbnail; } } @{ ProductViewModel product = null; if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) { product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; } else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) { var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); if (productList?.Products is object) { product = productList.Products[0]; } } } @if (product is object) { @* Supported formats *@ supportedImageFormats = new string[] { ".jpg", ".jpeg", ".webp", ".png", ".gif", ".bmp", ".tiff" }; supportedVideoFormats = new string[] { "youtu.be", "youtube", "vimeo", ".mp4", ".webm" }; supportedDocumentFormats = new string[] { ".pdf", ".docx", ".xlsx", ".ppt", "pptx" }; allSupportedFormats = supportedImageFormats.Concat(supportedVideoFormats).Concat(supportedDocumentFormats).ToArray(); @* Collect the assets *@ var selectedAssetCategories = Model.Item.GetList("ImageAssets")?.GetRawValue().OfType<string>(); bool includeImagePatternImages = Model.Item.GetBoolean("ImagePatternImages"); @* Needed image data collection to support both DefaultImage, ImagePatterns and Image Assets *@ string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : ""; IEnumerable<MediaViewModel> assetsImages = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets); assetsImages = assetsImages.OrderByDescending(x => x.Value.Equals(defaultImage)); IEnumerable<MediaViewModel> assetsList = new MediaViewModel[] { }; assetsList = includeImagePatternImages ? assetsList.Union(product.ImagePatternImages) : assetsList; assetsList = assetsList.Union(assetsImages); assetsList = includeImagePatternImages && assetsList.Count() == 0 ? assetsList.Append(product.DefaultImage) : assetsList; bool defaultImageFallback = Model.Item.GetBoolean("DefaultImageFallback"); bool showOnlyPrimaryImage = Model.Item.GetBoolean("ShowOnlyPrimaryImage"); int totalAssets = 0; if (showOnlyPrimaryImage == false) { foreach (MediaViewModel asset in assetsList) { var assetValue = asset.Value; foreach (string format in allSupportedFormats) { if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { totalAssets++; } } } } if ((totalAssets == 0 && product.DefaultImage != null && selectedAssetCategories.Count() == 0) || (showOnlyPrimaryImage == true && product.DefaultImage != null) || totalAssets == 0 && defaultImageFallback) { assetsList = new List<MediaViewModel>() { product.DefaultImage }; totalAssets = 1; } @* Theme settings *@ string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; var badgeParms = new Dictionary<string, object>(); badgeParms.Add("size", "h5"); badgeParms.Add("saleBadgeType", Model.Item.GetRawValue("SaleBadgeType")); badgeParms.Add("saleBadgeCssClassName", Model.Item.GetRawValue("SaleBadgeDesign")); badgeParms.Add("newBadgeCssClassName", Model.Item.GetRawValue("NewBadgeDesign")); badgeParms.Add("newPublicationDays", Model.Item.GetInt32("NewPublicationDays")); badgeParms.Add("campaignBadgesValues", Model.Item.GetList("CampaignBadges")?.GetRawValue().OfType<string>().ToList()); bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("SaleBadgeDesign")) && Model.Item.GetRawValueString("SaleBadgeDesign") != "none" ? true : false; bool newBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("NewBadgeDesign")) && Model.Item.GetRawValueString("NewBadgeDesign") != "none" ? true : false; DateTime createdDate = product.Created.Value; bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false; showBadges = (newBadgeEnabled && Model.Item.GetInt32("NewPublicationDays") == 0) || (newBadgeEnabled && (createdDate.AddDays(Model.Item.GetInt32("NewPublicationDays")) > DateTime.Now)) ? true : showBadges; showBadges = !string.IsNullOrEmpty(Model.Item.GetRawValueString("CampaignBadges")) ? true : showBadges; @* Get assets from selected categories or get all assets *@ if (totalAssets != 0) { int assetNumber = 0; int thumbnailNumber = 0; int modalAssetNumber = 0; var assetsListUnique = assetsList.GroupBy(x => x.Value).Select(g => g.First()).ToList(); string thumbnailAxisCss = GetThumbnailPlacement() == "bottom" ? "flex-column" : string.Empty; <div class="d-flex gap-3 h-100 @(thumbnailAxisCss) @(theme) item_@Model.Item.SystemName.ToLower()"> <div id="SmallScreenImages_@Model.ID" class="carousel@(GetArrowsColor()) col position-relative" data-bs-ride="carousel"> <div class="carousel-inner h-100"> @foreach (MediaViewModel asset in assetsListUnique) { var assetValue = asset.Value; foreach (string format in allSupportedFormats) { if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { string activeSlide = assetNumber == 0 ? "active" : ""; <div class="carousel-item @activeSlide" data-bs-interval="99999"> @{ string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; string size = "mobile"; <div class="h-100 @(imageTheme)"> @foreach (string imageFormat in supportedImageFormats) { //Images if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) { if (product is object) { string productName = product.Name; string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; string imageLinkPath = Uri.EscapeDataString(imagePath); RatioSettings ratioSettings = GetRatioSettings(size); var parms = new Dictionary<string, object>(); parms.Add("alt", productName + asset.Keywords); parms.Add("itemprop", "image"); parms.Add("fullwidth", true); parms.Add("columns", Model.GridRowColumnCount); parms.Add("eagerLoadNewImages", Model.Item.GetBoolean("DisableLazyLoading")); parms.Add("doNotUseGetimage", Model.Item.GetBoolean("DisableGetImage")); if (!string.IsNullOrEmpty(asset.DisplayName)) { parms.Add("title", asset.DisplayName); } if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") { parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); } else { parms.Add("cssClass", "mw-100 mh-100"); } <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@assetNumber"> @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) </div> </a> } } } @foreach (string videoFormat in supportedVideoFormats) { //Videos if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) { if (Model.Item.GetString("OpenVideoInModal") == "true") { if (product is object) { string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; string productName = product.Name; productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; RatioSettings ratioSettings = GetRatioSettings(size); string type = GetVideoType(asset.Value); string videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value, "maxresdefault") : string.Empty; videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeUriString(asset.Value) : videoScreendumpPath; string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : ""; <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable); cursor: pointer" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@assetNumber"> <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> @if (type != "selfhosted") { <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;"> } else { string videoType = Path.GetExtension(asset.Value).ToLower(); <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> </video> } </div> </div> } } else { if (product is object) { var videoParams = GetVideoParams(asset, size); @RenderPartial("Components/VideoPlayer.cshtml", new FileViewModel { Path = asset.Value}, videoParams); } } } } @foreach (string documentFormat in supportedDocumentFormats) { //Documents if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) { if (product is object) { string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; string productName = product.Name; string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; string imageLinkPath = Uri.EscapeDataString(imagePath); RatioSettings ratioSettings = GetRatioSettings(size); var parms = new Dictionary<string, object>(); parms.Add("alt", productName + asset.Keywords); parms.Add("itemprop", "image"); parms.Add("fullwidth", true); parms.Add("columns", Model.GridRowColumnCount); if (!string.IsNullOrEmpty(asset.DisplayName)) { parms.Add("title", asset.DisplayName); } if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") { parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); } else { parms.Add("cssClass", "mw-100 mh-100"); } <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download alt="@Translate("Download")"> <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) { @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) } </div> </a> } } } </div> } </div> assetNumber++; } } } </div> @if (showBadges) { <div class="position-absolute top-0 left-0 p-2 p-lg-3"> @{@RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms)} </div> } </div> @if (totalAssets > 1) { <div class="@(GetThumbnailRowSettingCss()) gap-3" id="SmallScreenImagesThumbnails_@Model.ID"> @foreach (MediaViewModel asset in assetsList) { var assetValue = asset.Value; string assetName = asset.Name; assetName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : null; string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; string imagePath = Dynamicweb.Context.Current.Server.UrlEncode(assetValue); imagePath = assetValue.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "https://img.youtube.com/vi/" + assetValue.Substring(assetValue.LastIndexOf('/') + 1) + "/mqdefault.jpg" : imagePath; string imagePathThumb = assetValue.StartsWith("/Files/", StringComparison.OrdinalIgnoreCase) ? imagePath.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) < 0 && imagePath.IndexOf(".mp4", StringComparison.OrdinalIgnoreCase) < 0 ? $"/Admin/Public/GetImage.ashx?image={imagePath}&width=180&format=webp" : imagePath : assetValue; RatioSettings ratioSettings = GetRatioSettings("desktop"); <div class="border outline-none @(ratioSettings.CssClass)" style="@(ratioSettings.CssVariable); cursor: pointer; min-width: 7rem; max-width: 8rem;" data-bs-target="#SmallScreenImages_@Model.ID" data-bs-slide-to="@thumbnailNumber"> @foreach (string imageFormat in supportedImageFormats) { //Images if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) { <img src="@imagePathThumb" alt="@assetName" @assetTitle class="p-0 p-lg-1 w-100 h-100" style="object-fit: contain;"> thumbnailNumber++; } } @foreach (string videoFormat in supportedVideoFormats) { //Videos if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) { string type = GetVideoType(asset.Value); string videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value, "mqdefault") : ""; videoScreendumpPath = type == "vimeo" ? string.Empty : videoScreendumpPath; videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeUriString(asset.Value) : videoScreendumpPath; string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : string.Empty; <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> if (type != "selfhosted") { <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@assetTitle" @assetTitle class="@videoJsClass mw-100 mh-100" data-asset-value="@asset.Value" style="object-fit: cover;" /> } else { string videoType = Path.GetExtension(asset.Value).ToLower(); <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> </video> } thumbnailNumber++; } } @foreach (string documentFormat in supportedDocumentFormats) { //Documents if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) { <a href="@Uri.EscapeDataString(assetValue)" class="ratio ratio-4x3 border outline-none" style="cursor: pointer; min-width: 7rem; max-width: 8rem;" download title="@asset.Value"> @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) { <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <div class="icon-3 position-absolute text-light" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> </div> <img src="@imagePathThumb" alt="@assetName" @assetTitle class="p-0 p-lg-1 mw-100 mh-100" style="object-fit: cover;"> } else { <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <div class="icon-3 position-absolute" style="z-index: 1">@ReadFile(iconPath + "file-text.svg")</div> </div> } </a> thumbnailNumber++; } } </div> } </div> } </div> @* Modal with slides *@ <div class="modal fade swift_products-details-images-modal" id="modal_@Model.ID" tabindex="-1" aria-labelledby="productDetailsGalleryModalTitle_@Model.ID" aria-hidden="true"> <div class="modal-dialog modal-dialog-centered modal-xl"> <div class="modal-content"> <div class="modal-header visually-hidden"> <strong class="modal-title" id="productDetailsGalleryModalTitle_@Model.ID">@product.Title</strong> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body p-2 p-lg-3 h-100"> <div id="ModalCarousel_@Model.ID" class="carousel@(GetArrowsColor()) h-100" data-bs-ride="carousel"> <div class="carousel-inner h-100 @theme"> @foreach (MediaViewModel asset in assetsList) { var assetValue = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; foreach (string supportedFormat in supportedImageFormats.Concat(supportedVideoFormats).ToArray()) { if (assetValue.IndexOf(supportedFormat, StringComparison.OrdinalIgnoreCase) >= 0) { string imagePath = assetValue; string activeSlide = modalAssetNumber == 0 ? "active" : ""; var parms = new Dictionary<string, object>(); parms.Add("cssClass", "d-block mw-100 mh-100 m-auto"); parms.Add("fullwidth", true); parms.Add("columns", Model.GridRowColumnCount); <div class="carousel-item @activeSlide h-100" data-bs-interval="99999"> @foreach (string imageFormat in supportedImageFormats) { //Images if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) { @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) } } @foreach (string videoFormat in supportedVideoFormats) { //Videos if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) { var videoParams = GetVideoParams(asset, "modal"); @RenderPartial("Components/VideoPlayer.cshtml", new FileViewModel { Path = asset.Value }, videoParams) } } </div> modalAssetNumber++; } } } <button class="carousel-control-prev carousel-control-area" type="button" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide="prev"> <span class="carousel-control-prev-icon" aria-hidden="true"></span> <span class="visually-hidden">@Translate("Previous")</span> </button> <button class="carousel-control-next carousel-control-area" type="button" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide="next"> <span class="carousel-control-next-icon" aria-hidden="true"></span> <span class="visually-hidden">@Translate("Next")</span> </button> </div> </div> </div> </div> </div> </div> } else if (Pageview.IsVisualEditorMode) { RatioSettings ratioSettings = GetRatioSettings("desktop"); <div class="h-100 @theme"> <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> <img src="/Files/Images/missing_image.jpg" loading="lazy" decoding="async" class="mh-100 mw-100" style="object-fit: cover;"> </div> </div> } } else if (Pageview.IsVisualEditorMode) { <div class="alert alert-dark m-0">@Translate("No products available")</div> }

Error executing template "Designs/Swift/Paragraph/Swift_ProductSpecification.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_4d3c733b94c84e72a5140a680d0afc01.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 22 @if (product is object) { 23 IEnumerable<string> selectedDisplayGroupIds = Model.Item.GetRawValueString("DisplayGroups").Split(',').ToList(); 24 List<CategoryFieldViewModel> displayGroups = new List<CategoryFieldViewModel>(); 25 26 foreach (var selection in selectedDisplayGroupIds) 27 { 28 foreach (CategoryFieldViewModel group in product.FieldDisplayGroups.Values) 29 { 30 if (selection == group.Id) 31 { 32 int fieldsWithNoValueOrZero = 0; 33 34 foreach (var field in group.Fields) 35 { 36 if (string.IsNullOrEmpty(field.Value.Value.ToString())) 37 { 38 fieldsWithNoValueOrZero++; 39 } 40 } 41 42 if (fieldsWithNoValueOrZero != group.Fields.Count) 43 { 44 displayGroups.Add(group); 45 } 46 } 47 } 48 } 49 50 bool showProductFields = Model.Item.GetBoolean("ProductFields"); 51 52 bool hideTitle = Model.Item.GetBoolean("HideTitle"); 53 54 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 55 56 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "display-4"); 57 58 string contentPadding = Model.Item.GetRawValueString("ContentPadding", ""); 59 contentPadding = contentPadding == "none" ? string.Empty : contentPadding; 60 contentPadding = contentPadding == "small" ? " p-2 p-md-3" : contentPadding; 61 contentPadding = contentPadding == "large" ? " p-4 p-md-5" : contentPadding; 62 63 string layout = Model.Item.GetRawValueString("Layout", "list"); 64 string size = Model.Item.GetRawValueString("Size", "full"); 65 string gaps = size == "full" ? " gap-4" : " gap-2"; 66 67 68 if (Pageview.IsVisualEditorMode && displayGroups.Count() == 0) 69 { 70 product.ProductFields.Clear(); 71 product.ProductFields.Add(Translate("Width"), new FieldValueViewModel { Name = Translate("Width"), Value = "99cm" }); 72 product.ProductFields.Add(Translate("Height"), new FieldValueViewModel { Name = Translate("Height"), Value = "195cm" }); 73 showProductFields = true; 74 } 75 76 if (layout == "commas") 77 { 78 gaps = size == "full" ? " gap-4" : " gap-2"; 79 80 } 81 82 <div class="h-100@(gaps)@(theme)@(contentPadding) item_@Model.Item.SystemName.ToLower()"> 83 <div class="grid"> 84 @if ((product.ProductFields != null && Model.Item.GetBoolean("ProductFields")) || (product.ProductCategories != null && Model.Item.GetBoolean("CategoryFields")) || (displayGroups.Count != 0)) { 85 if (!hideTitle) 86 { 87 <h2 class="g-col-12 @titleFontSize">@Translate(Model.Item.GetString("Title"))</h2> 88 } 89 } 90 91 @if (displayGroups.Count != 0) 92 { 93 if (layout != "accordion") 94 { 95 foreach (var group in displayGroups) 96 { 97 bool hideHeader = Model.Item.GetBoolean("HideGroupHeaders"); 98 99 if (!hideHeader) { 100 <h4 class="g-col-12 h4 mb-0">@group.Name</h4> 101 } 102 103 { @RenderFieldsFromList(group.Fields, layout) } 104 105 } 106 } 107 else 108 { 109 <div class="g-col-12"> 110 <div class="accordion accordion-flush w-100" id="Specifications_@Model.ID"> 111 @foreach (var group in displayGroups) 112 { 113 <div class="accordion-item"> 114 <h2 class="accordion-header" id="SpecificationHeading_@group.Id"> 115 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#SpecificationItem_@group.Id" aria-expanded="false" aria-controls="SpecificationItem_@group.Id"> 116 @group.Name 117 </button> 118 </h2> 119 <div id="SpecificationItem_@group.Id" class="accordion-collapse collapse" aria-labelledby="SpecificationHeading_@group.Id" data-bs-parent="#Specifications_@Model.ID"> 120 <div class="accordion-body"> 121 @{ @RenderFieldsFromList(group.Fields, "list") } 122 </div> 123 </div> 124 </div> 125 } 126 </div> 127 </div> 128 } 129 } 130 131 @if (product.ProductFields != null && showProductFields) 132 { 133 if (product.ProductFields.Count > 0) 134 { 135 if (layout != "accordion") 136 { 137 {@RenderFieldsFromList(product.ProductFields, layout) } 138 } 139 else 140 { 141 <div class="g-col-12"> 142 <div class="accordion accordion-flush w-100" id="Specifications_@Model.ID"> 143 <div class="accordion-item"> 144 <h2 class="accordion-header" id="SpecificationHeading_@Model.ID"> 145 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#SpecificationItem_@Model.ID" aria-expanded="false" aria-controls="SpecificationItem_@Model.ID"> 146 @Translate("Specifications") 147 </button> 148 </h2> 149 <div id="SpecificationItem_@Model.ID" class="accordion-collapse" aria-labelledby="SpecificationHeading_@Model.ID" data-bs-parent="#Specifications_@Model.ID"> 150 <div class="accordion-body"> 151 @{ @RenderFieldsFromList(product.ProductFields, "List") } 152 </div> 153 </div> 154 </div> 155 </div> 156 </div> 157 } 158 } 159 } 160 161 @if (product.ProductCategories != null && Model.Item.GetBoolean("CategoryFields")) 162 { 163 if (product.ProductCategories.Count > 0) 164 { 165 if (layout != "accordion") 166 { 167 foreach (var group in product.ProductCategories) 168 { 169 CategoryFieldViewModel category = group.Value; 170 bool hideHeader = Model.Item.GetBoolean("HideGroupHeaders"); 171 172 if (!hideHeader) { 173 <h4 class="g-col-12 h4 mb-0">@group.Value.Name</h4> 174 } 175 176 { @RenderFieldsFromList(category.Fields, layout) } 177 } 178 } 179 else 180 { 181 <div class="g-col-12"> 182 <div class="accordion accordion-flush w-100" id="Specifications_@Model.ID"> 183 @foreach (var group in product.ProductCategories) 184 { 185 CategoryFieldViewModel category = group.Value; 186 187 <div class="accordion-item"> 188 <h2 class="accordion-header" id="SpecificationHeading_@group.Value.Id"> 189 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#SpecificationItem_@group.Value.Id" aria-expanded="false" aria-controls="SpecificationItem_@group.Value.Id"> 190 @group.Value.Name 191 </button> 192 </h2> 193 <div id="SpecificationItem_@group.Value.Id" class="accordion-collapse" aria-labelledby="SpecificationHeading_@group.Value.Id" data-bs-parent="#Specifications_@Model.ID"> 194 <div class="accordion-body"> 195 @{ @RenderFieldsFromList(category.Fields, "list") } 196 </div> 197 </div> 198 </div> 199 } 200 </div> 201 </div> 202 } 203 } 204 } 205 206 @if (Model.Item.GetBoolean("ShowManufacturerProductPage")){ 207 208 string facturerProductPage = ""; 209 210 foreach (var field in product.ProductFields) { 211 212 213 if (field.Value.SystemName == "ManufacturerProductLink"){ 214 facturerProductPage = field.Value?.Value != null ? field.Value.Value.ToString() : "";; 215 } 216 } 217 218 if (!string.IsNullOrWhiteSpace(facturerProductPage)){ 219 <div class="g-col-12"> 220 <dl class="grid gap-1"> 221 222 <dd class="g-col-12 mb-0 text-break"> 223 <a class="btn btn-secondary" href="@(facturerProductPage)" target="_blank" rel="noopener" title="Link til producent">@Translate("Link til producent")</a> 224 </dd> 225 </dl> 226 </div> 227 } 228 } 229 </div> 230 </div> 231 } 232 else if (Pageview.IsVisualEditorMode) 233 { 234 <div class="alert alert-warning m-0">@Translate("No products available")</div> 235 } 236 237 @functions 238 { 239 string RenderFieldsFromList(Dictionary<string, FieldValueViewModel> fields, string layout) 240 { 241 string size = Model.Item.GetRawValueString("Size", "full"); 242 string gaps = size != "full" ? " gap-1" : string.Empty; 243 bool hideFieldLabels = Model.Item.GetBoolean("HideFieldLabels"); 244 bool hideFieldsWithZeroValue = Model.Item.GetBoolean("HideFieldsWithZeroValue"); 245 246 if (layout == "columns") { 247 <div class="g-col-12"> 248 <div class="grid@(gaps)"> 249 @foreach (var field in fields) 250 { 251 {@RenderField(field.Value, layout)} 252 } 253 </div> 254 </div> 255 } 256 if (layout == "list") { 257 <div class="g-col-12"> 258 <dl class="grid@(gaps)"> 259 @foreach (var field in fields) 260 { 261 {@RenderField(field.Value, layout)} 262 } 263 </dl> 264 </div> 265 } 266 if (layout == "table") 267 { 268 string tableSize = size == "full" ? "" : " table-sm"; 269 <div class="g-col-12"> 270 <table class="table table-striped@(tableSize)"> 271 @foreach (var field in fields) 272 { 273 {@RenderField(field.Value, layout)} 274 } 275 </table> 276 </div> 277 } 278 if (layout == "bullets") 279 { 280 string listSize = size == "full" ? "" : "m-0 p-0 lh-1 fs-7 opacity-75"; 281 string listStyle = size == "full" ? "" : "style=\"list-style-position: inside\""; 282 <div class="g-col-12"> 283 <ul class="@listSize" @listStyle> 284 @foreach (var field in fields) 285 { 286 {@RenderField(field.Value, layout)} 287 } 288 </ul> 289 </div> 290 } 291 if (layout == "commas") 292 { 293 List<string> featuresList = new List<string>(); 294 295 foreach (var field in fields) 296 { 297 string firstListItemValue = string.Empty; //Hack to support field type providers with a single value 298 299 if (field.Value?.Value != null) 300 { 301 if (field.Value.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 302 { 303 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 304 305 //Hack to support field type providers with a single value 306 if (values.FirstOrDefault() != null) 307 { 308 firstListItemValue = values.FirstOrDefault().Value; 309 } 310 } 311 } 312 313 if (!hideFieldsWithZeroValue || (firstListItemValue != "0" && firstListItemValue != "0.0" && field.Value.Value.ToString() != "0" && field.Value.Value.ToString() != "0.0")) 314 { 315 if (field.Value.Value is object && !string.IsNullOrEmpty(field.Value.Value.ToString())) 316 { 317 if (field.Value.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 318 { 319 List<string> options = new List<string>(); 320 foreach (FieldOptionValueViewModel option in field.Value.Value as System.Collections.Generic.List<FieldOptionValueViewModel>) 321 { 322 if (!string.IsNullOrWhiteSpace(option.Value)) 323 { 324 if (option.Value.ToString().Contains("#") && (Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour"))) 325 { 326 string colorSpan = "<span class=\"colorbox-sm\" style=\"background-color: " + option.Value + "\"></span>"; 327 options.Add(colorSpan); 328 } 329 else if (!string.IsNullOrEmpty(option.Value)) 330 { 331 options.Add(option.Name); 332 } 333 } 334 } 335 string optionsString = (string.Join(", ", options.Select(x => x.ToString()).ToArray())); 336 if ((Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour"))) 337 { 338 optionsString = (string.Join(" ", options.Select(x => x.ToString()).ToArray())); 339 } 340 341 if (!string.IsNullOrEmpty(optionsString)) 342 { 343 if (!hideFieldLabels) 344 { 345 featuresList.Add(field.Value.Name + ": " + optionsString); 346 } 347 else 348 { 349 featuresList.Add(optionsString); 350 } 351 } 352 } 353 else 354 { 355 if (!string.IsNullOrWhiteSpace(field.Value.Value.ToString())) 356 { 357 if (field.Value.Value.ToString().Contains("#") && (Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour"))) 358 { 359 string colorSpan = "<span class=\"colorbox-sm\" style=\"background-color: " + field.Value.Value + "\"></span>"; 360 361 if (!hideFieldLabels) 362 { 363 featuresList.Add(field.Value.Name + ": " + colorSpan); 364 } 365 else 366 { 367 featuresList.Add(colorSpan); 368 } 369 } 370 else 371 { 372 if (!hideFieldLabels) 373 { 374 featuresList.Add(field.Value.Name + ": " + field.Value.Value.ToString()); 375 } 376 else 377 { 378 featuresList.Add(field.Value.Value.ToString()); 379 } 380 } 381 } 382 } 383 } 384 } 385 } 386 387 string featuresString = (string.Join(", ", featuresList.Select(x => x.ToString()).ToArray())); 388 389 <div class="g-col-12 opacity-75 fs-7">@featuresString</div> 390 } 391 return string.Empty; 392 } 393 394 string RenderField(FieldValueViewModel field, string layout) 395 { 396 string size = Model.Item.GetRawValueString("Size", "full"); 397 string fieldValue = field?.Value != null ? field.Value.ToString() : ""; 398 bool hideFieldLabels = Model.Item.GetBoolean("HideFieldLabels"); 399 bool noValues = false; 400 string firstListItemValue = string.Empty; //Hack to support field type providers with a single value 401 bool hideFieldsWithZeroValue = Model.Item.GetBoolean("HideFieldsWithZeroValue"); 402 403 if (!string.IsNullOrEmpty(fieldValue)) 404 { 405 if (field.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 406 { 407 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 408 noValues = values.Count > 0 ? false : true; 409 410 //Hack to support field type providers with a single value 411 if (values.FirstOrDefault() != null) 412 { 413 firstListItemValue = values.FirstOrDefault().Value; 414 } 415 } 416 } 417 418 if (!string.IsNullOrEmpty(fieldValue) && noValues == false) 419 { 420 if (!hideFieldsWithZeroValue || (firstListItemValue != "0" && firstListItemValue != "0.0" && field.Value.ToString() != "0" && field.Value.ToString() != "0.0")) 421 { 422 if (layout == "columns") 423 { 424 425 <div class="grid g-col-6 g-col-lg-4 gap-1"> 426 @if (!hideFieldLabels) 427 { 428 <dt class="g-col-12 g-col-lg-4" t=a>@Translate(field.Name)</dt> 429 } 430 <dd class="g-col-12 g-col-lg-8 mb-0 text-break"> 431 @{ @RenderFieldValue(field) } 432 </dd> 433 </div> 434 } 435 if (layout == "list") 436 { 437 if (!hideFieldLabels) 438 { 439 <dt class="g-col-4" t=b>@Translate(field.Name)</dt> 440 } 441 <dd class="g-col-8 mb-0 text-break"> 442 @{ @RenderFieldValue(field) } 443 </dd> 444 } 445 if (layout == "table") 446 { 447 <tr> 448 @if (!hideFieldLabels) 449 { 450 <th class="w-25 w-lg-50" scope="row">@Translate(field.Name)</th> 451 } 452 <td class="text-break"> 453 @{ @RenderFieldValue(field) } 454 </td> 455 </tr> 456 } 457 if (layout == "bullets") 458 { 459 <li> 460 @if (!hideFieldLabels) 461 { 462 <strong>@Translate(field.Name)</strong> 463 } 464 <span> 465 @{ @RenderFieldValue(field) } 466 </span> 467 </li> 468 } 469 } 470 } 471 return string.Empty; 472 } 473 474 string RenderFieldValue(FieldValueViewModel field) 475 { 476 string fieldValue = field?.Value != null ? field.Value.ToString() : ""; 477 478 bool isLink = field?.Type == "Link"; 479 bool isColor = false; 480 bool isBrandName = field?.SystemName == "Brand_name"; 481 482 fieldValue = fieldValue == "False" ? Translate("No") : fieldValue; 483 fieldValue = fieldValue == "True" ? Translate("Yes") : fieldValue; 484 485 486 if (field.Value.GetType() == typeof(System.Collections.Generic.List<Dynamicweb.Ecommerce.ProductCatalog.FieldOptionValueViewModel>)) 487 { 488 int valueCount = 0; 489 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 490 int totalValues = values.Count; 491 492 foreach (FieldOptionValueViewModel option in values) 493 { 494 if (!string.IsNullOrEmpty(option.Value)) 495 { 496 if (option.Value.Substring(0, 1) == "#") 497 { 498 isColor = true; 499 } 500 } 501 502 if (!isColor) 503 { 504 @option.Name 505 } 506 else 507 { 508 <span class="colorbox-sm" style="background-color: @option.Value" title="@option.Name"></span> 509 } 510 511 if (valueCount != totalValues && valueCount < (totalValues - 1)) 512 { 513 if (isColor) 514 { 515 <text> </text> 516 } 517 else 518 { 519 <text>, </text> 520 } 521 } 522 valueCount++; 523 } 524 } 525 else 526 { 527 if (fieldValue.Substring(0, 1) == "#") 528 { 529 isColor = true; 530 } 531 532 if (!isColor) 533 { 534 if (isLink) 535 { 536 string linktTitle = !fieldValue.Contains("aspx") ? fieldValue : Translate("Go to link"); 537 string target = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") && fieldValue.Contains("http") ? "target=\"_blank\"" : string.Empty; 538 string rel = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") && fieldValue.Contains("http") ? "rel=\"noopener\"" : string.Empty; 539 540 <a href="@field.Value" title="@field.Name" @target @rel>@linktTitle</a> 541 } 542 else if (isBrandName) 543 { 544 <span itemprop="brand" itemtype="https://schema.org/Brand" itemscope> 545 <span itemprop="name">@fieldValue</span> 546 </span> 547 } 548 else 549 { 550 @fieldValue 551 } 552 553 } 554 else 555 { 556 <span class="colorbox-sm" style="background-color: @fieldValue" title="@fieldValue"></span> 557 } 558 } 559 return string.Empty; 560 } 561 }
Error executing template "Designs/Swift/Paragraph/Swift_ProductPriceTable.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_46085840760842dcad2d52ed7efa9338.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> @using Dynamicweb.Ecommerce.ProductCatalog @{ ProductViewModel product = null; if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) { product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; } else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) { var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); if (productList?.Products is object) { product = productList.Products[0]; } } string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", ""); bool anonymousUser = Pageview.User == null; bool hidePrice = anonymousUsersLimitations.Contains("price") && anonymousUser; hidePrice = Pageview.IsVisualEditorMode ? false : hidePrice; } @if (product is object && !hidePrice) { bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]); if (Pageview.IsVisualEditorMode && product.Prices.Count == 0) { product.Prices.Add(new PriceListViewModel { Price = new PriceViewModel { Price = 95, PriceFormatted = "€95" }, Quantity = 1 }); product.Prices.Add(new PriceListViewModel { Price = new PriceViewModel { Price = 85, PriceFormatted = "€85" }, Quantity = 2 }); } string layout = Model.Item.GetRawValueString("Layout", "list"); if (product.Prices.Count > 0) { if (isLazyLoadingForProductInfoEnabled) { <div class="product-prices-container d-none" data-show-if="LiveProductInfo.product.Prices.length > 0"> @if (layout == "list") { <div class="product-prices item_@Model.Item.SystemName.ToLower() list"> <small class="d-block opacity-75 product-prices-template"><span><span class="js-text-price-quantity"></span> @Translate("PCS")</span> - <span class="fw-bold"><span class="js-text-price-price"></span> <span class="d-none" data-show-if="LiveProductInfo.productPrice.Quantity > 1">@Translate("pr. PCS")</span></span></small> </div> } else if (layout == "table") { <div class="item_@Model.Item.SystemName.ToLower() grid"> <table class="table table-sm mt-3 g-col-12 g-col-lg-6"> <thead> <tr> <td>@Translate("QTY")</td> <td>@Translate("pr. PCS")</td> </tr> </thead> <tbody class="product-prices"> <tr class="product-prices-template"> <td class="js-text-price-quantity"></td> <td class="js-text-price-price"></td> </tr> </tbody> </table> </div> } </div> } else { if (layout == "list") { <div class="item_@Model.Item.SystemName.ToLower() list"> @foreach (PriceListViewModel quantityPrice in product.Prices) { string quantityLabel = Translate("PCS"); string quantityPriceSuffix = quantityPrice.Quantity > 1 ? Translate("pr. PCS") : ""; <small class="d-block opacity-75"><span>@quantityPrice.Quantity @quantityLabel</span> - <span class="fw-bold">@quantityPrice.Price.PriceFormatted @quantityPriceSuffix</span></small> } </div> } else if (layout == "table") { <div class="item_@Model.Item.SystemName.ToLower() grid"> <table class="table table-sm mt-3 g-col-12 g-col-lg-6"> <thead> <tr> <td>@Translate("QTY")</td> <td>@Translate("pr. PCS")</td> </tr> </thead> <tbody> @foreach (PriceListViewModel quantityPrice in product.Prices) { <tr> <td>@quantityPrice.Quantity</td> <td>@quantityPrice.Price.PriceFormatted</td> </tr> } </tbody> </table> </div> } } } else if (Pageview.IsVisualEditorMode) { <div class="alert alert-dark m-0" role="alert"> <span>@Translate("No products available")</span> </div> } } else if (Pageview.IsVisualEditorMode) { <div class="alert alert-dark m-0" role="alert"> <span>@Translate("No products available")</span> </div> }
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_2810615515a54af1bc502056430c1fa3.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.Ecommerce.Prices 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 anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", ""); 23 bool anonymousUser = Pageview.User == null; 24 bool isErpConnectionDown = !Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsWebServiceConnectionAvailable"]); 25 bool hidePrice = anonymousUsersLimitations.Contains("price") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHidePrices") && isErpConnectionDown; 26 27 bool productIsDiscontinued = product is object && product.Discontinued; 28 bool doNotShowPriceIfProductIsDiscontinued = Model.Item.GetBoolean("DoNotShowPriceIfProductIsDiscontinued"); 29 var isDiscontinued = productIsDiscontinued && doNotShowPriceIfProductIsDiscontinued; 30 31 string CurrencyTag = "<span class=\"CurrencyCode\">" + product.Price.CurrencyCode + "</span> "; 32 33 } 34 35 @if (product is object && !hidePrice && !isDiscontinued) { 36 bool showInformativePrice = Model.Item.GetBoolean("ShowInformativePrice"); 37 string unitId = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("UnitId")) ? Dynamicweb.Context.Current.Request.Form.Get("UnitId") : string.Empty; 38 39 string priceFontSize = Model.Item.GetRawValueString("PriceSize", "fs-2"); 40 string horizontalAlign = Model.Item.GetRawValueString("HorizontalAlignment", ""); 41 string layout = Model.Item.GetRawValueString("Layout", "horizontal"); 42 string textAlign = horizontalAlign == "center" ? "text-center" : string.Empty; 43 textAlign = horizontalAlign == "end" ? "text-end" : textAlign; 44 45 horizontalAlign = horizontalAlign == "center" && layout == "horizontal" ? "justify-content-center" : horizontalAlign; 46 horizontalAlign = horizontalAlign == "end" && layout == "horizontal" ? "justify-content-end" : horizontalAlign; 47 horizontalAlign = horizontalAlign == "center" && layout == "vertical" ? "align-items-center" : horizontalAlign; 48 horizontalAlign = horizontalAlign == "end" && layout == "vertical" ? "align-items-end" : horizontalAlign; 49 50 string flexDirection = layout == "horizontal" ? string.Empty : "flex-column"; 51 string flexGap = layout == "horizontal" ? "gap-3" : string.Empty; 52 string order = layout == "horizontal" ? string.Empty : "order-2"; 53 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? "theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 54 theme = GetViewParameter("theme") != null ? GetViewParameterString("theme") : theme; 55 56 string contentPadding = Model.Item.GetRawValueString("ContentPadding", ""); 57 contentPadding = contentPadding == "none" ? "p-0" : contentPadding; 58 contentPadding = contentPadding == "small" ? "p-1 px-md-2 py-md-1" : contentPadding; 59 contentPadding = contentPadding == "large" ? "p-2 px-md-3 py-md-2" : contentPadding; 60 61 string showPricesWithVat = Pageview.Area.EcomPricesWithVat.ToLower(); 62 bool neverShowVat = string.IsNullOrEmpty(showPricesWithVat); 63 64 string priceMin = ""; 65 string priceMax = ""; 66 67 string liveInfoClass = ""; 68 string productInfoFeed = ""; 69 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]); 70 if (isLazyLoadingForProductInfoEnabled) 71 { 72 if (Dynamicweb.Context.Current.Items.Contains("ProductInfoFeed")) 73 { 74 productInfoFeed = Dynamicweb.Context.Current.Items["ProductInfoFeed"]?.ToString(); 75 if (!string.IsNullOrEmpty(productInfoFeed)) 76 { 77 productInfoFeed = $"data-product-info-feed=\"{productInfoFeed}\""; 78 } 79 } 80 liveInfoClass = "js-live-info"; 81 } 82 83 84 // var priceContext = new PriceContext(Dynamicweb.Ecommerce.Services.Currencies.GetDefaultCurrency(), Dynamicweb.Ecommerce.Services.Countries.GetCountries().FirstOrDefault(), Dynamicweb.Ecommerce.Services.Shops.GetDefaultShop(), null, false, DateTime.Now); 85 86 87 double discountPriceMinD = 0; 88 double discountPriceMaxD = 0; 89 string discountPriceMin = ""; 90 string discountPriceMax = ""; 91 92 // var productCombinationList = new List<ProductViewModel>(); 93 var productVariants = product.VariantCombinations(); 94 var priceContext = new PriceContext(Dynamicweb.Ecommerce.Services.Currencies.GetDefaultCurrency(), Dynamicweb.Ecommerce.Services.Countries.GetCountries().FirstOrDefault(), Dynamicweb.Ecommerce.Services.Shops.GetDefaultShop(), Dynamicweb.Security.UserManagement.User.GetCurrentFrontendUser(), false, DateTime.Now); 95 96 foreach (var variant in productVariants){ 97 var productVariant = Dynamicweb.Ecommerce.Services.Products.GetProductById(product.Id, variant, true); 98 // D double VariantDiscountD = productVariant.GetDiscountMatrix(priceContext, product.DefaultUnitId, 1).Price.Price; 99 double VariantDiscountD = 0; 100 // string VariantDiscount = productVariant.GetDiscountMatrix(priceContext, product.DefaultUnitId, 1).Price.ToString(); 101 string VariantDiscount = ""; 102 // D string VariantDiscount = CurrencyTag + productVariant.GetDiscountMatrix(priceContext, product.DefaultUnitId, 1).Price.Price.ToString("#.00"); 103 104 105 var variantViewModel = Dynamicweb.Ecommerce.ProductCatalog.ViewModelFactory 106 .CreateView(new ProductViewModelSettings(), product.Id, variant); 107 108 var price = variantViewModel.GetPrice(product.DefaultUnitId); 109 110 double endPrice = price.Price.Price; 111 double beforeDiscountPrice = price.PriceBeforeDiscount.Price; 112 113 if (discountPriceMinD == 0 || endPrice < discountPriceMinD){ 114 discountPriceMinD = endPrice; 115 } 116 117 if (discountPriceMaxD == 0 || endPrice > discountPriceMaxD){ 118 discountPriceMaxD = endPrice; 119 } 120 121 } 122 123 if (discountPriceMaxD > 0){ 124 discountPriceMax = CurrencyTag + discountPriceMaxD.ToString("#.00"); 125 } 126 127 if (discountPriceMinD > 0){ 128 discountPriceMin = CurrencyTag + discountPriceMinD.ToString("#.00"); 129 } 130 131 132 133 134 135 <div class="@textAlign @liveInfoClass item_@Model.Item.SystemName.ToLower()" data-product-id="@product.Id" data-variant-id="@product.VariantId" @productInfoFeed> 136 @if (showInformativePrice && product.PriceInformative.Price != 0) 137 { 138 <div class="opacity-50"> 139 <span>@Translate("RRP") </span> 140 <span class="text-decoration-line-through text-price">@product.PriceInformative.Price.ToString("#.00")</span> 141 </div> 142 } 143 <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"> 144 <span itemprop="priceCurrency" content="@product.Price.CurrencyCode" class="d-none"></span> 145 146 147 @if (showPricesWithVat == "false" && !neverShowVat) 148 { 149 if (isLazyLoadingForProductInfoEnabled && !Pageview.IsVisualEditorMode) 150 { 151 <span itemprop="price" content="" class="d-none"></span> 152 <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> 153 } 154 else 155 { 156 string beforePrice = !string.IsNullOrEmpty(unitId) ? CurrencyTag + product.GetPrice(unitId).PriceBeforeDiscount.PriceWithoutVat.ToString("#.00") : CurrencyTag + product.PriceBeforeDiscount.PriceWithoutVat.ToString("#.00"); 157 158 <span itemprop="price" content="@product.Price.PriceWithoutVat.ToString().Replace(',','.')" class="d-none"></span> 159 if (product.Price.Price != product.PriceBeforeDiscount.Price) 160 { 161 <span class="text-decoration-line-through opacity-75 @order">@beforePrice</span> 162 } 163 } 164 } 165 else 166 { 167 if (isLazyLoadingForProductInfoEnabled && !Pageview.IsVisualEditorMode) 168 { 169 <span itemprop="price" content="" class="d-none"></span> 170 <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> 171 } 172 else 173 { 174 string beforePrice = !string.IsNullOrEmpty(unitId) ? CurrencyTag + product.GetPrice(unitId).PriceBeforeDiscount.Price.ToString("#.00") : CurrencyTag + product.PriceBeforeDiscount.Price.ToString("#.00"); 175 176 <span itemprop="price" content="@product.Price.Price.ToString().Replace(',','.')" class="d-none"></span> 177 178 if (product.Price.Price != product.PriceBeforeDiscount.Price) 179 { 180 <span class="text-decoration-line-through opacity-75 @order"> 181 <span class="text-price" t=a>@beforePrice</span> 182 </span> 183 } 184 } 185 } 186 187 @if (showPricesWithVat == "false" && !neverShowVat) 188 { 189 if (isLazyLoadingForProductInfoEnabled && !Pageview.IsVisualEditorMode) 190 { 191 <span class="text-price js-text-price"> 192 <span class="spinner-border" role="status"></span> 193 </span> 194 } 195 else 196 { 197 string price = !string.IsNullOrEmpty(unitId) ? product.GetPrice(unitId).Price.PriceWithoutVat.ToString("#.00") : product.Price.PriceWithoutVat.ToString("#.00"); 198 199 if (product?.VariantInfo?.VariantInfo != null) 200 { 201 priceMin = product?.VariantInfo?.PriceMin?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithoutVat.ToString("#.00") : ""; 202 priceMax = product?.VariantInfo?.PriceMax?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithoutVat.ToString("#.00") : ""; 203 } 204 if (priceMin != priceMax) 205 { 206 price = priceMin + " - " + priceMax; 207 } 208 <span class="@theme @contentPadding"> 209 <span class="text-price" t=b>@price</span> 210 </span> 211 } 212 } 213 else 214 { 215 if (isLazyLoadingForProductInfoEnabled && !Pageview.IsVisualEditorMode) 216 { 217 <span class="text-price js-text-price"> 218 <span class="spinner-border" role="status"></span> 219 </span> 220 } 221 else 222 { 223 // string price = !string.IsNullOrEmpty(unitId) ? product.GetPrice(unitId).Price.PriceFormatted : product.Price.PriceFormatted; 224 string price = !string.IsNullOrEmpty(unitId) ? CurrencyTag + product.GetPrice(unitId).Price.Price.ToString("#.00") : CurrencyTag + product.Price.Price.ToString("#.00"); 225 226 double priceD = !string.IsNullOrEmpty(unitId) ? product.GetPrice(unitId).Price.Price : product.Price.Price; 227 double priceMinD= 0; 228 double priceMaxD = 0; 229 if (product?.VariantInfo?.VariantInfo != null) 230 { 231 232 // priceMin = product?.VariantInfo?.PriceMin?.PriceFormatted != null ? product.VariantInfo.PriceMin.PriceFormatted : ""; 233 priceMin = product?.VariantInfo?.PriceMin?.PriceFormatted != null ? CurrencyTag + product.VariantInfo.PriceMin.Price.ToString("#.00") : ""; 234 235 priceMinD = product?.VariantInfo?.PriceMin?.Price != null ? product.VariantInfo.PriceMin.Price : 0; 236 237 // priceMax = product?.VariantInfo?.PriceMax?.PriceFormatted != null ? product.VariantInfo.PriceMax.PriceFormatted : ""; 238 priceMax = product?.VariantInfo?.PriceMin?.PriceFormatted != null ? CurrencyTag + product.VariantInfo.PriceMax.Price.ToString("#.00") : ""; 239 240 priceMaxD = product?.VariantInfo?.PriceMax?.Price != null ? product.VariantInfo.PriceMax.Price : 0; 241 } 242 243 if (priceMin != priceMax){ 244 price = priceMin + " - " + priceMax; 245 246 if(priceMinD>discountPriceMinD && priceMaxD>discountPriceMaxD){ 247 price = discountPriceMin + " - " + discountPriceMax; 248 } 249 } 250 if(priceD == 0 && priceMinD > 0 && priceMinD == priceMaxD){ 251 price = priceMin; 252 } 253 254 255 256 <span class="@theme @contentPadding"> 257 <span class="text-price" t=c>@price</span> 258 </span> 259 } 260 } 261 262 @* Stock state for Schema.org, start *@ 263 @{ 264 Uri url = Dynamicweb.Context.Current.Request.Url; 265 } 266 267 <link itemprop="url" href="@url"> 268 269 @{ 270 bool IsNeverOutOfStock = product.NeverOutOfstock; 271 } 272 273 @if (IsNeverOutOfStock) 274 { 275 <span itemprop="availability" class="d-none">InStock</span> 276 <span class="d-none">@Translate("Available in stock")</span> 277 } 278 else 279 { 280 if (product.StockLevel > 0) 281 { 282 <span itemprop="availability" class="d-none">InStock</span> 283 } 284 else 285 { 286 <span itemprop="availability" class="d-none">OutOfStock</span> 287 } 288 } 289 @* Stock state for Schema.org, stop *@ 290 291 </div> 292 293 @if (showPricesWithVat == "false" && !neverShowVat) 294 { 295 if (isLazyLoadingForProductInfoEnabled && !Pageview.IsVisualEditorMode) 296 { 297 <small class="opacity-85 fst-normal js-text-price-with-vat d-none" data-suffix="@Translate("Incl. VAT")"></small> 298 } 299 else 300 { 301 string price = !string.IsNullOrEmpty(unitId) ? product.GetPrice(unitId).Price.PriceWithVat.ToString("#.00") : product.Price.PriceWithVat.ToString("#.00"); 302 303 if (product?.VariantInfo?.VariantInfo != null) 304 { 305 priceMin = product?.VariantInfo?.PriceMin?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithVat.ToString("#.00") : ""; 306 priceMax = product?.VariantInfo?.PriceMax?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithVat.ToString("#.00") : ""; 307 } 308 if (priceMin != priceMax) 309 { 310 price = priceMin + " - " + priceMax; 311 } 312 <small class="opacity-85 fst-normal">@price @Translate("Incl. VAT")</small> 313 } 314 } 315 </div> 316 } 317 else if (Pageview.IsVisualEditorMode) 318 { 319 <div class="alert alert-dark m-0" role="alert"> 320 <span>@Translate("No products available")</span> 321 </div> 322 } 323
Error executing template "Designs/Swift/Paragraph/Swift_ProductStock.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_bb28c877e2bc4e399ad8c04c52d4b6e7.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> @using Dynamicweb.Ecommerce.ProductCatalog @using Dynamicweb.Ecommerce.Stocks @using Dynamicweb.Ecommerce.Products @using Dynamicweb.Ecommerce @using System @{ ProductViewModel product = null; if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) { product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; } else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) { var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); if (productList?.Products is object) { product = productList.Products[0]; } } bool isErpConnectionDown = !Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsWebServiceConnectionAvailable"]); bool hideStock = Model.Item.GetBoolean("HideStockState") || (Pageview.AreaSettings.GetBoolean("ErpDownHideStock") && isErpConnectionDown); } @if (product is object && product.ProductType == Dynamicweb.Ecommerce.Products.ProductType.Stock && !hideStock) { bool IsNeverOutOfStock = false; string horizontalAlign = Model.Item.GetRawValueString("HorizontalAlignment", ""); horizontalAlign = horizontalAlign == "center" ? "text-center" : horizontalAlign; horizontalAlign = horizontalAlign == "end" ? "text-end" : horizontalAlign; bool hasExpectedDelivery = product.ExpectedDelivery != null && product.ExpectedDelivery > DateTime.Now; string expectedDeliveryDate = product.ExpectedDelivery?.ToShortDateString() ?? ""; string liveInfoClass = ""; string productInfoFeed = ""; bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]); if (!IsNeverOutOfStock) { string deliveryLabel = !string.IsNullOrEmpty(product.StockDeliveryText) ? $"{product.StockDeliveryText}" : ""; deliveryLabel += !string.IsNullOrEmpty(product.StockDeliveryValue) ? $" {product.StockDeliveryValue}" : ""; string firstUnitId = product?.UnitOptions?.FirstOrDefault() != null ? product.UnitOptions.FirstOrDefault().Id : ""; string defaultUnitId = !string.IsNullOrEmpty(product.DefaultUnitId) ? product.DefaultUnitId : firstUnitId; string unitId = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("UnitId")) ? Dynamicweb.Context.Current.Request.Form.Get("UnitId") : defaultUnitId; double? currentStockLevel = product.StockLevel; string stockStateLabel = currentStockLevel > 0 ? Translate("In stock") : Translate("Out of stock"); stockStateLabel = !string.IsNullOrEmpty(product.StockStatus) ? product.StockStatus : stockStateLabel; string stockStateCss = currentStockLevel > 0 ? "text-success" : "text-danger"; string stockStateIconCss = currentStockLevel > 0 ? "bg-success" : "bg-danger"; string stockLabel = ""; string stockLabelWebshop = Translate("In local stock"); string stockLabelSupplier = Translate("In remote stock"); string deliveryLabelSupplier = Translate("1-2 days delivery time from remote warehouse"); var todaysDate = DateTime.Today; // var todaysDate = DateTime.Now.AddDays(+1); var parameterDate = product.ExpectedDelivery; string ExpDelivery = ""; if(parameterDate >= todaysDate){ ExpDelivery = string.Format("{0:dd/MM/yyyy}", product.ExpectedDelivery); } // PRODUCTEXPECTEDDELIVERY "Forventes på lager den" double StockWebshop = 0; double StockSupplier = 0; Dictionary<string, FieldValueViewModel> fields = product.ProductFields; string supplierDeliveryTime = ""; foreach (var field in fields) { if (field.Value.SystemName == "CDLevLtid"){ supplierDeliveryTime = field.Value?.Value != null ? field.Value.Value.ToString() : ""; } } //Product productObject = Services.Products.GetProductById(product.Id, product.VariantId, Pageview.Area.EcomLanguageId); //var stockLocations = Dynamicweb.Ecommerce.Stocks.StockService.GetStockLocation(1); //Product productObject = Services.Products.GetProductById(product.Id, product.VariantId, product.LanguageId); IEnumerable<StockLocation> stockLocations = Services.StockService.GetStockLocations(); //double unitStock = productObject.GetUnitStock(stockLocation, unitId); //if (stockLocations.Any() && !string.IsNullOrEmpty(product.VariantId)) //{ // foreach (var stockLocation in stockLocations) // { // if (stockLocation.ID == 1) // { // StockWebshop = productObject.GetUnitStock(stockLocation); // } // if (stockLocation.ID == 2) // { // StockSupplier = productObject.GetUnitStock(stockLocation); // } // } //} //else //{ // foreach (var unit in product.StockUnits) // { // if (unit.StockLocationId == 1) { StockWebshop = unit.StockLevel; } // if (unit.StockLocationId == 2) { StockSupplier = unit.StockLevel; } // } //} var stockUnits = Dynamicweb.Ecommerce.Services.StockService.GetStockUnits(product.Id, product.VariantId).ToList(); if (stockUnits.Any()) { var webShopStockUnit = stockUnits.FirstOrDefault(su => su.StockLocationId == 1); if (webShopStockUnit != null) { StockWebshop = webShopStockUnit.StockQuantity; } var supplierStockUnit = stockUnits.FirstOrDefault(su => su.StockLocationId == 2); if (supplierStockUnit != null) { StockSupplier = supplierStockUnit.StockQuantity; } } if (StockWebshop > 0){ currentStockLevel = StockWebshop; stockLabel = Translate("In stock"); deliveryLabel = ""; stockStateCss = "text-success"; } else if (StockSupplier > 0){ currentStockLevel = StockSupplier; stockLabel = Translate("In stock"); deliveryLabel = Translate("1-2 days delivery time from remote warehouse"); stockStateCss = "text-success"; } else{ currentStockLevel = 0; stockLabel = Translate("Out of stock"); deliveryLabel = supplierDeliveryTime + Translate(" days delivery time"); stockStateCss = "text-danger"; } <div class="js-stock-state @horizontalAlign item_@Model.Item.SystemName.ToLower()"> <div class="small"> @{ bool disableShowStock = false; if (product.VariantInfo.VariantInfo != null && string.IsNullOrEmpty(product.VariantId)){ disableShowStock = true; } } @if (!Model.Item.GetBoolean("HideInventory") && !disableShowStock) { @* if (currentStockLevel > 0){ <span class="@stockStateCss js-text-stock">@currentStockLevel</span> <span class="@stockStateCss">@stockLabel</span> <div class="opacity-75">@deliveryLabel</div> } else { <div class="opacity-75">@deliveryLabel</div> } <hr> *@ if(StockWebshop>0){ <span class="@stockStateCss js-text-stock">@StockWebshop</span> <span class="@stockStateCss">@stockLabelWebshop</span> } if(StockWebshop>0 && StockSupplier>0){ <span class="@stockStateCss">@Translate("and")</span> } if(StockSupplier>0){ <span class="@stockStateCss js-text-stock">@StockSupplier</span> <span class="@stockStateCss">@stockLabelSupplier</span> if(StockWebshop<=0){ <div class="opacity-75">@deliveryLabelSupplier</div> } } if(StockWebshop<=0 && StockSupplier<=0){ <div class="opacity-75">@deliveryLabel</div> } } @if(!string.IsNullOrEmpty(ExpDelivery)){ <div class="fs-6 fw-bold"><br>@Translate("Expected delivery day", "Forventes på lager den") @ExpDelivery</div> } </div> </div> } else if (Pageview.IsVisualEditorMode) { <div class="alert alert-info">@Translate("No products available")</div> } }
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_a5504130add742c39f87065ee4647104.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> @using Dynamicweb.Ecommerce.ProductCatalog @using Dynamicweb.Core.Encoders @using Dynamicweb.Ecommerce.CustomerExperienceCenter.Favorites @using System.Linq @using Dynamicweb.Core @using Dynamicweb.Environment @using System.Globalization @functions { string DoubleToString(double? value) { if (value.HasValue) { return value.Value.ToString(CultureInfo.InvariantCulture); } return null; } } @{ bool isDetailPage = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("ProductID")); ProductViewModel product = null; if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) { product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; } else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) { var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); if (productList?.Products is object) { product = productList.Products[0]; } } string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", ""); bool anonymousUser = Pageview.User == null; bool isErpConnectionDown = !Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsWebServiceConnectionAvailable"]); bool hideAddToCart = anonymousUsersLimitations.Contains("cart") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHideAddToCart") && isErpConnectionDown; hideAddToCart = Pageview.IsVisualEditorMode ? false : hideAddToCart; } @if (product is object && !hideAddToCart) { string horizontalAlign = Model.Item.GetRawValueString("HorizontalAlignment", ""); horizontalAlign = horizontalAlign == "center" ? "justify-content-center" : horizontalAlign; horizontalAlign = horizontalAlign == "end" ? "justify-content-end" : horizontalAlign; horizontalAlign = horizontalAlign == "full" ? "" : horizontalAlign; bool favoritesSelector = !string.IsNullOrEmpty(Model.Item.GetString("ShowAddToFavorites")) ? Model.Item.GetBoolean("ShowAddToFavorites") : false; bool quantitySelector = !string.IsNullOrEmpty(Model.Item.GetString("ShowQuantitySelector")) ? Model.Item.GetBoolean("ShowQuantitySelector") : false; bool unitsSelector = !string.IsNullOrEmpty(Model.Item.GetString("ShowUnitsSelector")) ? Model.Item.GetBoolean("ShowUnitsSelector") : false; bool hideInventory = !string.IsNullOrEmpty(Model.Item.GetString("HideInventory")) ? Model.Item.GetBoolean("HideInventory") : false; bool hideStockState = !string.IsNullOrEmpty(Model.Item.GetString("HideStockState")) ? Model.Item.GetBoolean("HideStockState") : false; string buttonSize = Model.Item.GetRawValueString("ButtonSize", "regular"); string inputSize = string.Empty; Dictionary<string, FieldValueViewModel> fields = product.ProductFields; switch (buttonSize) { case "small": inputSize = " input-group-sm"; buttonSize = " btn-sm"; break; case "regular": buttonSize = string.Empty; break; case "large": inputSize = " input-group-lg"; buttonSize = " btn-lg"; break; } string iconPath = "/Files/icons/"; string url = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("CartService")); if (!url.Contains("LayoutTemplate")) { url += url.Contains("?") ? "&LayoutTemplate=Swift_MiniCart.cshtml" : "?LayoutTemplate=Swift_MiniCart.cshtml"; } string whenVariantsExist = Model.Item.GetRawValueString("WhenVariantsExist", "hide"); string flexFill = Model.Item.GetRawValueString("HorizontalAlignment", "") == "full" ? "flex-fill" : ""; string fullWidth = Model.Item.GetRawValueString("HorizontalAlignment", "") == "full" ? "w-100" : ""; string addToCartIcon = Model.Item.GetRawValueString("Icon", iconPath + "shopping-cart.svg"); string addToCartLabel = !addToCartIcon.Contains("_none") ? $"<span class=\"icon-2\">{ReadFile(addToCartIcon)}</span>" : ""; addToCartLabel += !addToCartIcon.Contains("_none") && !Model.Item.GetBoolean("HideButtonText") ? " " : ""; 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>" : ""; bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]); string disableAddToCart = (product.StockLevel <= 0) ? "disabled" : ""; bool isNeverOutOfStock = product.NeverOutOfstock; disableAddToCart = isNeverOutOfStock && !isLazyLoadingForProductInfoEnabled ? "" : disableAddToCart; bool userHasPendingQuote = Dynamicweb.Ecommerce.Common.Context.Cart != null && Dynamicweb.Ecommerce.Common.Context.Cart.IsQuote; string Surface = ""; string hasLockKeyCode = ""; int CylinderCount = 1; bool KeyCutField = false; foreach (var field in fields) { if (field.Value.SystemName == "HasLockKeyCode"){ hasLockKeyCode = field.Value?.Value != null ? field.Value.Value.ToString() : ""; } if (field.Value.SystemName == "CylinderCount"){ CylinderCount = Convert.ToInt32(field.Value?.Value != null ? field.Value.Value : 1); } if (field.Value.SystemName == "KeyCutField"){ string KeyCutField_string = field.Value?.Value != null ? field.Value.Value.ToString() : ""; if(KeyCutField_string=="True"){KeyCutField = true;} } if (field.Value.SystemName == "Surface"){ Surface = field.Value?.Value != null ? field.Value.Value.ToString() : ""; } } if(CylinderCount<1){CylinderCount=1;} <span style="display:none" id="cylindercountvalue" value="@CylinderCount"></span> if (product.VariantInfo.VariantInfo == null || whenVariantsExist == "disable" && isDetailPage) { string unitId = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("UnitId")) ? Dynamicweb.Context.Current.Request.Form.Get("UnitId") : product.DefaultUnitId; if (string.IsNullOrEmpty(unitId) && product?.UnitOptions != null) { if (product.UnitOptions.FirstOrDefault<UnitOptionViewModel>() != null) { unitId = product.UnitOptions.FirstOrDefault<UnitOptionViewModel>().Id; } } // Flex // IEnumerable<StockLocation> stockLocations = Services.StockService.GetStockLocations(); double StockWebshop = 0; double StockSupplier = 0; var stockUnits = Dynamicweb.Ecommerce.Services.StockService.GetStockUnits(product.Id, product.VariantId).ToList(); if (stockUnits.Any()) { var webShopStockUnit = stockUnits.FirstOrDefault(su => su.StockLocationId == 1); if (webShopStockUnit != null) { StockWebshop = webShopStockUnit.StockQuantity; } var supplierStockUnit = stockUnits.FirstOrDefault(su => su.StockLocationId == 2); if (supplierStockUnit != null) { StockSupplier = supplierStockUnit.StockQuantity; } } double? unitStockLevel = 0; bool HasStock = false; foreach (var unitOption in product.UnitOptions){ if (unitOption.StockLevel > 0){ unitStockLevel = unitOption.StockLevel; HasStock = true; } } if (StockWebshop > 0){ HasStock = true; } else if (StockSupplier > 0){ HasStock = true; } disableAddToCart = HasStock && !isLazyLoadingForProductInfoEnabled ? "" : disableAddToCart; string minQty = product.PurchaseMinimumQuantity != 1 ? $"min=\"{product.PurchaseMinimumQuantity.ToString()}\"" : "min=\"1\""; string stepQty = product.PurchaseQuantityStep > 1 ? product.PurchaseQuantityStep.ToString() : "1"; string valueQty = product.PurchaseMinimumQuantity > product.PurchaseQuantityStep ? product.PurchaseMinimumQuantity.ToString() : stepQty; disableAddToCart = product.VariantInfo.VariantInfo != null && string.IsNullOrEmpty(product.VariantId) ? "disabled" : disableAddToCart; disableAddToCart = product.Discontinued && !product.NeverOutOfstock && !HasStock ? "disabled" : disableAddToCart; bool needKey = false; string KeyGroupID = ""; string KeyList = ""; string DefaultKey = ""; string ReCodeList = ""; string DefaultReCode = ""; string KeyId = ""; string KeyName = ""; string KeyPrice = ""; string keyCode = ""; string KeyWay = ""; string KeyWayType = ""; bool hasKeyWayNew = false; // bool hasKeyWayInCart = false; //.. bool hasKeyInCart = false; bool hasKeyWithCodeInCart = false; foreach (var Groups in product.Groups){ // <small>@Groups.Id</small> if(string.IsNullOrEmpty(KeyGroupID)){ string HasKeyId = Dynamicweb.Ecommerce.Services.ProductGroups.GetGroup(Groups.Id)?.ProductGroupFieldValues.FirstOrDefault(f => f.ProductGroupField.SystemName == "KeyProductIds")?.Value.ToString(); string HasReCodeId = Dynamicweb.Ecommerce.Services.ProductGroups.GetGroup(Groups.Id)?.ProductGroupFieldValues.FirstOrDefault(f => f.ProductGroupField.SystemName == "ReCodeProductIds")?.Value.ToString(); if(!string.IsNullOrEmpty(HasKeyId) || !string.IsNullOrEmpty(HasReCodeId)){ KeyGroupID = Groups.Id; } } } if(!string.IsNullOrEmpty(KeyGroupID)){ KeyList = Dynamicweb.Ecommerce.Services.ProductGroups.GetGroup(KeyGroupID)?.ProductGroupFieldValues.FirstOrDefault(f => f.ProductGroupField.SystemName == "KeyProductIds")?.Value.ToString(); ReCodeList = Dynamicweb.Ecommerce.Services.ProductGroups.GetGroup(KeyGroupID)?.ProductGroupFieldValues.FirstOrDefault(f => f.ProductGroupField.SystemName == "ReCodeProductIds")?.Value.ToString(); KeyWayType = Dynamicweb.Ecommerce.Services.ProductGroups.GetGroup(KeyGroupID)?.ProductGroupFieldValues.FirstOrDefault(f => f.ProductGroupField.SystemName == "KeyWayType")?.Value.ToString(); } List<string> KeyCodes = new List<string>(); string[] Keys = KeyList.Split(','); DefaultKey = Keys[0]; List<string> ReCodes = new List<string> (); string[] ReCode = ReCodeList.Split(','); DefaultReCode = ReCode[0]; if((KeyList.Length>0 || ReCodeList.Length>0) && isDetailPage && !KeyCutField){needKey = true;} if(needKey || KeyCutField){ if (Dynamicweb.Ecommerce.Common.Context.Cart != null) { var CartList = Dynamicweb.Ecommerce.Common.Context.Cart.OrderLines; foreach (var Orderline in CartList){ bool matchKey = false; if(Orderline.OrderLineFieldValues.Count>0){ var KeyWayObj = Orderline.OrderLineFieldValues.FirstOrDefault(fv => fv.OrderLineFieldSystemName == "KeyWayType"); if(!string.IsNullOrEmpty(KeyWayObj?.Value)){KeyWay = KeyWayObj.Value;}else{KeyWay="";} var keyCodeObj = Orderline.OrderLineFieldValues.FirstOrDefault(fv => fv.OrderLineFieldSystemName == "LockKeyCode"); if(!string.IsNullOrEmpty(keyCodeObj?.Value)){keyCode = keyCodeObj.Value;}else{keyCode="";} if(KeyWayType==KeyWay){ matchKey = true; } } foreach (var Fields in Orderline.Product.OrderLineFields){ if(Fields.SystemName=="LockKeyCode" && matchKey){ bool isNew = true; foreach(string K in KeyCodes){if(K==keyCode){isNew=false;}} if(!string.IsNullOrEmpty(keyCode) && isNew){KeyCodes.Add(keyCode);} if(string.IsNullOrEmpty(keyCode)){hasKeyWayNew=true;} } } // finding keycode in cart - end foreach (var Key in Keys){ if(Key == Orderline.ProductId && matchKey){ hasKeyInCart = true; if(!string.IsNullOrEmpty(keyCode)){ hasKeyWithCodeInCart = true; } } } // foreach (var Fields in Orderline.Product.OrderLineFields){ // if(Fields.SystemName == "KeyWayType" && matchKey){ // <small style="font-size:.6em;">@Orderline.Id @Fields.Name @KeyWayType @keyCode<br/></small> // } // } } } } if(needKey || KeyCutField){ // options from cart <datalist id="knowenkeys"> @if(hasKeyWayNew){<option value="">Leave blank to match one in cart</option>} @foreach(string c in KeyCodes){<option value="@c">@c</option>} </datalist> } if(needKey && 1==2){ // test <div style="position:relative; z-index:10;"> <div style="font-size:.7em;color:#aaa;"> <small>KeyCutField: @KeyCutField<br /></small> <small>hasLockKeyCode: @hasLockKeyCode<br /></small> <small>CylinderCount: @CylinderCount<br /></small> <small>need key: @needKey <br/></small> <small>key in cart: @hasKeyInCart <br/></small> <small>key w code in cart: @hasKeyWithCodeInCart</small> </div> </div> } if(needKey && 1==2){ // select options <div class="mt-1 mb-1" style="position:relative; z-index:10;"> @* <label>temp: for adding to cart if recode</label>*@ <select style="font-size: 0.66em;"> @foreach (var Key in Keys){ var KeyProduct = Dynamicweb.Ecommerce.Services.Products.GetProductById(Key,"", product.LanguageId); if(KeyProduct != null){ KeyId = KeyProduct.Id.ToString(); KeyName = KeyProduct.Name.ToString(); KeyPrice = KeyProduct.DefaultPrice.ToString(); // something = KeyProduct.ProductFieldValues.GetProductFieldValue("Width").Value.ToString(); } <option value="@KeyId" @if(KeyId==DefaultKey){<text>selected="selected" </text>}>@KeyName @KeyPrice</option> // <small><br/>@KeyId @KeyName @KeyPrice </small> } </select> </div> } if(needKey && 1==2){ if (hasLockKeyCode == "True" && whenVariantsExist != "disable"){ <div class="js-stock-state text-center"> <div class="small">@Translate("hasLockKeyCode_text")</div> </div> } } var reserveMode = Dynamicweb.Ecommerce.Frontend.Cart.ProductReserve.Mode; if (unitsSelector && product.UnitOptions.Count > 0) { <form f=a method="post" action="/Default.aspx?ID=@(Pageview.Page.ID)&ProductId=@product.Id" id="UnitSelectorForm_@(product.Id)_@(product.VariantId)_@Model.ID"> <input type="hidden" name="redirect" value="false"> <input type="hidden" name="VariantID" value="@product.VariantId"> <input type="hidden" name="UnitID" class="js-unit-id" value="@unitId"> </form> } <div class="d-flex @horizontalAlign @fullWidth js-input-group item_@Model.Item.SystemName.ToLower()"> <form f=b method="post" action="@url" class="@fullWidth" style="z-index: 1"> <input type="hidden" name="redirect" value="false"> <input type="hidden" name="ProductCurrency" value="@Dynamicweb.Ecommerce.Common.Context.Currency.Code"> <input type="hidden" name="ProductPrice" value="@PriceViewModelExtensions.ToStringInvariant(product.Price)"> <input type="hidden" name="ProductReferer" value="component_ProductAddToCart"> @if(!needKey){ <input type="hidden" name="cartcmd" value="add"> <input type="hidden" name="ProductId" value="@product.Id"> <input type="hidden" name="ProductName" value="@HtmlEncoder.HtmlEncode(product.Name)"> <input type="hidden" name="VariantID" value="@product.VariantId"> <input type="hidden" name="ProductVariantName" value="@product.VariantName"> <input id="@(product.Id)_StockLocationId" type="hidden" name="StockLocationId" value=""> if (quantitySelector || (!anonymousUser && product.VariantInfo.VariantInfo != null) || (!anonymousUser && favoritesSelector)) { <input type="hidden" id="Unit_@(product.Id)_@product.VariantId" name="UnitID" value="@unitId" /> } <input type="hidden" name="EcomOrderLineFieldInput_Surface" value="@Surface" /> } @if(needKey){ <input name="cartcmd" value="addmulti" type="hidden" /> <input type="hidden" name="ProductLoopCounter1" value="1" /> <input type="hidden" name="ProductId1" value="@product.Id" /> <input type="hidden" name="ProductName1" value="@product.Name" /> <input type="hidden" name="VariantID1" value="@product.VariantId"> <input type="hidden" name="ProductVariantName1" value="@product.VariantName" /> <input id="@(product.Id)_StockLocationId" type="hidden" name="StockLocationId1" value="" /> if (!string.IsNullOrEmpty(KeyWayType)){ <input type="hidden" name="EcomOrderLineFieldInput_KeyWayType1" value="@KeyWayType" /> } if (quantitySelector || (!anonymousUser && product.VariantInfo.VariantInfo != null) || (!anonymousUser && favoritesSelector)) { <input type="hidden" id="Unit_@(product.Id)_@product.VariantId" name="UnitID1" value="@unitId" /> } <input type="hidden" name="EcomOrderLineFieldInput_Surface1" value="@Surface" /> <input type="hidden" name="EcomOrderLineFieldInput_QuantityPrItem1" value="@CylinderCount" /> <input type="hidden" name="ProductLoopCounter2" value="2" /> <input type="hidden" name="ProductId2" value="@DefaultReCode" /> <input id="Quantity2" type="hidden" name="Quantity2" value="1" disabled="" /> <input type="hidden" name="EcomOrderLineFieldInput_ForProductId2" value="@product.Id" /> <input type="hidden" name="EcomOrderLineFieldInput_ForProductVariantId2" value="@product.VariantId" /> <input type="hidden" name="EcomOrderLineFieldInput_KeyWayType2" value="@KeyWayType" /> <input type="hidden" name="EcomOrderLineFieldInput_LockKeyCode2" id="EcomOrderLineFieldInput_LockKeyCode2" value="" /> <input type="hidden" name="ProductLoopCounter3" value="3" /> <input type="hidden" name="ProductId3" value="@DefaultKey" /> <input id="Quantity3" type="hidden" name="Quantity3" value="1" disabled="" /> <input type="hidden" name="EcomOrderLineFieldInput_IsCodeKey3" value="True" /> <input type="hidden" name="EcomOrderLineFieldInput_ForProductId3" value="@product.Id" /> <input type="hidden" name="EcomOrderLineFieldInput_ForProductVariantId3" value="@product.VariantId" /> <input type="hidden" name="EcomOrderLineFieldInput_KeyWayType3" value="@KeyWayType" /> <input type="hidden" name="EcomOrderLineFieldInput_LockKeyCode3" id="EcomOrderLineFieldInput_LockKeyCode3" value="" /> } @if (reserveMode == Dynamicweb.Ecommerce.Frontend.Cart.ProductReserveMode.AddToCart) { <input type="hidden" name="GetReservedAmount" value="true"> } @if (!string.IsNullOrEmpty(product.VariantId)) { // <input type="hidden" name="VariantId" value="@product.VariantId"> } @if (!product.NeverOutOfstock) { <input type="hidden" name="Stock" value="@unitStockLevel" u="@unitStockLevel" p="@product.StockLevel" sw="@StockWebshop" > <template class="js-out-of-stock-notice"> <div class="modal-header"> <h1 class="modal-title fs-5">@Translate("Stock limit")</h1> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body"> @Translate("There are not enough products in stock. The product might be sold out or discontinued. Please adjust the quantity.") </div> </template> } @if (stepQty != "1") { <template class="js-step-quantity-warning"> <div class="modal-header"> <h1 class="modal-title fs-5">@Translate("The quantity is not valid")</h1> </div> <div class="modal-body"> @Translate("Please select a quantity that is dividable by") @stepQty </div> </template> } @if (product.PurchaseMinimumQuantity != 1) { <template class="js-min-quantity-warning"> <div class="modal-header"> <h1 class="modal-title fs-5">@Translate("The product could not be added to the cart")</h1> </div> <div class="modal-body"> @Translate("The quantity is not valid. You must buy at least") @product.PurchaseMinimumQuantity </div> </template> } <div class="d-flex flex-row w-100"> @if (!quantitySelector) { if(!needKey){ <input id="Quantity_@(product.Id)_@product.VariantId" class="swift_quantity_field" name="Quantity" value="@valueQty" type="hidden" @disableAddToCart> } if(needKey){ <input id="Quantity_@(product.Id)_@product.VariantId" class="swift_quantity_field" name="Quantity1" value="@valueQty" type="hidden" @disableAddToCart> } } @if (unitsSelector && product.UnitOptions.Count > 0) { string selectedUnitName = !string.IsNullOrEmpty(unitId) && product?.UnitOptions != null ? unitId : product.UnitOptions.FirstOrDefault<UnitOptionViewModel>().Name; foreach (var unitOption in product.UnitOptions) { if (unitOption.Id == unitId) { selectedUnitName = unitOption.Name; } } <div class="d-flex flex-column gap-2 w-100"> <div class="input-group input-primary-button-group flex-nowrap@(inputSize)"> @if (!anonymousUser && favoritesSelector) { @RenderPartial("Components/ToggleFavorite.cshtml", product) } @if (quantitySelector) { if(!needKey){ <input id="Quantity_@(product.Id)_@product.VariantId" name="Quantity" value="@valueQty" step="@stepQty" @minQty class="form-control swift_quantity-field" style="min-width: 60px; max-width: 100px; z-index: 1" type="number" onchange="swift.Cart.UpdateOnEnterKey(event)" onkeyup="swift.Cart.UpdateOnEnterKey(event)" @disableAddToCart> } if(needKey){ <input id="Quantity_@(product.Id)_@product.VariantId" name="Quantity1" value="@valueQty" step="@stepQty" @minQty class="form-control swift_quantity-field" style="min-width: 60px; max-width: 100px; z-index: 1" type="number" onchange="swift.Cart.UpdateOnEnterKey(event)" onkeyup="swift.Cart.UpdateOnEnterKey(event)" @disableAddToCart> } } <button class="btn btn-secondary @flexFill dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false"> @selectedUnitName </button> <ul class="dropdown-menu swift_unit-field"> @foreach (var unitOption in product.UnitOptions) { var selectedUnit = unitOption.Id == unitId ? "selected" : ""; <li> <button type="button" class="btn dropdown-item" data-value="@unitOption.Id" onclick="document.querySelector('#UnitSelectorForm_@(product.Id)_@(product.VariantId)_@Model.ID').querySelector('.js-unit-id').value = this.getAttribute('data-value'); document.querySelector('#Unit_@(product.Id)_@product.VariantId').value = this.getAttribute('data-value'); swift.PageUpdater.Update(document.querySelector('#UnitSelectorForm_@(product.Id)_@(product.VariantId)_@Model.ID'))"> <span>@unitOption.Name</span> <span> @if (unitOption.StockLevel > 0) { if (!Model.Item.GetBoolean("HideInventory")) { <span class="small text-success" u=a>@unitOption.StockLevel @Translate("In stock")</span> } else { <span class="small text-success" u=b>@Translate("In stock")</span> } } else { <span class="small text-danger" u=c>@Translate("Out of Stock")</span> } </span> </button> </li> } </ul> </div> <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"> @if (!Model.Item.GetBoolean("HideButtonText")) { <span class="text-nowrap d-flex align-items-center justify-content-center gap-2"> @addToCartLabel </span> } else { @addToCartLabel } </button> </div> } else { if (!anonymousUser && favoritesSelector) { @RenderPartial("Components/ToggleFavorite.cshtml", product) } <div class="input-group input-primary-button-group flex-nowrap@(inputSize)"> @if (quantitySelector) { if(!needKey){ <input id="Quantity_@(product.Id)_@product.VariantId" name="Quantity" value="@valueQty" step="@stepQty" @minQty class="form-control swift_quantity-field" style="min-width: 60px; max-width: 100px; z-index: 1" type="number" onchange="swift.Cart.UpdateOnEnterKey(event)" onkeyup="swift.Cart.UpdateOnEnterKey(event)" @disableAddToCart> } if(needKey){ <input id="Quantity1" name="Quantity1" value="@valueQty" step="@stepQty" @minQty class="form-control swift_quantity-field" style="min-width: 60px; max-width: 100px; z-index: 1" type="number" onchange="ReCodeQuantity();swift.Cart.UpdateOnEnterKey(event)" onkeyup="ReCodeQuantity();swift.Cart.UpdateOnEnterKey(event)" @disableAddToCart> } } <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"> @if (!Model.Item.GetBoolean("HideButtonText")) { <span class="text-nowrap d-flex align-items-center justify-content-center gap-2"> @addToCartLabel </span> } else { @addToCartLabel } </button> </div> } </div> @if (hasLockKeyCode == "True" && whenVariantsExist == "disable"){ <p>&nbsp;</p> <p><input id="LockKeyCode" name="EcomOrderLineFieldInput_ReCoding1" type="checkbox" value="true" onChange="checkboxElement(this,'KeyCodeSection');" /> <label for="LockKeyCode">@Translate("Ønsker den omkodet")</label></p> <div id="KeyCodeSection" class="hide"> @* <small style="font-size: .7em;color: #999;">@DefaultKey @DefaultReCode</small>*@ <div class="mt-4 input-group"> <label for="EcomOrderLineFieldInput_LockKeyCode1">@Translate("Ønsker du låsen/låsene kodet til eksisterende system") <br /> <input list="knowenkeys" style="max-width: 26rem;" class="form-control" type="text" name="EcomOrderLineFieldInput_LockKeyCode1" id="EcomOrderLineFieldInput_LockKeyCode1" onchange="matchCartOption(this)" onkeyup="matchCartOption(this)"> </label> </div> </div> <script> // var CylinderCount = @CylinderCount; var CylinderCount = document.getElementById("cylindercountvalue").getAttribute("value"); // alert(document.getElementById("cylindercountvalue").getAttribute("value")) function checkboxElement(a,b){ c=document.getElementById(b).classList; if(a.checked){c.remove("hide");}else{c.add("hide");} wantReCode(); } function wantReCode(){ a=document.getElementById("LockKeyCode").checked; if(a){ document.getElementById("Quantity2").disabled=false; document.getElementById("EcomOrderLineFieldInput_LockKeyCode1").disabled=false; }else{ document.getElementById("Quantity2").disabled=true; document.getElementById("EcomOrderLineFieldInput_LockKeyCode1").disabled=true; } ReCodeQuantity(); matchCartOption(document.getElementById('EcomOrderLineFieldInput_LockKeyCode1')); } function matchCartOption(a){ m=false;l=a.getAttribute("list");v=a.value; b=document.getElementById(l).getElementsByTagName("option"); c=document.getElementById("LockKeyCode").checked; document.getElementById("EcomOrderLineFieldInput_LockKeyCode2").value=v; document.getElementById("EcomOrderLineFieldInput_LockKeyCode3").value=v; for(i=0;i<b.length;i++){if(b[i].value==v){m=true;}} if(c && !m){ // if wants recode and no match in cart // enable "add key to cart" document.getElementById("Quantity3").disabled=false; }else{ // disable "add key to cart" document.getElementById("Quantity3").disabled=true; } ReCodeQuantity(); } function ReCodeQuantity(){ var Qcount = document.getElementById("Quantity1").value * document.getElementById("cylindercountvalue").getAttribute("value"); // alert(Qcount) // document.getElementById("Quantity2").value=document.getElementById("Quantity1").value; document.getElementById("Quantity2").value=Qcount; } document.addEventListener("DOMContentLoaded", function(){ checkboxElement(document.getElementById('LockKeyCode'),'KeyCodeSection'); }); ReCodeQuantity(); // checkboxElement(document.getElementById('LockKeyCode'),'KeyCodeSection'); // wantReCode(); // ReCodeQuantity(); // matchCartOption(document.getElementById('EcomOrderLineFieldInput_LockKeyCode1')); // alert("bob") </script> <style>.hide{display: none;}</style> } @if (KeyCutField && whenVariantsExist == "disable"){ <div id="KeyCodeSection"> <div class="mt-4 input-group"> <label for="EcomOrderLineFieldInput_LockKeyCode">@Translate("Ønsker du nøglen/nøglernerne kodet til eksisterende system") <br /> <input list="knowenkeys" style="max-width: 26rem;" class="form-control" type="text" name="EcomOrderLineFieldInput_LockKeyCode" id="EcomOrderLineFieldInput_LockKeyCode"> <input type="hidden" name="EcomOrderLineFieldInput_KeyWayType" value="@KeyWayType" /> </label> </div> </div> } </form> </div> } else if (whenVariantsExist == "modal") { string ButtonShape = Model.Item.GetRawValueString("VariantButtonShape", "square"); string buttonAspectRatio = Model.Item.GetRawValueString("VariantImageAspectRatio", "56%"); string buttonText = Translate("Select"); string variantId = !string.IsNullOrWhiteSpace(product.VariantId) ? product.VariantId : product.DefaultVariantId; string variantSelectorServicePageId = !string.IsNullOrEmpty(Model.Item.GetString("VariantSelectorServicePageId")) ? Model.Item.GetLink("VariantSelectorServicePageId").PageId.ToString() : ""; variantSelectorServicePageId = variantSelectorServicePageId != "" ? variantSelectorServicePageId : GetPageIdByNavigationTag("VariantSelectorService").ToString(); // Flex if (hasLockKeyCode == "True"){ <div class="js-stock-state text-center"> <div class="small">@Translate("hasLockKeyCode_text")</div> </div> } <div class="d-flex @horizontalAlign w-100 item_@Model.Item.SystemName.ToLower()"> @if (!anonymousUser && favoritesSelector) { @RenderPartial("Components/ToggleFavorite.cshtml", product) } <form action="/Default.aspx?ID=@variantSelectorServicePageId" data-response-target-element="DynamicModalContent" data-preloader="inline" style="z-index: 1" class="@fullWidth"> <input type="hidden" name="ProductID" value="@product.Id"> <input type="hidden" name="VariantID" value="@variantId"> <input type="hidden" name="QuantitySelector" value="@quantitySelector.ToString()"> <input type="hidden" name="HideInventory" value="@hideInventory.ToString()"> <input type="hidden" name="HideStockState" value="@hideStockState.ToString()"> <input type="hidden" name="ButtonLayout" value="@ButtonShape"> <input type="hidden" name="ButtonAspectRatio" value="@buttonAspectRatio"> <input type="hidden" name="VariantSelectorServicePage" value="@variantSelectorServicePageId"> <input type="hidden" name="ViewType" value="ModalContent"> @if (isLazyLoadingForProductInfoEnabled) { @* If lazy loading is enabled, bypass it because we're loading a modal window, so render everything as if it was server-side *@ <input type="hidden" name="getproductinfo" value="true"> } @{ string link = product.GetProductLink(GetPageIdByNavigationTag("Shop"), false); string googleTagManagerID = Pageview.AreaSettings.GetString("GoogleTagManagerID"); string googleAnalyticsMeasurementID = Pageview.AreaSettings.GetString("GoogleAnalyticsMeasurementID"); bool allowTracking = true; if (CookieManager.IsCookieManagementActive) { var cookieOptInLevel = CookieManager.GetCookieOptInLevel(); allowTracking = cookieOptInLevel == CookieOptInLevel.All || (cookieOptInLevel == CookieOptInLevel.Functional && CookieManager.GetCookieOptInCategories().Contains("Statistical")); } allowTracking = true; string clickProductLink = string.Empty; if ((!string.IsNullOrWhiteSpace(googleAnalyticsMeasurementID) || !string.IsNullOrWhiteSpace(googleTagManagerID)) && allowTracking) { clickProductLink = "onclick=\"return clickProductLink('" + @product.Id + "', '" + @product.Name + "', '" + @product.VariantName + "', '" + @product.Price.CurrencyCode + "', '" + @product.Price.Price + "')\""; } } <a type="button" href="@(link)" class="btn btn-primary@(buttonSize) @fullWidth" title="@Translate("Select")" id="OpenVariantSelectorModal@(product.Id)_@Pageview.CurrentParagraph.ID">@buttonText</a> @*<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>*@ </form> </div> } } else if (Pageview.IsVisualEditorMode) { <div class="alert alert-dark m-0">@Translate("No products available")</div> }

Gratis levering
På alle ordrer over 799 DKK
Dag til dag levering
På lagervarer bestilt inden kl. 12
Over 120.000 tilfredse kunder
Vi står klar til at hjælpe dig
Dansk virksomhed
Få råd og vejledning på mail og telefon

 


 

 


 

Error executing template "Designs/Swift/Paragraph/Swift_ProductSpecification.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_4d3c733b94c84e72a5140a680d0afc01.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 22 @if (product is object) { 23 IEnumerable<string> selectedDisplayGroupIds = Model.Item.GetRawValueString("DisplayGroups").Split(',').ToList(); 24 List<CategoryFieldViewModel> displayGroups = new List<CategoryFieldViewModel>(); 25 26 foreach (var selection in selectedDisplayGroupIds) 27 { 28 foreach (CategoryFieldViewModel group in product.FieldDisplayGroups.Values) 29 { 30 if (selection == group.Id) 31 { 32 int fieldsWithNoValueOrZero = 0; 33 34 foreach (var field in group.Fields) 35 { 36 if (string.IsNullOrEmpty(field.Value.Value.ToString())) 37 { 38 fieldsWithNoValueOrZero++; 39 } 40 } 41 42 if (fieldsWithNoValueOrZero != group.Fields.Count) 43 { 44 displayGroups.Add(group); 45 } 46 } 47 } 48 } 49 50 bool showProductFields = Model.Item.GetBoolean("ProductFields"); 51 52 bool hideTitle = Model.Item.GetBoolean("HideTitle"); 53 54 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 55 56 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "display-4"); 57 58 string contentPadding = Model.Item.GetRawValueString("ContentPadding", ""); 59 contentPadding = contentPadding == "none" ? string.Empty : contentPadding; 60 contentPadding = contentPadding == "small" ? " p-2 p-md-3" : contentPadding; 61 contentPadding = contentPadding == "large" ? " p-4 p-md-5" : contentPadding; 62 63 string layout = Model.Item.GetRawValueString("Layout", "list"); 64 string size = Model.Item.GetRawValueString("Size", "full"); 65 string gaps = size == "full" ? " gap-4" : " gap-2"; 66 67 68 if (Pageview.IsVisualEditorMode && displayGroups.Count() == 0) 69 { 70 product.ProductFields.Clear(); 71 product.ProductFields.Add(Translate("Width"), new FieldValueViewModel { Name = Translate("Width"), Value = "99cm" }); 72 product.ProductFields.Add(Translate("Height"), new FieldValueViewModel { Name = Translate("Height"), Value = "195cm" }); 73 showProductFields = true; 74 } 75 76 if (layout == "commas") 77 { 78 gaps = size == "full" ? " gap-4" : " gap-2"; 79 80 } 81 82 <div class="h-100@(gaps)@(theme)@(contentPadding) item_@Model.Item.SystemName.ToLower()"> 83 <div class="grid"> 84 @if ((product.ProductFields != null && Model.Item.GetBoolean("ProductFields")) || (product.ProductCategories != null && Model.Item.GetBoolean("CategoryFields")) || (displayGroups.Count != 0)) { 85 if (!hideTitle) 86 { 87 <h2 class="g-col-12 @titleFontSize">@Translate(Model.Item.GetString("Title"))</h2> 88 } 89 } 90 91 @if (displayGroups.Count != 0) 92 { 93 if (layout != "accordion") 94 { 95 foreach (var group in displayGroups) 96 { 97 bool hideHeader = Model.Item.GetBoolean("HideGroupHeaders"); 98 99 if (!hideHeader) { 100 <h4 class="g-col-12 h4 mb-0">@group.Name</h4> 101 } 102 103 { @RenderFieldsFromList(group.Fields, layout) } 104 105 } 106 } 107 else 108 { 109 <div class="g-col-12"> 110 <div class="accordion accordion-flush w-100" id="Specifications_@Model.ID"> 111 @foreach (var group in displayGroups) 112 { 113 <div class="accordion-item"> 114 <h2 class="accordion-header" id="SpecificationHeading_@group.Id"> 115 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#SpecificationItem_@group.Id" aria-expanded="false" aria-controls="SpecificationItem_@group.Id"> 116 @group.Name 117 </button> 118 </h2> 119 <div id="SpecificationItem_@group.Id" class="accordion-collapse collapse" aria-labelledby="SpecificationHeading_@group.Id" data-bs-parent="#Specifications_@Model.ID"> 120 <div class="accordion-body"> 121 @{ @RenderFieldsFromList(group.Fields, "list") } 122 </div> 123 </div> 124 </div> 125 } 126 </div> 127 </div> 128 } 129 } 130 131 @if (product.ProductFields != null && showProductFields) 132 { 133 if (product.ProductFields.Count > 0) 134 { 135 if (layout != "accordion") 136 { 137 {@RenderFieldsFromList(product.ProductFields, layout) } 138 } 139 else 140 { 141 <div class="g-col-12"> 142 <div class="accordion accordion-flush w-100" id="Specifications_@Model.ID"> 143 <div class="accordion-item"> 144 <h2 class="accordion-header" id="SpecificationHeading_@Model.ID"> 145 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#SpecificationItem_@Model.ID" aria-expanded="false" aria-controls="SpecificationItem_@Model.ID"> 146 @Translate("Specifications") 147 </button> 148 </h2> 149 <div id="SpecificationItem_@Model.ID" class="accordion-collapse" aria-labelledby="SpecificationHeading_@Model.ID" data-bs-parent="#Specifications_@Model.ID"> 150 <div class="accordion-body"> 151 @{ @RenderFieldsFromList(product.ProductFields, "List") } 152 </div> 153 </div> 154 </div> 155 </div> 156 </div> 157 } 158 } 159 } 160 161 @if (product.ProductCategories != null && Model.Item.GetBoolean("CategoryFields")) 162 { 163 if (product.ProductCategories.Count > 0) 164 { 165 if (layout != "accordion") 166 { 167 foreach (var group in product.ProductCategories) 168 { 169 CategoryFieldViewModel category = group.Value; 170 bool hideHeader = Model.Item.GetBoolean("HideGroupHeaders"); 171 172 if (!hideHeader) { 173 <h4 class="g-col-12 h4 mb-0">@group.Value.Name</h4> 174 } 175 176 { @RenderFieldsFromList(category.Fields, layout) } 177 } 178 } 179 else 180 { 181 <div class="g-col-12"> 182 <div class="accordion accordion-flush w-100" id="Specifications_@Model.ID"> 183 @foreach (var group in product.ProductCategories) 184 { 185 CategoryFieldViewModel category = group.Value; 186 187 <div class="accordion-item"> 188 <h2 class="accordion-header" id="SpecificationHeading_@group.Value.Id"> 189 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#SpecificationItem_@group.Value.Id" aria-expanded="false" aria-controls="SpecificationItem_@group.Value.Id"> 190 @group.Value.Name 191 </button> 192 </h2> 193 <div id="SpecificationItem_@group.Value.Id" class="accordion-collapse" aria-labelledby="SpecificationHeading_@group.Value.Id" data-bs-parent="#Specifications_@Model.ID"> 194 <div class="accordion-body"> 195 @{ @RenderFieldsFromList(category.Fields, "list") } 196 </div> 197 </div> 198 </div> 199 } 200 </div> 201 </div> 202 } 203 } 204 } 205 206 @if (Model.Item.GetBoolean("ShowManufacturerProductPage")){ 207 208 string facturerProductPage = ""; 209 210 foreach (var field in product.ProductFields) { 211 212 213 if (field.Value.SystemName == "ManufacturerProductLink"){ 214 facturerProductPage = field.Value?.Value != null ? field.Value.Value.ToString() : "";; 215 } 216 } 217 218 if (!string.IsNullOrWhiteSpace(facturerProductPage)){ 219 <div class="g-col-12"> 220 <dl class="grid gap-1"> 221 222 <dd class="g-col-12 mb-0 text-break"> 223 <a class="btn btn-secondary" href="@(facturerProductPage)" target="_blank" rel="noopener" title="Link til producent">@Translate("Link til producent")</a> 224 </dd> 225 </dl> 226 </div> 227 } 228 } 229 </div> 230 </div> 231 } 232 else if (Pageview.IsVisualEditorMode) 233 { 234 <div class="alert alert-warning m-0">@Translate("No products available")</div> 235 } 236 237 @functions 238 { 239 string RenderFieldsFromList(Dictionary<string, FieldValueViewModel> fields, string layout) 240 { 241 string size = Model.Item.GetRawValueString("Size", "full"); 242 string gaps = size != "full" ? " gap-1" : string.Empty; 243 bool hideFieldLabels = Model.Item.GetBoolean("HideFieldLabels"); 244 bool hideFieldsWithZeroValue = Model.Item.GetBoolean("HideFieldsWithZeroValue"); 245 246 if (layout == "columns") { 247 <div class="g-col-12"> 248 <div class="grid@(gaps)"> 249 @foreach (var field in fields) 250 { 251 {@RenderField(field.Value, layout)} 252 } 253 </div> 254 </div> 255 } 256 if (layout == "list") { 257 <div class="g-col-12"> 258 <dl class="grid@(gaps)"> 259 @foreach (var field in fields) 260 { 261 {@RenderField(field.Value, layout)} 262 } 263 </dl> 264 </div> 265 } 266 if (layout == "table") 267 { 268 string tableSize = size == "full" ? "" : " table-sm"; 269 <div class="g-col-12"> 270 <table class="table table-striped@(tableSize)"> 271 @foreach (var field in fields) 272 { 273 {@RenderField(field.Value, layout)} 274 } 275 </table> 276 </div> 277 } 278 if (layout == "bullets") 279 { 280 string listSize = size == "full" ? "" : "m-0 p-0 lh-1 fs-7 opacity-75"; 281 string listStyle = size == "full" ? "" : "style=\"list-style-position: inside\""; 282 <div class="g-col-12"> 283 <ul class="@listSize" @listStyle> 284 @foreach (var field in fields) 285 { 286 {@RenderField(field.Value, layout)} 287 } 288 </ul> 289 </div> 290 } 291 if (layout == "commas") 292 { 293 List<string> featuresList = new List<string>(); 294 295 foreach (var field in fields) 296 { 297 string firstListItemValue = string.Empty; //Hack to support field type providers with a single value 298 299 if (field.Value?.Value != null) 300 { 301 if (field.Value.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 302 { 303 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 304 305 //Hack to support field type providers with a single value 306 if (values.FirstOrDefault() != null) 307 { 308 firstListItemValue = values.FirstOrDefault().Value; 309 } 310 } 311 } 312 313 if (!hideFieldsWithZeroValue || (firstListItemValue != "0" && firstListItemValue != "0.0" && field.Value.Value.ToString() != "0" && field.Value.Value.ToString() != "0.0")) 314 { 315 if (field.Value.Value is object && !string.IsNullOrEmpty(field.Value.Value.ToString())) 316 { 317 if (field.Value.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 318 { 319 List<string> options = new List<string>(); 320 foreach (FieldOptionValueViewModel option in field.Value.Value as System.Collections.Generic.List<FieldOptionValueViewModel>) 321 { 322 if (!string.IsNullOrWhiteSpace(option.Value)) 323 { 324 if (option.Value.ToString().Contains("#") && (Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour"))) 325 { 326 string colorSpan = "<span class=\"colorbox-sm\" style=\"background-color: " + option.Value + "\"></span>"; 327 options.Add(colorSpan); 328 } 329 else if (!string.IsNullOrEmpty(option.Value)) 330 { 331 options.Add(option.Name); 332 } 333 } 334 } 335 string optionsString = (string.Join(", ", options.Select(x => x.ToString()).ToArray())); 336 if ((Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour"))) 337 { 338 optionsString = (string.Join(" ", options.Select(x => x.ToString()).ToArray())); 339 } 340 341 if (!string.IsNullOrEmpty(optionsString)) 342 { 343 if (!hideFieldLabels) 344 { 345 featuresList.Add(field.Value.Name + ": " + optionsString); 346 } 347 else 348 { 349 featuresList.Add(optionsString); 350 } 351 } 352 } 353 else 354 { 355 if (!string.IsNullOrWhiteSpace(field.Value.Value.ToString())) 356 { 357 if (field.Value.Value.ToString().Contains("#") && (Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour"))) 358 { 359 string colorSpan = "<span class=\"colorbox-sm\" style=\"background-color: " + field.Value.Value + "\"></span>"; 360 361 if (!hideFieldLabels) 362 { 363 featuresList.Add(field.Value.Name + ": " + colorSpan); 364 } 365 else 366 { 367 featuresList.Add(colorSpan); 368 } 369 } 370 else 371 { 372 if (!hideFieldLabels) 373 { 374 featuresList.Add(field.Value.Name + ": " + field.Value.Value.ToString()); 375 } 376 else 377 { 378 featuresList.Add(field.Value.Value.ToString()); 379 } 380 } 381 } 382 } 383 } 384 } 385 } 386 387 string featuresString = (string.Join(", ", featuresList.Select(x => x.ToString()).ToArray())); 388 389 <div class="g-col-12 opacity-75 fs-7">@featuresString</div> 390 } 391 return string.Empty; 392 } 393 394 string RenderField(FieldValueViewModel field, string layout) 395 { 396 string size = Model.Item.GetRawValueString("Size", "full"); 397 string fieldValue = field?.Value != null ? field.Value.ToString() : ""; 398 bool hideFieldLabels = Model.Item.GetBoolean("HideFieldLabels"); 399 bool noValues = false; 400 string firstListItemValue = string.Empty; //Hack to support field type providers with a single value 401 bool hideFieldsWithZeroValue = Model.Item.GetBoolean("HideFieldsWithZeroValue"); 402 403 if (!string.IsNullOrEmpty(fieldValue)) 404 { 405 if (field.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 406 { 407 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 408 noValues = values.Count > 0 ? false : true; 409 410 //Hack to support field type providers with a single value 411 if (values.FirstOrDefault() != null) 412 { 413 firstListItemValue = values.FirstOrDefault().Value; 414 } 415 } 416 } 417 418 if (!string.IsNullOrEmpty(fieldValue) && noValues == false) 419 { 420 if (!hideFieldsWithZeroValue || (firstListItemValue != "0" && firstListItemValue != "0.0" && field.Value.ToString() != "0" && field.Value.ToString() != "0.0")) 421 { 422 if (layout == "columns") 423 { 424 425 <div class="grid g-col-6 g-col-lg-4 gap-1"> 426 @if (!hideFieldLabels) 427 { 428 <dt class="g-col-12 g-col-lg-4" t=a>@Translate(field.Name)</dt> 429 } 430 <dd class="g-col-12 g-col-lg-8 mb-0 text-break"> 431 @{ @RenderFieldValue(field) } 432 </dd> 433 </div> 434 } 435 if (layout == "list") 436 { 437 if (!hideFieldLabels) 438 { 439 <dt class="g-col-4" t=b>@Translate(field.Name)</dt> 440 } 441 <dd class="g-col-8 mb-0 text-break"> 442 @{ @RenderFieldValue(field) } 443 </dd> 444 } 445 if (layout == "table") 446 { 447 <tr> 448 @if (!hideFieldLabels) 449 { 450 <th class="w-25 w-lg-50" scope="row">@Translate(field.Name)</th> 451 } 452 <td class="text-break"> 453 @{ @RenderFieldValue(field) } 454 </td> 455 </tr> 456 } 457 if (layout == "bullets") 458 { 459 <li> 460 @if (!hideFieldLabels) 461 { 462 <strong>@Translate(field.Name)</strong> 463 } 464 <span> 465 @{ @RenderFieldValue(field) } 466 </span> 467 </li> 468 } 469 } 470 } 471 return string.Empty; 472 } 473 474 string RenderFieldValue(FieldValueViewModel field) 475 { 476 string fieldValue = field?.Value != null ? field.Value.ToString() : ""; 477 478 bool isLink = field?.Type == "Link"; 479 bool isColor = false; 480 bool isBrandName = field?.SystemName == "Brand_name"; 481 482 fieldValue = fieldValue == "False" ? Translate("No") : fieldValue; 483 fieldValue = fieldValue == "True" ? Translate("Yes") : fieldValue; 484 485 486 if (field.Value.GetType() == typeof(System.Collections.Generic.List<Dynamicweb.Ecommerce.ProductCatalog.FieldOptionValueViewModel>)) 487 { 488 int valueCount = 0; 489 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 490 int totalValues = values.Count; 491 492 foreach (FieldOptionValueViewModel option in values) 493 { 494 if (!string.IsNullOrEmpty(option.Value)) 495 { 496 if (option.Value.Substring(0, 1) == "#") 497 { 498 isColor = true; 499 } 500 } 501 502 if (!isColor) 503 { 504 @option.Name 505 } 506 else 507 { 508 <span class="colorbox-sm" style="background-color: @option.Value" title="@option.Name"></span> 509 } 510 511 if (valueCount != totalValues && valueCount < (totalValues - 1)) 512 { 513 if (isColor) 514 { 515 <text> </text> 516 } 517 else 518 { 519 <text>, </text> 520 } 521 } 522 valueCount++; 523 } 524 } 525 else 526 { 527 if (fieldValue.Substring(0, 1) == "#") 528 { 529 isColor = true; 530 } 531 532 if (!isColor) 533 { 534 if (isLink) 535 { 536 string linktTitle = !fieldValue.Contains("aspx") ? fieldValue : Translate("Go to link"); 537 string target = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") && fieldValue.Contains("http") ? "target=\"_blank\"" : string.Empty; 538 string rel = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") && fieldValue.Contains("http") ? "rel=\"noopener\"" : string.Empty; 539 540 <a href="@field.Value" title="@field.Name" @target @rel>@linktTitle</a> 541 } 542 else if (isBrandName) 543 { 544 <span itemprop="brand" itemtype="https://schema.org/Brand" itemscope> 545 <span itemprop="name">@fieldValue</span> 546 </span> 547 } 548 else 549 { 550 @fieldValue 551 } 552 553 } 554 else 555 { 556 <span class="colorbox-sm" style="background-color: @fieldValue" title="@fieldValue"></span> 557 } 558 } 559 return string.Empty; 560 } 561 }
Error executing template "Designs/Swift/Paragraph/Swift_ProductSpecification.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_4d3c733b94c84e72a5140a680d0afc01.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 22 @if (product is object) { 23 IEnumerable<string> selectedDisplayGroupIds = Model.Item.GetRawValueString("DisplayGroups").Split(',').ToList(); 24 List<CategoryFieldViewModel> displayGroups = new List<CategoryFieldViewModel>(); 25 26 foreach (var selection in selectedDisplayGroupIds) 27 { 28 foreach (CategoryFieldViewModel group in product.FieldDisplayGroups.Values) 29 { 30 if (selection == group.Id) 31 { 32 int fieldsWithNoValueOrZero = 0; 33 34 foreach (var field in group.Fields) 35 { 36 if (string.IsNullOrEmpty(field.Value.Value.ToString())) 37 { 38 fieldsWithNoValueOrZero++; 39 } 40 } 41 42 if (fieldsWithNoValueOrZero != group.Fields.Count) 43 { 44 displayGroups.Add(group); 45 } 46 } 47 } 48 } 49 50 bool showProductFields = Model.Item.GetBoolean("ProductFields"); 51 52 bool hideTitle = Model.Item.GetBoolean("HideTitle"); 53 54 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 55 56 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "display-4"); 57 58 string contentPadding = Model.Item.GetRawValueString("ContentPadding", ""); 59 contentPadding = contentPadding == "none" ? string.Empty : contentPadding; 60 contentPadding = contentPadding == "small" ? " p-2 p-md-3" : contentPadding; 61 contentPadding = contentPadding == "large" ? " p-4 p-md-5" : contentPadding; 62 63 string layout = Model.Item.GetRawValueString("Layout", "list"); 64 string size = Model.Item.GetRawValueString("Size", "full"); 65 string gaps = size == "full" ? " gap-4" : " gap-2"; 66 67 68 if (Pageview.IsVisualEditorMode && displayGroups.Count() == 0) 69 { 70 product.ProductFields.Clear(); 71 product.ProductFields.Add(Translate("Width"), new FieldValueViewModel { Name = Translate("Width"), Value = "99cm" }); 72 product.ProductFields.Add(Translate("Height"), new FieldValueViewModel { Name = Translate("Height"), Value = "195cm" }); 73 showProductFields = true; 74 } 75 76 if (layout == "commas") 77 { 78 gaps = size == "full" ? " gap-4" : " gap-2"; 79 80 } 81 82 <div class="h-100@(gaps)@(theme)@(contentPadding) item_@Model.Item.SystemName.ToLower()"> 83 <div class="grid"> 84 @if ((product.ProductFields != null && Model.Item.GetBoolean("ProductFields")) || (product.ProductCategories != null && Model.Item.GetBoolean("CategoryFields")) || (displayGroups.Count != 0)) { 85 if (!hideTitle) 86 { 87 <h2 class="g-col-12 @titleFontSize">@Translate(Model.Item.GetString("Title"))</h2> 88 } 89 } 90 91 @if (displayGroups.Count != 0) 92 { 93 if (layout != "accordion") 94 { 95 foreach (var group in displayGroups) 96 { 97 bool hideHeader = Model.Item.GetBoolean("HideGroupHeaders"); 98 99 if (!hideHeader) { 100 <h4 class="g-col-12 h4 mb-0">@group.Name</h4> 101 } 102 103 { @RenderFieldsFromList(group.Fields, layout) } 104 105 } 106 } 107 else 108 { 109 <div class="g-col-12"> 110 <div class="accordion accordion-flush w-100" id="Specifications_@Model.ID"> 111 @foreach (var group in displayGroups) 112 { 113 <div class="accordion-item"> 114 <h2 class="accordion-header" id="SpecificationHeading_@group.Id"> 115 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#SpecificationItem_@group.Id" aria-expanded="false" aria-controls="SpecificationItem_@group.Id"> 116 @group.Name 117 </button> 118 </h2> 119 <div id="SpecificationItem_@group.Id" class="accordion-collapse collapse" aria-labelledby="SpecificationHeading_@group.Id" data-bs-parent="#Specifications_@Model.ID"> 120 <div class="accordion-body"> 121 @{ @RenderFieldsFromList(group.Fields, "list") } 122 </div> 123 </div> 124 </div> 125 } 126 </div> 127 </div> 128 } 129 } 130 131 @if (product.ProductFields != null && showProductFields) 132 { 133 if (product.ProductFields.Count > 0) 134 { 135 if (layout != "accordion") 136 { 137 {@RenderFieldsFromList(product.ProductFields, layout) } 138 } 139 else 140 { 141 <div class="g-col-12"> 142 <div class="accordion accordion-flush w-100" id="Specifications_@Model.ID"> 143 <div class="accordion-item"> 144 <h2 class="accordion-header" id="SpecificationHeading_@Model.ID"> 145 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#SpecificationItem_@Model.ID" aria-expanded="false" aria-controls="SpecificationItem_@Model.ID"> 146 @Translate("Specifications") 147 </button> 148 </h2> 149 <div id="SpecificationItem_@Model.ID" class="accordion-collapse" aria-labelledby="SpecificationHeading_@Model.ID" data-bs-parent="#Specifications_@Model.ID"> 150 <div class="accordion-body"> 151 @{ @RenderFieldsFromList(product.ProductFields, "List") } 152 </div> 153 </div> 154 </div> 155 </div> 156 </div> 157 } 158 } 159 } 160 161 @if (product.ProductCategories != null && Model.Item.GetBoolean("CategoryFields")) 162 { 163 if (product.ProductCategories.Count > 0) 164 { 165 if (layout != "accordion") 166 { 167 foreach (var group in product.ProductCategories) 168 { 169 CategoryFieldViewModel category = group.Value; 170 bool hideHeader = Model.Item.GetBoolean("HideGroupHeaders"); 171 172 if (!hideHeader) { 173 <h4 class="g-col-12 h4 mb-0">@group.Value.Name</h4> 174 } 175 176 { @RenderFieldsFromList(category.Fields, layout) } 177 } 178 } 179 else 180 { 181 <div class="g-col-12"> 182 <div class="accordion accordion-flush w-100" id="Specifications_@Model.ID"> 183 @foreach (var group in product.ProductCategories) 184 { 185 CategoryFieldViewModel category = group.Value; 186 187 <div class="accordion-item"> 188 <h2 class="accordion-header" id="SpecificationHeading_@group.Value.Id"> 189 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#SpecificationItem_@group.Value.Id" aria-expanded="false" aria-controls="SpecificationItem_@group.Value.Id"> 190 @group.Value.Name 191 </button> 192 </h2> 193 <div id="SpecificationItem_@group.Value.Id" class="accordion-collapse" aria-labelledby="SpecificationHeading_@group.Value.Id" data-bs-parent="#Specifications_@Model.ID"> 194 <div class="accordion-body"> 195 @{ @RenderFieldsFromList(category.Fields, "list") } 196 </div> 197 </div> 198 </div> 199 } 200 </div> 201 </div> 202 } 203 } 204 } 205 206 @if (Model.Item.GetBoolean("ShowManufacturerProductPage")){ 207 208 string facturerProductPage = ""; 209 210 foreach (var field in product.ProductFields) { 211 212 213 if (field.Value.SystemName == "ManufacturerProductLink"){ 214 facturerProductPage = field.Value?.Value != null ? field.Value.Value.ToString() : "";; 215 } 216 } 217 218 if (!string.IsNullOrWhiteSpace(facturerProductPage)){ 219 <div class="g-col-12"> 220 <dl class="grid gap-1"> 221 222 <dd class="g-col-12 mb-0 text-break"> 223 <a class="btn btn-secondary" href="@(facturerProductPage)" target="_blank" rel="noopener" title="Link til producent">@Translate("Link til producent")</a> 224 </dd> 225 </dl> 226 </div> 227 } 228 } 229 </div> 230 </div> 231 } 232 else if (Pageview.IsVisualEditorMode) 233 { 234 <div class="alert alert-warning m-0">@Translate("No products available")</div> 235 } 236 237 @functions 238 { 239 string RenderFieldsFromList(Dictionary<string, FieldValueViewModel> fields, string layout) 240 { 241 string size = Model.Item.GetRawValueString("Size", "full"); 242 string gaps = size != "full" ? " gap-1" : string.Empty; 243 bool hideFieldLabels = Model.Item.GetBoolean("HideFieldLabels"); 244 bool hideFieldsWithZeroValue = Model.Item.GetBoolean("HideFieldsWithZeroValue"); 245 246 if (layout == "columns") { 247 <div class="g-col-12"> 248 <div class="grid@(gaps)"> 249 @foreach (var field in fields) 250 { 251 {@RenderField(field.Value, layout)} 252 } 253 </div> 254 </div> 255 } 256 if (layout == "list") { 257 <div class="g-col-12"> 258 <dl class="grid@(gaps)"> 259 @foreach (var field in fields) 260 { 261 {@RenderField(field.Value, layout)} 262 } 263 </dl> 264 </div> 265 } 266 if (layout == "table") 267 { 268 string tableSize = size == "full" ? "" : " table-sm"; 269 <div class="g-col-12"> 270 <table class="table table-striped@(tableSize)"> 271 @foreach (var field in fields) 272 { 273 {@RenderField(field.Value, layout)} 274 } 275 </table> 276 </div> 277 } 278 if (layout == "bullets") 279 { 280 string listSize = size == "full" ? "" : "m-0 p-0 lh-1 fs-7 opacity-75"; 281 string listStyle = size == "full" ? "" : "style=\"list-style-position: inside\""; 282 <div class="g-col-12"> 283 <ul class="@listSize" @listStyle> 284 @foreach (var field in fields) 285 { 286 {@RenderField(field.Value, layout)} 287 } 288 </ul> 289 </div> 290 } 291 if (layout == "commas") 292 { 293 List<string> featuresList = new List<string>(); 294 295 foreach (var field in fields) 296 { 297 string firstListItemValue = string.Empty; //Hack to support field type providers with a single value 298 299 if (field.Value?.Value != null) 300 { 301 if (field.Value.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 302 { 303 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 304 305 //Hack to support field type providers with a single value 306 if (values.FirstOrDefault() != null) 307 { 308 firstListItemValue = values.FirstOrDefault().Value; 309 } 310 } 311 } 312 313 if (!hideFieldsWithZeroValue || (firstListItemValue != "0" && firstListItemValue != "0.0" && field.Value.Value.ToString() != "0" && field.Value.Value.ToString() != "0.0")) 314 { 315 if (field.Value.Value is object && !string.IsNullOrEmpty(field.Value.Value.ToString())) 316 { 317 if (field.Value.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 318 { 319 List<string> options = new List<string>(); 320 foreach (FieldOptionValueViewModel option in field.Value.Value as System.Collections.Generic.List<FieldOptionValueViewModel>) 321 { 322 if (!string.IsNullOrWhiteSpace(option.Value)) 323 { 324 if (option.Value.ToString().Contains("#") && (Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour"))) 325 { 326 string colorSpan = "<span class=\"colorbox-sm\" style=\"background-color: " + option.Value + "\"></span>"; 327 options.Add(colorSpan); 328 } 329 else if (!string.IsNullOrEmpty(option.Value)) 330 { 331 options.Add(option.Name); 332 } 333 } 334 } 335 string optionsString = (string.Join(", ", options.Select(x => x.ToString()).ToArray())); 336 if ((Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour"))) 337 { 338 optionsString = (string.Join(" ", options.Select(x => x.ToString()).ToArray())); 339 } 340 341 if (!string.IsNullOrEmpty(optionsString)) 342 { 343 if (!hideFieldLabels) 344 { 345 featuresList.Add(field.Value.Name + ": " + optionsString); 346 } 347 else 348 { 349 featuresList.Add(optionsString); 350 } 351 } 352 } 353 else 354 { 355 if (!string.IsNullOrWhiteSpace(field.Value.Value.ToString())) 356 { 357 if (field.Value.Value.ToString().Contains("#") && (Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour"))) 358 { 359 string colorSpan = "<span class=\"colorbox-sm\" style=\"background-color: " + field.Value.Value + "\"></span>"; 360 361 if (!hideFieldLabels) 362 { 363 featuresList.Add(field.Value.Name + ": " + colorSpan); 364 } 365 else 366 { 367 featuresList.Add(colorSpan); 368 } 369 } 370 else 371 { 372 if (!hideFieldLabels) 373 { 374 featuresList.Add(field.Value.Name + ": " + field.Value.Value.ToString()); 375 } 376 else 377 { 378 featuresList.Add(field.Value.Value.ToString()); 379 } 380 } 381 } 382 } 383 } 384 } 385 } 386 387 string featuresString = (string.Join(", ", featuresList.Select(x => x.ToString()).ToArray())); 388 389 <div class="g-col-12 opacity-75 fs-7">@featuresString</div> 390 } 391 return string.Empty; 392 } 393 394 string RenderField(FieldValueViewModel field, string layout) 395 { 396 string size = Model.Item.GetRawValueString("Size", "full"); 397 string fieldValue = field?.Value != null ? field.Value.ToString() : ""; 398 bool hideFieldLabels = Model.Item.GetBoolean("HideFieldLabels"); 399 bool noValues = false; 400 string firstListItemValue = string.Empty; //Hack to support field type providers with a single value 401 bool hideFieldsWithZeroValue = Model.Item.GetBoolean("HideFieldsWithZeroValue"); 402 403 if (!string.IsNullOrEmpty(fieldValue)) 404 { 405 if (field.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 406 { 407 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 408 noValues = values.Count > 0 ? false : true; 409 410 //Hack to support field type providers with a single value 411 if (values.FirstOrDefault() != null) 412 { 413 firstListItemValue = values.FirstOrDefault().Value; 414 } 415 } 416 } 417 418 if (!string.IsNullOrEmpty(fieldValue) && noValues == false) 419 { 420 if (!hideFieldsWithZeroValue || (firstListItemValue != "0" && firstListItemValue != "0.0" && field.Value.ToString() != "0" && field.Value.ToString() != "0.0")) 421 { 422 if (layout == "columns") 423 { 424 425 <div class="grid g-col-6 g-col-lg-4 gap-1"> 426 @if (!hideFieldLabels) 427 { 428 <dt class="g-col-12 g-col-lg-4" t=a>@Translate(field.Name)</dt> 429 } 430 <dd class="g-col-12 g-col-lg-8 mb-0 text-break"> 431 @{ @RenderFieldValue(field) } 432 </dd> 433 </div> 434 } 435 if (layout == "list") 436 { 437 if (!hideFieldLabels) 438 { 439 <dt class="g-col-4" t=b>@Translate(field.Name)</dt> 440 } 441 <dd class="g-col-8 mb-0 text-break"> 442 @{ @RenderFieldValue(field) } 443 </dd> 444 } 445 if (layout == "table") 446 { 447 <tr> 448 @if (!hideFieldLabels) 449 { 450 <th class="w-25 w-lg-50" scope="row">@Translate(field.Name)</th> 451 } 452 <td class="text-break"> 453 @{ @RenderFieldValue(field) } 454 </td> 455 </tr> 456 } 457 if (layout == "bullets") 458 { 459 <li> 460 @if (!hideFieldLabels) 461 { 462 <strong>@Translate(field.Name)</strong> 463 } 464 <span> 465 @{ @RenderFieldValue(field) } 466 </span> 467 </li> 468 } 469 } 470 } 471 return string.Empty; 472 } 473 474 string RenderFieldValue(FieldValueViewModel field) 475 { 476 string fieldValue = field?.Value != null ? field.Value.ToString() : ""; 477 478 bool isLink = field?.Type == "Link"; 479 bool isColor = false; 480 bool isBrandName = field?.SystemName == "Brand_name"; 481 482 fieldValue = fieldValue == "False" ? Translate("No") : fieldValue; 483 fieldValue = fieldValue == "True" ? Translate("Yes") : fieldValue; 484 485 486 if (field.Value.GetType() == typeof(System.Collections.Generic.List<Dynamicweb.Ecommerce.ProductCatalog.FieldOptionValueViewModel>)) 487 { 488 int valueCount = 0; 489 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 490 int totalValues = values.Count; 491 492 foreach (FieldOptionValueViewModel option in values) 493 { 494 if (!string.IsNullOrEmpty(option.Value)) 495 { 496 if (option.Value.Substring(0, 1) == "#") 497 { 498 isColor = true; 499 } 500 } 501 502 if (!isColor) 503 { 504 @option.Name 505 } 506 else 507 { 508 <span class="colorbox-sm" style="background-color: @option.Value" title="@option.Name"></span> 509 } 510 511 if (valueCount != totalValues && valueCount < (totalValues - 1)) 512 { 513 if (isColor) 514 { 515 <text> </text> 516 } 517 else 518 { 519 <text>, </text> 520 } 521 } 522 valueCount++; 523 } 524 } 525 else 526 { 527 if (fieldValue.Substring(0, 1) == "#") 528 { 529 isColor = true; 530 } 531 532 if (!isColor) 533 { 534 if (isLink) 535 { 536 string linktTitle = !fieldValue.Contains("aspx") ? fieldValue : Translate("Go to link"); 537 string target = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") && fieldValue.Contains("http") ? "target=\"_blank\"" : string.Empty; 538 string rel = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") && fieldValue.Contains("http") ? "rel=\"noopener\"" : string.Empty; 539 540 <a href="@field.Value" title="@field.Name" @target @rel>@linktTitle</a> 541 } 542 else if (isBrandName) 543 { 544 <span itemprop="brand" itemtype="https://schema.org/Brand" itemscope> 545 <span itemprop="name">@fieldValue</span> 546 </span> 547 } 548 else 549 { 550 @fieldValue 551 } 552 553 } 554 else 555 { 556 <span class="colorbox-sm" style="background-color: @fieldValue" title="@fieldValue"></span> 557 } 558 } 559 return string.Empty; 560 } 561 }
Error executing template "Designs/Swift/Paragraph/Swift_ProductDetailsMediaTable.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_3cf99cbc0d9743b18a564a3a449243cc.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> @using Dynamicweb.Ecommerce.ProductCatalog @using System.Text.RegularExpressions; @using System.IO @functions { public ProductViewModel product { get; set; } = new ProductViewModel(); public string[] supportedImageFormats { get; set; } public string[] supportedVideoFormats { get; set; } public string[] supportedDocumentFormats { get; set; } public string[] allSupportedFormats { get; set; } public class RatioSettings { public string Ratio { get; set; } public string CssClass { get; set; } public string CssVariable { get; set; } public string Fill { get; set; } } public RatioSettings GetRatioSettings() { var ratioSettings = new RatioSettings(); string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); ratio = ratio != "0" ? ratio : ""; string cssClass = ratio != "" && ratio != "fill" ? " ratio" : ""; string cssVariable = ratio != "" && ratio != "fill" ? "--bs-aspect-ratio: " + ratio : ""; ratioSettings.Ratio = ratio; ratioSettings.CssClass = cssClass; ratioSettings.CssVariable = cssVariable; ratioSettings.Fill = ratio == "fill" ? " h-100" : ""; return ratioSettings; } public Dictionary<string, object> GetVideoParams(MediaViewModel asset, string size) { string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Name; string type = GetVideoType(asset.Value); bool openInModal = Model.Item.GetString("OpenVideoInModal") == "true" ? true : false; bool autoPlay = Model.Item.GetBoolean("VideoAutoPlay"); var videoParams = new Dictionary<string, object>(); videoParams.Add("AssetName", asset.Name); videoParams.Add("AssetVideoType", type); videoParams.Add("AssetDisplayName", asset.DisplayName); videoParams.Add("OpenVideoInModal", openInModal); videoParams.Add("VideoAutoPlay", autoPlay); videoParams.Add("Size", size); videoParams.Add("Id", Model.ID); return videoParams; } public string GetVideoType(string assetValue) { string type = assetValue.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "youtube" : string.Empty; type = assetValue.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "vimeo" : type; type = string.IsNullOrEmpty(type) ? "selfhosted" : type; return type; } public string GetYoutubeScreenDump(string assetValue) { var regex = new Regex(@"(?:youtube\.com\/.*[\?&]v=|youtu\.be\/|youtube\.com\/embed\/)([\w-]+)(?:\?.*)?"); Match match = regex.Match(assetValue); string videoId = match.Success ? match.Groups[1].Value : string.Empty; string youtubeThumbnail = $"https://img.youtube.com/vi/{videoId}/mqdefault.jpg"; return youtubeThumbnail; } } @{ @* Get the product data *@ ProductViewModel product = null; if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) { product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; } else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) { var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); if (productList?.Products is object) { product = productList.Products[0]; } } } @if (product is object) { @* Supported formats *@ supportedImageFormats = new string[] { ".jpg", ".jpeg", ".webp", ".png", ".gif", ".bmp", ".tiff" }; supportedVideoFormats = new string[] { "youtu.be", "youtube", "vimeo", ".mp4", ".webm" }; supportedDocumentFormats = new string[] { ".pdf", ".docx", ".xlsx", ".ppt", ".pptx", ".igs", ".ipt", ".sat", ".stp", ".dwg", ".dxf", ".dwf" }; allSupportedFormats = supportedImageFormats.Concat(supportedVideoFormats).Concat(supportedDocumentFormats).ToArray(); @* Collect the assets *@ var selectedAssetCategories = Model.Item.GetList("ImageAssets")?.GetRawValue().OfType<string>(); bool includeImagePatternImages = Model.Item.GetBoolean("ImagePatternImages"); @* Needed image data collection to support both DefaultImage, ImagePatterns and Image Assets *@ string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : ""; IEnumerable<MediaViewModel> assetsImages = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets); assetsImages = assetsImages.OrderByDescending(x => x.Value.Equals(defaultImage)); IEnumerable<MediaViewModel> assetsList = new MediaViewModel[] { }; assetsList = assetsList.Union(assetsImages); assetsList = includeImagePatternImages ? assetsList.Union(product.ImagePatternImages) : assetsList; assetsList = includeImagePatternImages && assetsList.Count() == 0 ? assetsList.Append(product.DefaultImage) : assetsList; bool defaultImageFallback = Model.Item.GetBoolean("DefaultImageFallback"); bool showOnlyPrimaryImage = Model.Item.GetBoolean("ShowOnlyPrimaryImage"); int totalAssets = 0; if (showOnlyPrimaryImage == false) { foreach (MediaViewModel asset in assetsList) { var assetValue = asset.Value; foreach (string format in allSupportedFormats) { if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { totalAssets++; } } } } if ((totalAssets == 0 && product.DefaultImage != null && selectedAssetCategories.Count() == 0) || (showOnlyPrimaryImage == true && product.DefaultImage != null)) { assetsList = new List<MediaViewModel>(){ product.DefaultImage }; totalAssets = 1; } int videoNumber = 0; @* Layout settings *@ string spacing = Model.Item.GetRawValueString("Spacing", "p-0"); spacing = spacing == "none" ? "p-0" : spacing; spacing = spacing == "small" ? "p-3" : spacing; spacing = spacing == "large" ? "p-5" : spacing; string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; bool hideThumbnails = Model.Item.GetBoolean("HideThumbnails"); string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; int modalVideoNumber = 0; @* Get assets from selected categories or get all assets *@ if (totalAssets != 0 && assetsList.Any()) { <div class="@spacing@(theme) item_@Model.Item.SystemName.ToLower()"> @if (!string.IsNullOrEmpty(Model.Item.GetString("Title")) && !Model.Item.GetBoolean("HideTitle")) { string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "h3"); <h3 class="@titleFontSize mb-3"> @Model.Item.GetString("Title") </h3> } <div class="table-responsive"> <table class="table table-hover align-middle mb-0" style="table-layout: fixed;"> <thead> <tr> @if (!hideThumbnails) { <th style="width:60px">&nbsp;</th> } <th>@Translate("Name")</th> <th class="text-end d-none d-lg-table-cell">@Translate("Download")</th> <th class="text-end" style="width:100px">@Translate("File type")</th> </tr> </thead> <tbody class="border-top-0"> @foreach (MediaViewModel asset in assetsList) { var assetValue = asset.Value; string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Value.Substring(asset.Value.LastIndexOf('/') + 1); bool isVideo = false; foreach (string format in supportedVideoFormats) { //Videos if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { isVideo = true; } } if (!isVideo) { if (assetValue.StartsWith("/Files/", StringComparison.OrdinalIgnoreCase)) { string filePath = Dynamicweb.Context.Current.Server.MapPath(assetValue); long fileSize = 0; if (File.Exists(filePath)) { fileSize = new System.IO.FileInfo(filePath) != null ? new System.IO.FileInfo(filePath).Length / 1024 : 0; foreach (string format in allSupportedFormats) { if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { <tr> @if (!hideThumbnails) { //From @RenderAsset string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; <td class="@(imageTheme) px-0"> @foreach (string imageFormat in supportedImageFormats) { //Images if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) { string productName = product.Name; productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; string imageLinkPath = imagePath; imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; imagePath = !imageLinkPath.StartsWith("/Files/", StringComparison.OrdinalIgnoreCase) ? asset.Value : imagePath; string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; RatioSettings ratioSettings = GetRatioSettings(); <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download> <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle itemprop="image"> </div> </a> } } @foreach (string videoFormat in supportedVideoFormats) { //Videos if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) { string productName = product.Name; productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; RatioSettings ratioSettings = GetRatioSettings(); string type = GetVideoType(asset.Value); string videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value) : string.Empty; videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeUriString(asset.Value) : videoScreendumpPath; string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : string.Empty; <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <div class="icon-2 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> @if (type != "selfhosted") { <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;"> } else { string videoType = Path.GetExtension(asset.Value).ToLower(); <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> </video> } </div> </div> } } @foreach (string documentFormat in supportedDocumentFormats) { //Documents if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) { string productName = product.Name; productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; string imageLinkPath = imagePath; imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; RatioSettings ratioSettings = GetRatioSettings(); <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download alt="@productName"> @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) { <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle /> </div> } else { <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <div class="icon-3 position-absolute" style="z-index: 1">@ReadFile(iconPath + "file-text.svg")</div> </div> } </a> } } </td> } <td> <a href="@assetValue" class="text-decoration-none text-break" download="@assetName" title="@assetName"> @assetName </a> </td> <td class="text-end d-none d-lg-table-cell"> <a href="@assetValue" class="text-decoration-none" download="@assetName" title="@assetName"> @fileSize KB <div class="icon-2" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> </a> </td> <td class="text-end">@format</td> </tr> } } } } else { <tr> @if (!hideThumbnails) { //From @RenderAsset string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; <td class="@(imageTheme) px-0"> @foreach (string format in supportedImageFormats) { //Images if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { string productName = product.Name; productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; string imageLinkPath = imagePath; imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; imagePath = !imageLinkPath.StartsWith("/Files/", StringComparison.OrdinalIgnoreCase) ? asset.Value : imagePath; string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; RatioSettings ratioSettings = GetRatioSettings(); <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download> <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle itemprop="image"> </div> </a> } } @foreach (string format in supportedVideoFormats) { //Videos if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { string type = GetVideoType(asset.Value); string videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value) : string.Empty; videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeUriString(asset.Value) : videoScreendumpPath; string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : string.Empty; string productName = product.Name; productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; RatioSettings ratioSettings = GetRatioSettings(); <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <div class="icon-2 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> @if (type != "selfhosted") { <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;"> } else { string videoType = Path.GetExtension(asset.Value).ToLower(); <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> </video> } </div> </div> } } @foreach (string format in supportedDocumentFormats) { //Documents if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { string productName = product.Name; productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; string imageLinkPath = imagePath; imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; RatioSettings ratioSettings = GetRatioSettings(); <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download alt="@productName"> @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) { <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle /> </div> } else { <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <div class="icon-3 position-absolute" style="z-index: 1">@ReadFile(iconPath + "file-text.svg")</div> </div> } </a> } } </td> } <td> <a href="@assetValue" class="text-decoration-none text-break" download="@assetName" title="@assetName"> @assetName </a> </td> <td>&nbsp;</td> <td>&nbsp;</td> </tr> } } else { string videoType = asset.Value.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || asset.Value.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "Youtube" : ""; videoType = asset.Value.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "Vimeo" : videoType; <tr data-bs-toggle="modal" data-bs-target="#modal_@(Model.ID)_@videoNumber" style="cursor: pointer"> @if (!hideThumbnails) { //From @RenderAsset string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; <td class="@(imageTheme) px-0"> @foreach (string format in supportedImageFormats) { //Images if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { string productName = product.Name; productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; string imageLinkPath = imagePath; imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; imagePath = !imageLinkPath.StartsWith("/Files/", StringComparison.OrdinalIgnoreCase) ? asset.Value : imagePath; string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; RatioSettings ratioSettings = GetRatioSettings(); <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download> <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle itemprop="image"> </div> </a> } } @foreach (string format in supportedVideoFormats) { //Videos if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { string type = GetVideoType(asset.Value); string videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value) : string.Empty; videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeUriString(asset.Value) : videoScreendumpPath; string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : string.Empty; string productName = product.Name; productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; RatioSettings ratioSettings = GetRatioSettings(); <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <div class="icon-2 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> @if (type != "selfhosted") { <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;"> } else { string fileExtension = Path.GetExtension(asset.Value).ToLower(); <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> <source src="@(videoScreendumpPath)#t=0.001" type="video/@fileExtension.Replace(".", "")"> </video> } </div> </div> } } @foreach (string documentFormat in supportedDocumentFormats) { //Documents if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) { string productName = product.Name; productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; string imageLinkPath = imagePath; imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; RatioSettings ratioSettings = GetRatioSettings(); <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download alt="@productName"> @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) { <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle /> </div> } else { <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <div class="icon-3 position-absolute" style="z-index: 1">@ReadFile(iconPath + "file-text.svg")</div> </div> } </a> } } </td> } <td> @assetName </td> <td class="d-none d-lg-table-cell">&nbsp;</td> <td align="right">@videoType</td> </tr> videoNumber++; } } </tbody> </table> </div> @foreach (MediaViewModel asset in assetsList) { var assetName = asset.Value.ToLower(); foreach (string videoFormat in supportedVideoFormats) { //Videos if (assetName.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) { <div class="modal fade js-video-modal" id="modal_@(Model.ID)_@modalVideoNumber" tabindex="-1" aria-labelledby="productDetailsTableModalTitle_@(Model.ID)_@modalVideoNumber" aria-hidden="true"> <div class="modal-dialog modal-dialog-centered modal-xl"> <div class="modal-content"> <div class="modal-header visually-hidden"> <h5 class="modal-title" id="productDetailsTableModalTitle_@(Model.ID)_@modalVideoNumber">@product.Title</h5> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body p-2 p-lg-3 h-100"> @{ var videoParams = GetVideoParams(asset, "modal"); @RenderPartial("Components/VideoPlayer.cshtml", new Dynamicweb.Frontend.FileViewModel { Path = asset.Value }, videoParams) } </div> </div> </div> </div> modalVideoNumber++; } } } </div> } else if (Pageview.IsVisualEditorMode) { <div class="h-100 @theme"> <div class="alert alert-dark m-0"> @Translate("No assets are available") </div> </div> } }
Error executing template "Designs/Swift/Paragraph/Swift_ProductDetailsMediaTable.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_3cf99cbc0d9743b18a564a3a449243cc.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> @using Dynamicweb.Ecommerce.ProductCatalog @using System.Text.RegularExpressions; @using System.IO @functions { public ProductViewModel product { get; set; } = new ProductViewModel(); public string[] supportedImageFormats { get; set; } public string[] supportedVideoFormats { get; set; } public string[] supportedDocumentFormats { get; set; } public string[] allSupportedFormats { get; set; } public class RatioSettings { public string Ratio { get; set; } public string CssClass { get; set; } public string CssVariable { get; set; } public string Fill { get; set; } } public RatioSettings GetRatioSettings() { var ratioSettings = new RatioSettings(); string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); ratio = ratio != "0" ? ratio : ""; string cssClass = ratio != "" && ratio != "fill" ? " ratio" : ""; string cssVariable = ratio != "" && ratio != "fill" ? "--bs-aspect-ratio: " + ratio : ""; ratioSettings.Ratio = ratio; ratioSettings.CssClass = cssClass; ratioSettings.CssVariable = cssVariable; ratioSettings.Fill = ratio == "fill" ? " h-100" : ""; return ratioSettings; } public Dictionary<string, object> GetVideoParams(MediaViewModel asset, string size) { string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Name; string type = GetVideoType(asset.Value); bool openInModal = Model.Item.GetString("OpenVideoInModal") == "true" ? true : false; bool autoPlay = Model.Item.GetBoolean("VideoAutoPlay"); var videoParams = new Dictionary<string, object>(); videoParams.Add("AssetName", asset.Name); videoParams.Add("AssetVideoType", type); videoParams.Add("AssetDisplayName", asset.DisplayName); videoParams.Add("OpenVideoInModal", openInModal); videoParams.Add("VideoAutoPlay", autoPlay); videoParams.Add("Size", size); videoParams.Add("Id", Model.ID); return videoParams; } public string GetVideoType(string assetValue) { string type = assetValue.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "youtube" : string.Empty; type = assetValue.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "vimeo" : type; type = string.IsNullOrEmpty(type) ? "selfhosted" : type; return type; } public string GetYoutubeScreenDump(string assetValue) { var regex = new Regex(@"(?:youtube\.com\/.*[\?&]v=|youtu\.be\/|youtube\.com\/embed\/)([\w-]+)(?:\?.*)?"); Match match = regex.Match(assetValue); string videoId = match.Success ? match.Groups[1].Value : string.Empty; string youtubeThumbnail = $"https://img.youtube.com/vi/{videoId}/mqdefault.jpg"; return youtubeThumbnail; } } @{ @* Get the product data *@ ProductViewModel product = null; if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) { product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; } else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) { var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); if (productList?.Products is object) { product = productList.Products[0]; } } } @if (product is object) { @* Supported formats *@ supportedImageFormats = new string[] { ".jpg", ".jpeg", ".webp", ".png", ".gif", ".bmp", ".tiff" }; supportedVideoFormats = new string[] { "youtu.be", "youtube", "vimeo", ".mp4", ".webm" }; supportedDocumentFormats = new string[] { ".pdf", ".docx", ".xlsx", ".ppt", ".pptx", ".igs", ".ipt", ".sat", ".stp", ".dwg", ".dxf", ".dwf" }; allSupportedFormats = supportedImageFormats.Concat(supportedVideoFormats).Concat(supportedDocumentFormats).ToArray(); @* Collect the assets *@ var selectedAssetCategories = Model.Item.GetList("ImageAssets")?.GetRawValue().OfType<string>(); bool includeImagePatternImages = Model.Item.GetBoolean("ImagePatternImages"); @* Needed image data collection to support both DefaultImage, ImagePatterns and Image Assets *@ string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : ""; IEnumerable<MediaViewModel> assetsImages = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets); assetsImages = assetsImages.OrderByDescending(x => x.Value.Equals(defaultImage)); IEnumerable<MediaViewModel> assetsList = new MediaViewModel[] { }; assetsList = assetsList.Union(assetsImages); assetsList = includeImagePatternImages ? assetsList.Union(product.ImagePatternImages) : assetsList; assetsList = includeImagePatternImages && assetsList.Count() == 0 ? assetsList.Append(product.DefaultImage) : assetsList; bool defaultImageFallback = Model.Item.GetBoolean("DefaultImageFallback"); bool showOnlyPrimaryImage = Model.Item.GetBoolean("ShowOnlyPrimaryImage"); int totalAssets = 0; if (showOnlyPrimaryImage == false) { foreach (MediaViewModel asset in assetsList) { var assetValue = asset.Value; foreach (string format in allSupportedFormats) { if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { totalAssets++; } } } } if ((totalAssets == 0 && product.DefaultImage != null && selectedAssetCategories.Count() == 0) || (showOnlyPrimaryImage == true && product.DefaultImage != null)) { assetsList = new List<MediaViewModel>(){ product.DefaultImage }; totalAssets = 1; } int videoNumber = 0; @* Layout settings *@ string spacing = Model.Item.GetRawValueString("Spacing", "p-0"); spacing = spacing == "none" ? "p-0" : spacing; spacing = spacing == "small" ? "p-3" : spacing; spacing = spacing == "large" ? "p-5" : spacing; string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; bool hideThumbnails = Model.Item.GetBoolean("HideThumbnails"); string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; int modalVideoNumber = 0; @* Get assets from selected categories or get all assets *@ if (totalAssets != 0 && assetsList.Any()) { <div class="@spacing@(theme) item_@Model.Item.SystemName.ToLower()"> @if (!string.IsNullOrEmpty(Model.Item.GetString("Title")) && !Model.Item.GetBoolean("HideTitle")) { string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "h3"); <h3 class="@titleFontSize mb-3"> @Model.Item.GetString("Title") </h3> } <div class="table-responsive"> <table class="table table-hover align-middle mb-0" style="table-layout: fixed;"> <thead> <tr> @if (!hideThumbnails) { <th style="width:60px">&nbsp;</th> } <th>@Translate("Name")</th> <th class="text-end d-none d-lg-table-cell">@Translate("Download")</th> <th class="text-end" style="width:100px">@Translate("File type")</th> </tr> </thead> <tbody class="border-top-0"> @foreach (MediaViewModel asset in assetsList) { var assetValue = asset.Value; string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Value.Substring(asset.Value.LastIndexOf('/') + 1); bool isVideo = false; foreach (string format in supportedVideoFormats) { //Videos if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { isVideo = true; } } if (!isVideo) { if (assetValue.StartsWith("/Files/", StringComparison.OrdinalIgnoreCase)) { string filePath = Dynamicweb.Context.Current.Server.MapPath(assetValue); long fileSize = 0; if (File.Exists(filePath)) { fileSize = new System.IO.FileInfo(filePath) != null ? new System.IO.FileInfo(filePath).Length / 1024 : 0; foreach (string format in allSupportedFormats) { if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { <tr> @if (!hideThumbnails) { //From @RenderAsset string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; <td class="@(imageTheme) px-0"> @foreach (string imageFormat in supportedImageFormats) { //Images if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) { string productName = product.Name; productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; string imageLinkPath = imagePath; imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; imagePath = !imageLinkPath.StartsWith("/Files/", StringComparison.OrdinalIgnoreCase) ? asset.Value : imagePath; string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; RatioSettings ratioSettings = GetRatioSettings(); <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download> <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle itemprop="image"> </div> </a> } } @foreach (string videoFormat in supportedVideoFormats) { //Videos if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) { string productName = product.Name; productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; RatioSettings ratioSettings = GetRatioSettings(); string type = GetVideoType(asset.Value); string videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value) : string.Empty; videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeUriString(asset.Value) : videoScreendumpPath; string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : string.Empty; <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <div class="icon-2 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> @if (type != "selfhosted") { <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;"> } else { string videoType = Path.GetExtension(asset.Value).ToLower(); <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> </video> } </div> </div> } } @foreach (string documentFormat in supportedDocumentFormats) { //Documents if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) { string productName = product.Name; productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; string imageLinkPath = imagePath; imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; RatioSettings ratioSettings = GetRatioSettings(); <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download alt="@productName"> @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) { <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle /> </div> } else { <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <div class="icon-3 position-absolute" style="z-index: 1">@ReadFile(iconPath + "file-text.svg")</div> </div> } </a> } } </td> } <td> <a href="@assetValue" class="text-decoration-none text-break" download="@assetName" title="@assetName"> @assetName </a> </td> <td class="text-end d-none d-lg-table-cell"> <a href="@assetValue" class="text-decoration-none" download="@assetName" title="@assetName"> @fileSize KB <div class="icon-2" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> </a> </td> <td class="text-end">@format</td> </tr> } } } } else { <tr> @if (!hideThumbnails) { //From @RenderAsset string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; <td class="@(imageTheme) px-0"> @foreach (string format in supportedImageFormats) { //Images if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { string productName = product.Name; productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; string imageLinkPath = imagePath; imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; imagePath = !imageLinkPath.StartsWith("/Files/", StringComparison.OrdinalIgnoreCase) ? asset.Value : imagePath; string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; RatioSettings ratioSettings = GetRatioSettings(); <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download> <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle itemprop="image"> </div> </a> } } @foreach (string format in supportedVideoFormats) { //Videos if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { string type = GetVideoType(asset.Value); string videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value) : string.Empty; videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeUriString(asset.Value) : videoScreendumpPath; string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : string.Empty; string productName = product.Name; productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; RatioSettings ratioSettings = GetRatioSettings(); <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <div class="icon-2 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> @if (type != "selfhosted") { <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;"> } else { string videoType = Path.GetExtension(asset.Value).ToLower(); <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> </video> } </div> </div> } } @foreach (string format in supportedDocumentFormats) { //Documents if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { string productName = product.Name; productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; string imageLinkPath = imagePath; imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; RatioSettings ratioSettings = GetRatioSettings(); <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download alt="@productName"> @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) { <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle /> </div> } else { <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <div class="icon-3 position-absolute" style="z-index: 1">@ReadFile(iconPath + "file-text.svg")</div> </div> } </a> } } </td> } <td> <a href="@assetValue" class="text-decoration-none text-break" download="@assetName" title="@assetName"> @assetName </a> </td> <td>&nbsp;</td> <td>&nbsp;</td> </tr> } } else { string videoType = asset.Value.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || asset.Value.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "Youtube" : ""; videoType = asset.Value.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "Vimeo" : videoType; <tr data-bs-toggle="modal" data-bs-target="#modal_@(Model.ID)_@videoNumber" style="cursor: pointer"> @if (!hideThumbnails) { //From @RenderAsset string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; <td class="@(imageTheme) px-0"> @foreach (string format in supportedImageFormats) { //Images if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { string productName = product.Name; productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; string imageLinkPath = imagePath; imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; imagePath = !imageLinkPath.StartsWith("/Files/", StringComparison.OrdinalIgnoreCase) ? asset.Value : imagePath; string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; RatioSettings ratioSettings = GetRatioSettings(); <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download> <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle itemprop="image"> </div> </a> } } @foreach (string format in supportedVideoFormats) { //Videos if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { string type = GetVideoType(asset.Value); string videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value) : string.Empty; videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeUriString(asset.Value) : videoScreendumpPath; string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : string.Empty; string productName = product.Name; productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; RatioSettings ratioSettings = GetRatioSettings(); <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <div class="icon-2 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> @if (type != "selfhosted") { <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;"> } else { string fileExtension = Path.GetExtension(asset.Value).ToLower(); <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> <source src="@(videoScreendumpPath)#t=0.001" type="video/@fileExtension.Replace(".", "")"> </video> } </div> </div> } } @foreach (string documentFormat in supportedDocumentFormats) { //Documents if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) { string productName = product.Name; productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; string imageLinkPath = imagePath; imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; RatioSettings ratioSettings = GetRatioSettings(); <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download alt="@productName"> @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) { <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle /> </div> } else { <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <div class="icon-3 position-absolute" style="z-index: 1">@ReadFile(iconPath + "file-text.svg")</div> </div> } </a> } } </td> } <td> @assetName </td> <td class="d-none d-lg-table-cell">&nbsp;</td> <td align="right">@videoType</td> </tr> videoNumber++; } } </tbody> </table> </div> @foreach (MediaViewModel asset in assetsList) { var assetName = asset.Value.ToLower(); foreach (string videoFormat in supportedVideoFormats) { //Videos if (assetName.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) { <div class="modal fade js-video-modal" id="modal_@(Model.ID)_@modalVideoNumber" tabindex="-1" aria-labelledby="productDetailsTableModalTitle_@(Model.ID)_@modalVideoNumber" aria-hidden="true"> <div class="modal-dialog modal-dialog-centered modal-xl"> <div class="modal-content"> <div class="modal-header visually-hidden"> <h5 class="modal-title" id="productDetailsTableModalTitle_@(Model.ID)_@modalVideoNumber">@product.Title</h5> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body p-2 p-lg-3 h-100"> @{ var videoParams = GetVideoParams(asset, "modal"); @RenderPartial("Components/VideoPlayer.cshtml", new Dynamicweb.Frontend.FileViewModel { Path = asset.Value }, videoParams) } </div> </div> </div> </div> modalVideoNumber++; } } } </div> } else if (Pageview.IsVisualEditorMode) { <div class="h-100 @theme"> <div class="alert alert-dark m-0"> @Translate("No assets are available") </div> </div> } }
Error executing template "Designs/Swift/Paragraph/Swift_ProductDetailsMediaTable.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_3cf99cbc0d9743b18a564a3a449243cc.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> @using Dynamicweb.Ecommerce.ProductCatalog @using System.Text.RegularExpressions; @using System.IO @functions { public ProductViewModel product { get; set; } = new ProductViewModel(); public string[] supportedImageFormats { get; set; } public string[] supportedVideoFormats { get; set; } public string[] supportedDocumentFormats { get; set; } public string[] allSupportedFormats { get; set; } public class RatioSettings { public string Ratio { get; set; } public string CssClass { get; set; } public string CssVariable { get; set; } public string Fill { get; set; } } public RatioSettings GetRatioSettings() { var ratioSettings = new RatioSettings(); string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); ratio = ratio != "0" ? ratio : ""; string cssClass = ratio != "" && ratio != "fill" ? " ratio" : ""; string cssVariable = ratio != "" && ratio != "fill" ? "--bs-aspect-ratio: " + ratio : ""; ratioSettings.Ratio = ratio; ratioSettings.CssClass = cssClass; ratioSettings.CssVariable = cssVariable; ratioSettings.Fill = ratio == "fill" ? " h-100" : ""; return ratioSettings; } public Dictionary<string, object> GetVideoParams(MediaViewModel asset, string size) { string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Name; string type = GetVideoType(asset.Value); bool openInModal = Model.Item.GetString("OpenVideoInModal") == "true" ? true : false; bool autoPlay = Model.Item.GetBoolean("VideoAutoPlay"); var videoParams = new Dictionary<string, object>(); videoParams.Add("AssetName", asset.Name); videoParams.Add("AssetVideoType", type); videoParams.Add("AssetDisplayName", asset.DisplayName); videoParams.Add("OpenVideoInModal", openInModal); videoParams.Add("VideoAutoPlay", autoPlay); videoParams.Add("Size", size); videoParams.Add("Id", Model.ID); return videoParams; } public string GetVideoType(string assetValue) { string type = assetValue.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "youtube" : string.Empty; type = assetValue.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "vimeo" : type; type = string.IsNullOrEmpty(type) ? "selfhosted" : type; return type; } public string GetYoutubeScreenDump(string assetValue) { var regex = new Regex(@"(?:youtube\.com\/.*[\?&]v=|youtu\.be\/|youtube\.com\/embed\/)([\w-]+)(?:\?.*)?"); Match match = regex.Match(assetValue); string videoId = match.Success ? match.Groups[1].Value : string.Empty; string youtubeThumbnail = $"https://img.youtube.com/vi/{videoId}/mqdefault.jpg"; return youtubeThumbnail; } } @{ @* Get the product data *@ ProductViewModel product = null; if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) { product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; } else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) { var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); if (productList?.Products is object) { product = productList.Products[0]; } } } @if (product is object) { @* Supported formats *@ supportedImageFormats = new string[] { ".jpg", ".jpeg", ".webp", ".png", ".gif", ".bmp", ".tiff" }; supportedVideoFormats = new string[] { "youtu.be", "youtube", "vimeo", ".mp4", ".webm" }; supportedDocumentFormats = new string[] { ".pdf", ".docx", ".xlsx", ".ppt", ".pptx", ".igs", ".ipt", ".sat", ".stp", ".dwg", ".dxf", ".dwf" }; allSupportedFormats = supportedImageFormats.Concat(supportedVideoFormats).Concat(supportedDocumentFormats).ToArray(); @* Collect the assets *@ var selectedAssetCategories = Model.Item.GetList("ImageAssets")?.GetRawValue().OfType<string>(); bool includeImagePatternImages = Model.Item.GetBoolean("ImagePatternImages"); @* Needed image data collection to support both DefaultImage, ImagePatterns and Image Assets *@ string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : ""; IEnumerable<MediaViewModel> assetsImages = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets); assetsImages = assetsImages.OrderByDescending(x => x.Value.Equals(defaultImage)); IEnumerable<MediaViewModel> assetsList = new MediaViewModel[] { }; assetsList = assetsList.Union(assetsImages); assetsList = includeImagePatternImages ? assetsList.Union(product.ImagePatternImages) : assetsList; assetsList = includeImagePatternImages && assetsList.Count() == 0 ? assetsList.Append(product.DefaultImage) : assetsList; bool defaultImageFallback = Model.Item.GetBoolean("DefaultImageFallback"); bool showOnlyPrimaryImage = Model.Item.GetBoolean("ShowOnlyPrimaryImage"); int totalAssets = 0; if (showOnlyPrimaryImage == false) { foreach (MediaViewModel asset in assetsList) { var assetValue = asset.Value; foreach (string format in allSupportedFormats) { if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { totalAssets++; } } } } if ((totalAssets == 0 && product.DefaultImage != null && selectedAssetCategories.Count() == 0) || (showOnlyPrimaryImage == true && product.DefaultImage != null)) { assetsList = new List<MediaViewModel>(){ product.DefaultImage }; totalAssets = 1; } int videoNumber = 0; @* Layout settings *@ string spacing = Model.Item.GetRawValueString("Spacing", "p-0"); spacing = spacing == "none" ? "p-0" : spacing; spacing = spacing == "small" ? "p-3" : spacing; spacing = spacing == "large" ? "p-5" : spacing; string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; bool hideThumbnails = Model.Item.GetBoolean("HideThumbnails"); string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; int modalVideoNumber = 0; @* Get assets from selected categories or get all assets *@ if (totalAssets != 0 && assetsList.Any()) { <div class="@spacing@(theme) item_@Model.Item.SystemName.ToLower()"> @if (!string.IsNullOrEmpty(Model.Item.GetString("Title")) && !Model.Item.GetBoolean("HideTitle")) { string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "h3"); <h3 class="@titleFontSize mb-3"> @Model.Item.GetString("Title") </h3> } <div class="table-responsive"> <table class="table table-hover align-middle mb-0" style="table-layout: fixed;"> <thead> <tr> @if (!hideThumbnails) { <th style="width:60px">&nbsp;</th> } <th>@Translate("Name")</th> <th class="text-end d-none d-lg-table-cell">@Translate("Download")</th> <th class="text-end" style="width:100px">@Translate("File type")</th> </tr> </thead> <tbody class="border-top-0"> @foreach (MediaViewModel asset in assetsList) { var assetValue = asset.Value; string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Value.Substring(asset.Value.LastIndexOf('/') + 1); bool isVideo = false; foreach (string format in supportedVideoFormats) { //Videos if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { isVideo = true; } } if (!isVideo) { if (assetValue.StartsWith("/Files/", StringComparison.OrdinalIgnoreCase)) { string filePath = Dynamicweb.Context.Current.Server.MapPath(assetValue); long fileSize = 0; if (File.Exists(filePath)) { fileSize = new System.IO.FileInfo(filePath) != null ? new System.IO.FileInfo(filePath).Length / 1024 : 0; foreach (string format in allSupportedFormats) { if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { <tr> @if (!hideThumbnails) { //From @RenderAsset string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; <td class="@(imageTheme) px-0"> @foreach (string imageFormat in supportedImageFormats) { //Images if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) { string productName = product.Name; productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; string imageLinkPath = imagePath; imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; imagePath = !imageLinkPath.StartsWith("/Files/", StringComparison.OrdinalIgnoreCase) ? asset.Value : imagePath; string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; RatioSettings ratioSettings = GetRatioSettings(); <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download> <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle itemprop="image"> </div> </a> } } @foreach (string videoFormat in supportedVideoFormats) { //Videos if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) { string productName = product.Name; productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; RatioSettings ratioSettings = GetRatioSettings(); string type = GetVideoType(asset.Value); string videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value) : string.Empty; videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeUriString(asset.Value) : videoScreendumpPath; string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : string.Empty; <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <div class="icon-2 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> @if (type != "selfhosted") { <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;"> } else { string videoType = Path.GetExtension(asset.Value).ToLower(); <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> </video> } </div> </div> } } @foreach (string documentFormat in supportedDocumentFormats) { //Documents if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) { string productName = product.Name; productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; string imageLinkPath = imagePath; imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; RatioSettings ratioSettings = GetRatioSettings(); <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download alt="@productName"> @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) { <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle /> </div> } else { <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <div class="icon-3 position-absolute" style="z-index: 1">@ReadFile(iconPath + "file-text.svg")</div> </div> } </a> } } </td> } <td> <a href="@assetValue" class="text-decoration-none text-break" download="@assetName" title="@assetName"> @assetName </a> </td> <td class="text-end d-none d-lg-table-cell"> <a href="@assetValue" class="text-decoration-none" download="@assetName" title="@assetName"> @fileSize KB <div class="icon-2" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> </a> </td> <td class="text-end">@format</td> </tr> } } } } else { <tr> @if (!hideThumbnails) { //From @RenderAsset string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; <td class="@(imageTheme) px-0"> @foreach (string format in supportedImageFormats) { //Images if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { string productName = product.Name; productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; string imageLinkPath = imagePath; imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; imagePath = !imageLinkPath.StartsWith("/Files/", StringComparison.OrdinalIgnoreCase) ? asset.Value : imagePath; string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; RatioSettings ratioSettings = GetRatioSettings(); <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download> <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle itemprop="image"> </div> </a> } } @foreach (string format in supportedVideoFormats) { //Videos if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { string type = GetVideoType(asset.Value); string videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value) : string.Empty; videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeUriString(asset.Value) : videoScreendumpPath; string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : string.Empty; string productName = product.Name; productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; RatioSettings ratioSettings = GetRatioSettings(); <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <div class="icon-2 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> @if (type != "selfhosted") { <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;"> } else { string videoType = Path.GetExtension(asset.Value).ToLower(); <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> </video> } </div> </div> } } @foreach (string format in supportedDocumentFormats) { //Documents if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { string productName = product.Name; productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; string imageLinkPath = imagePath; imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; RatioSettings ratioSettings = GetRatioSettings(); <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download alt="@productName"> @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) { <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle /> </div> } else { <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <div class="icon-3 position-absolute" style="z-index: 1">@ReadFile(iconPath + "file-text.svg")</div> </div> } </a> } } </td> } <td> <a href="@assetValue" class="text-decoration-none text-break" download="@assetName" title="@assetName"> @assetName </a> </td> <td>&nbsp;</td> <td>&nbsp;</td> </tr> } } else { string videoType = asset.Value.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || asset.Value.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "Youtube" : ""; videoType = asset.Value.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "Vimeo" : videoType; <tr data-bs-toggle="modal" data-bs-target="#modal_@(Model.ID)_@videoNumber" style="cursor: pointer"> @if (!hideThumbnails) { //From @RenderAsset string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; <td class="@(imageTheme) px-0"> @foreach (string format in supportedImageFormats) { //Images if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { string productName = product.Name; productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; string imageLinkPath = imagePath; imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; imagePath = !imageLinkPath.StartsWith("/Files/", StringComparison.OrdinalIgnoreCase) ? asset.Value : imagePath; string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; RatioSettings ratioSettings = GetRatioSettings(); <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download> <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle itemprop="image"> </div> </a> } } @foreach (string format in supportedVideoFormats) { //Videos if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { string type = GetVideoType(asset.Value); string videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value) : string.Empty; videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeUriString(asset.Value) : videoScreendumpPath; string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : string.Empty; string productName = product.Name; productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; RatioSettings ratioSettings = GetRatioSettings(); <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <div class="icon-2 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> @if (type != "selfhosted") { <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;"> } else { string fileExtension = Path.GetExtension(asset.Value).ToLower(); <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> <source src="@(videoScreendumpPath)#t=0.001" type="video/@fileExtension.Replace(".", "")"> </video> } </div> </div> } } @foreach (string documentFormat in supportedDocumentFormats) { //Documents if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) { string productName = product.Name; productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; string imageLinkPath = imagePath; imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; RatioSettings ratioSettings = GetRatioSettings(); <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download alt="@productName"> @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) { <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle /> </div> } else { <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> <div class="icon-3 position-absolute" style="z-index: 1">@ReadFile(iconPath + "file-text.svg")</div> </div> } </a> } } </td> } <td> @assetName </td> <td class="d-none d-lg-table-cell">&nbsp;</td> <td align="right">@videoType</td> </tr> videoNumber++; } } </tbody> </table> </div> @foreach (MediaViewModel asset in assetsList) { var assetName = asset.Value.ToLower(); foreach (string videoFormat in supportedVideoFormats) { //Videos if (assetName.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) { <div class="modal fade js-video-modal" id="modal_@(Model.ID)_@modalVideoNumber" tabindex="-1" aria-labelledby="productDetailsTableModalTitle_@(Model.ID)_@modalVideoNumber" aria-hidden="true"> <div class="modal-dialog modal-dialog-centered modal-xl"> <div class="modal-content"> <div class="modal-header visually-hidden"> <h5 class="modal-title" id="productDetailsTableModalTitle_@(Model.ID)_@modalVideoNumber">@product.Title</h5> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body p-2 p-lg-3 h-100"> @{ var videoParams = GetVideoParams(asset, "modal"); @RenderPartial("Components/VideoPlayer.cshtml", new Dynamicweb.Frontend.FileViewModel { Path = asset.Value }, videoParams) } </div> </div> </div> </div> modalVideoNumber++; } } } </div> } else if (Pageview.IsVisualEditorMode) { <div class="h-100 @theme"> <div class="alert alert-dark m-0"> @Translate("No assets are available") </div> </div> } }

Relaterede produkter

Error executing template "Designs/Swift/Paragraph/Swift_ProductComponentSlider.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_0dace93b20dd41559ccd6b579402d2c8.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> @using Dynamicweb.Core @using Dynamicweb.Ecommerce.ProductCatalog @{ ProductViewModel product = null; if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) { product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; } else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) { var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); if (productList?.Products is object) { product = productList.Products[0]; } } string layout = Model.Item.GetRawValueString("Layout") == "slider" ? "ProductSliderComponent" : "ProductGridComponent"; string lazyHeight = Model.Item.GetBoolean("SetMinHeightForLazyLoading") ? "min-height: 360px" : ""; string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; //Link generation string pageId = Model.Item.GetLink("ProductSliderServicePage") != null ? Model.Item.GetLink("ProductSliderServicePage").PageId.ToString() : ""; string servicePageByNavigationTag = GetPageIdByNavigationTag("ProductSliderService") != 0 ? GetPageIdByNavigationTag("ProductSliderService").ToString() : ""; pageId = pageId == "" ? servicePageByNavigationTag : pageId; string url = "/Default.aspx?ID=" + pageId; if (Pageview.IsVisualEditorMode) { url += "&VisualEdit=True"; } bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]); if (isLazyLoadingForProductInfoEnabled) { url += "&getproductinfo=true"; } url += $"&ProductListPartial={layout}"; //Use this to render either a slider or a grid //Source type string sourceType = Model.Item.GetRawValueString("RelationType", "trending"); IList<string> relateFromProductIds = new List<string> { }; IList<string> relateFromProductVariantIds = new List<string> { }; IList<string> relateFromGroupIds = new List<string> { }; bool hasVariants = false; ProductListViewModel relateToViewModel = Model.Item.GetValue<ProductListViewModel>("RelateTo"); //--- PRODUCTS --- if (sourceType == "variants" || sourceType == "frequently" || sourceType == "selected") { if (relateToViewModel?.Products != null) { hasVariants = relateToViewModel.Products.Any(p => !string.IsNullOrEmpty(p.VariantId)); foreach (var fromProduct in relateToViewModel.Products) { if (hasVariants) { if (!string.IsNullOrEmpty(fromProduct.VariantId)) { relateFromProductVariantIds.Add($"{fromProduct.Id} {fromProduct.VariantId}"); } else { relateFromProductVariantIds.Add($"{fromProduct.Id}"); } } relateFromProductIds.Add($"{fromProduct.Id}"); } } } // -- flex Related -- if(sourceType == "related"){ int relatedCnt = 0; foreach (var g in product.RelatedGroups){ foreach (var p in g.Products){ relateFromProductIds.Add($"{p.ProductId}"); relatedCnt++; } } if(relatedCnt == 0){sourceType = "most-sold";} } //--- GROUPS --- if (sourceType == "most-sold" || sourceType == "trending" || sourceType == "latest" || sourceType == "custom") { var groupsToRelateTo = Model.Item.GetList("RelateTo")?.GetRawValue().OfType<string>().ToList(); if (groupsToRelateTo != null) { foreach (var fromGroup in groupsToRelateTo) { var groupId = fromGroup.Length > 2 ? fromGroup.Remove(0, 2) : fromGroup; if (!fromGroup.Contains("p_")) { relateFromGroupIds.Add(groupId); } } } if (relateToViewModel?.Products != null) { foreach (var fromProduct in relateToViewModel.Products) { var groupId = fromProduct.PrimaryOrDefaultGroup.Id; relateFromGroupIds.Add(groupId); } } } string relationGroupId = Model.Item.GetRawValueString("RelationGroup", string.Empty); //--- RELATED GROUP --- if (sourceType == "related") { if (relateToViewModel?.Products != null) { product = relateToViewModel.Products.FirstOrDefault(); } if (product?.RelatedGroups != null) { foreach (var relationGroup in product.RelatedGroups) { if (relationGroup.Id == relationGroupId) { hasVariants = relationGroup.Products.Any(p => !string.IsNullOrEmpty(p.VariantId)); foreach (var fromProduct in relationGroup.Products) { if (hasVariants) { if (!string.IsNullOrEmpty(fromProduct.VariantId)) { relateFromProductVariantIds.Add($"{fromProduct.ProductId} {fromProduct.VariantId}"); } else { relateFromProductVariantIds.Add($"{fromProduct.ProductId}"); } } relateFromProductIds.Add($"{fromProduct.ProductId}"); } } } } } //Create group id collection and products id collection strings string productIds = relateFromProductIds.Count > 0 ? string.Join(",", relateFromProductIds) : ""; string productVariantIds = relateFromProductVariantIds.Count > 0 ? string.Join(",", relateFromProductVariantIds) : ""; string groupIds = relateFromGroupIds.Count > 0 ? string.Join(",", relateFromGroupIds) : ""; if (product is object) { if (string.IsNullOrEmpty(productIds) && (sourceType == "variants" || sourceType == "frequently" || sourceType == "selected" || sourceType == "custom")) { productIds = product.Id; } if (string.IsNullOrEmpty(groupIds) && (sourceType == "most-sold" || sourceType == "trending" || sourceType == "latest")) { groupIds = product.PrimaryOrDefaultGroup.Id; } } } @*Container element for the request*@ @if (!string.IsNullOrEmpty(productIds) || !string.IsNullOrEmpty(groupIds) || (string.IsNullOrEmpty(productIds) && string.IsNullOrEmpty(groupIds) && sourceType != "related")) { <form method="post" action="@url" id="ComponentSliderProductsForm_@Model.ID" data-response-target-element="ComponentSliderProducts_@Model.ID" data-preloader="inline" data-update-url="false" class="item_@Model.Item.SystemName.ToLower()"> <input type="hidden" name="ParagraphId" value="@Model.ID" /> <input type="hidden" name="SortOrder" value="DESC"> <input type="hidden" name="SourceType" value="@sourceType"> @if (!string.IsNullOrEmpty(groupIds)) { <input type="hidden" name="GroupId" value="@groupIds"> } @if (sourceType != "frequently" && !string.IsNullOrEmpty(productIds) && !hasVariants) { <input type="hidden" name="MainProductId" value="@productIds"> } @if (sourceType != "frequently" && hasVariants) { <input type="hidden" name="ProductVariantId" value="@productVariantIds"> } @if (Model.Item.GetInt32("ProductsCount") != 0) { <input type="hidden" name="PageSize" value="@Model.Item.GetInt32("ProductsCount")"> } @if (sourceType == "variants") { <input type="hidden" name="isVariant" value="true"> } @if (sourceType == "most-sold") { <input type="hidden" name="SortBy" value="OrderCount"> } @if (sourceType == "trending") { <input type="hidden" name="SortBy" value="OrderCountGrowth"> } @if (sourceType == "frequently" && !string.IsNullOrEmpty(productIds)) { <input type="hidden" name="BoughtWithProductIds" value="[@productIds]"> } @if (sourceType == "latest") { <input type="hidden" name="SortBy" value="Created"> } </form> <script type="module" src="/Files/Templates/Designs/Swift/Assets/js/swiffy-slider.js"></script> <script> window.addEventListener("load", () => { swift.AssetLoader.Load('/Files/Templates/Designs/Swift/Assets/css/swiffy-slider.min.css', 'css'); }); </script> <div class="w-100 h-100@(theme)"> <div id="@Model.ID" class="user-select-none" style="scroll-margin-top:var(--header-height,150px)"></div> <div id="ComponentSliderProducts_@Model.ID" class="h-100" style="@lazyHeight"></div> </div> <script type="module"> const productSliderContainer = document.querySelector("#ComponentSliderProducts_@Model.ID"); const productSliderForm = document.querySelector("#ComponentSliderProductsForm_@Model.ID"); swift.PageUpdater.Update(productSliderForm); productSliderForm.addEventListener("updated.swift.pageupdater", function(e){ if (e.detail.html === "") { productSliderContainer.closest("[data-col-size]").classList.add("d-none"); } }); </script> } else if (Pageview.IsVisualEditorMode == true) { <div class="alert alert-dark" role="alert"> <span>@Translate("Product component slider: The slider will be rendered here, if there is anything to show")</span> </div> }

Mest solgte