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> </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"> </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> </td> <td> </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"> </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"> </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> </td> <td> </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"> </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"> </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> </td> <td> </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"> </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
Beklager. Der er ikke noget at se her




