Error executing template "Designs/Swift/eCom/ProductCatalog/ProductViewDetail.cshtml" System.Data.SqlClient.SqlException (0x80131904): SHUTDOWN is in progress. Login failed for user 'danzafe.cloud.dynamicweb-cms.com'. Cannot continue the execution because the session is in the kill state. A severe error occurred on the current command. The results, if any, should be discarded. at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction) at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose) at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady) at System.Data.SqlClient.SqlDataReader.TryConsumeMetaData() at System.Data.SqlClient.SqlDataReader.get_MetaData() at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString, Boolean isInternal, Boolean forDescribeParameterEncryption, Boolean shouldCacheForAlwaysEncrypted) at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, Boolean inRetry, SqlDataReader ds, Boolean describeParameterEncryptionRequest) at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry) at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method) at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method) at Dynamicweb.Data.Database.CreateDataReader(IDbCommand command, CommandBehavior behavior) at Dynamicweb.Data.Database.CreateDataReader(CommandBuilder commandBuilder, IDbConnection connection, IDbTransaction transaction, CommandBehavior behavior, Int32 commandTimeout) at Dynamicweb.Data.Database.CreateDataReader(CommandBuilder commandBuilder, IDbConnection connection, IDbTransaction transaction, Int32 commandTimeout) at Dynamicweb.Ecommerce.Products.DetailRepository.GetInheritedDetailsBulk(List`1 productIds, String detailType, Boolean onlyDefault) at Dynamicweb.Ecommerce.Products.DetailService.GetPrimaryDetailsBulk(IEnumerable`1 productKeys, String detailType) at System.Lazy`1.CreateValue() at System.Lazy`1.LazyInitValue() at Dynamicweb.Ecommerce.ProductCatalog.ViewEngine.GetDefaultImage(MediaViewModelSettings settings, String productId, String languageId, String variantId, Lazy`1 productImages, Lazy`1 details) at Dynamicweb.Ecommerce.ProductCatalog.ViewEngine.GetDefaultImage(MediaViewModelSettings settings, Product product, Lazy`1 productImages, Lazy`1 details) at Dynamicweb.Ecommerce.ProductCatalog.ViewEngine.<>c__DisplayClass3_2.<BulkCreateView>b__62() at System.Lazy`1.CreateValue() at System.Lazy`1.LazyInitValue() at CompiledRazorTemplates.Dynamic.RazorEngine_c54b4806f72d4133a3d93fbeefa4f304.Execute() in D:\dynamicweb.net\Solutions\Flex Media\danzafe.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\eCom\ProductCatalog\ProductViewDetail.cshtml:line 16 at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader) at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer) at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter) at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template) at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template) at Dynamicweb.Rendering.Template.RenderRazorTemplate() ClientConnectionId:669d93f8-efdc-4c6f-9be7-0ac361195ffa Error Number:6005,State:1,Class:14
1 @inherits ViewModelTemplate<ProductViewModel> 2 @using Dynamicweb.Rendering 3 @using Dynamicweb.Ecommerce.ProductCatalog 4 @using Dynamicweb.Core 5 @using Dynamicweb.Environment 6 @using System.Web 7 8 @{ 9 string metaDescription = string.IsNullOrEmpty(Model.MetaDescription) ? Model.Name : Model.MetaDescription; 10 string klaviyoID = Pageview.AreaSettings.GetString("KlaviyoPublicKey"); 11 12 string baseUrl = Dynamicweb.Context.Current.Request.Url.Scheme + "://" + Dynamicweb.Context.Current.Request.Url.Host; 13 14 15 16 Pageview.Meta.AddTag($"<meta property=\"og:image\" content=\"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{Model.DefaultImage.Value}\">"); 17 Pageview.Meta.AddTag($"<meta property=\"og:image:alt\" content=\"{Model.Name}\">"); 18 Pageview.Meta.AddTag($"<meta property=\"og:description\" content=\"{metaDescription}\">"); 19 20 Pageview.Meta.AddTag("twitter:image", Model.DefaultImage.Value); 21 Pageview.Meta.AddTag("twitter:image:alt", Model.Name); 22 Pageview.Meta.AddTag("twitter:description", metaDescription); 23 } 24 25 @{ 26 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 27 { 28 Dynamicweb.Context.Current.Items["ProductDetails"] = Model; 29 } 30 else 31 { 32 Dynamicweb.Context.Current.Items.Add("ProductDetails", Model); 33 } 34 35 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]); 36 if (isLazyLoadingForProductInfoEnabled) 37 { 38 string showPricesWithVat = Pageview.Area.EcomPricesWithVat.ToLower(); 39 bool neverShowVat = string.IsNullOrEmpty(showPricesWithVat); 40 bool hasVariantId = !string.IsNullOrEmpty(Model.VariantId); 41 string variantIdParam = hasVariantId ? $"/{Model.VariantId}" : ""; 42 string priceFilledProperties = $"Price,PriceFormatted{(showPricesWithVat == "false" && !neverShowVat ? ",PriceWithVat,PriceWithVatFormatted" : "")}"; 43 string productInfoFeed = $@"/dwapi/ecommerce/products/{Model.Id}{variantIdParam} 44 ?UserId={Converter.ToString(Pageview.User?.ID)} 45 &LanguageId={Pageview.Area.EcomLanguageId}&CurrencyCode={Pageview.Area.EcomCurrencyId}&CountryCode={Pageview.Area.EcomCountryCode}&ShopId={Pageview.Area.EcomShopId} 46 &FilledProperties=Id,Price,PriceBeforeDiscount,StockLevel,VariantInfo,NeverOutOfstock,Prices 47 &PriceSettings.ShowPricesWithVat={Pageview.Area.EcomPricesWithVat} 48 &PriceSettings.FilledProperties={priceFilledProperties} 49 &getproductinfo=true"; 50 Dynamicweb.Context.Current.Items["ProductInfoFeed"] = productInfoFeed; 51 52 <script type="module"> 53 swift.LiveProductInfo.init(); 54 </script> 55 } 56 57 string googleTagManagerID = Pageview.AreaSettings.GetString("GoogleTagManagerID"); 58 string googleAnalyticsMeasurementID = Pageview.AreaSettings.GetString("GoogleAnalyticsMeasurementID"); 59 60 bool allowTracking = true; 61 if (CookieManager.IsCookieManagementActive) 62 { 63 var cookieOptInLevel = CookieManager.GetCookieOptInLevel(); 64 allowTracking = cookieOptInLevel == CookieOptInLevel.All || (cookieOptInLevel == CookieOptInLevel.Functional && CookieManager.GetCookieOptInCategories().Contains("Statistical")); 65 } 66 67 if ((!string.IsNullOrWhiteSpace(googleAnalyticsMeasurementID) || !string.IsNullOrWhiteSpace(googleTagManagerID)) && allowTracking) 68 { 69 <script> 70 gtag("event", "view_item", { 71 currency: "@Model.Price.CurrencyCode", 72 value: @PriceViewModelExtensions.ToStringInvariant(Model.Price), 73 items: [ 74 { 75 item_id: "@Model.Number", 76 item_name: "@Dynamicweb.Core.Encoders.HtmlEncoder.JavaScriptStringEncode(Model.Name)", 77 currency: "@Model.Price.CurrencyCode", 78 price: @PriceViewModelExtensions.ToStringInvariant(Model.Price) 79 } 80 ] 81 }); 82 </script> 83 } 84 85 if ((!string.IsNullOrWhiteSpace(klaviyoID))) 86 { 87 88 ProductViewModelSettings productSetting = new ProductViewModelSettings 89 { 90 LanguageId = Dynamicweb.Ecommerce.Common.Context.LanguageID, 91 CurrencyCode = Dynamicweb.Ecommerce.Common.Context.Currency.Code, 92 CountryCode = Dynamicweb.Ecommerce.Common.Context.Country.Code2, 93 ShopId = Pageview.Area.EcomShopId 94 }; 95 96 var productViewModel = ViewModelFactory.CreateView(productSetting, Model.Id, Model.VariantId); 97 98 string productLink = baseUrl + "/" + productViewModel.GetProductLink(GetPageIdByNavigationTag("Shop"), false); 99 100 string defaultProductImage = productViewModel.DefaultImage.Value; 101 102 defaultProductImage = baseUrl + "/" + defaultProductImage; 103 104 string categories = ""; 105 106 var i=0; 107 var count = Model.ProductCategories.Count(); 108 109 foreach (var category in Model.ProductCategories){ 110 categories += category.Value.Name; 111 112 if (++i != count){ 113 categories += ", "; 114 } 115 116 117 } 118 119 120 121 var groups = Model.ProductCategories; 122 123 string brandName = ""; 124 125 foreach (var group in groups) { 126 foreach (var field in group.Value.Fields){ 127 if (field.Value.Name == "Brand"){ 128 if (field.Value.Value != null){ 129 brandName = field.Value.Value.ToString(); 130 } 131 } 132 } 133 } 134 135 136 137 if ((!string.IsNullOrWhiteSpace(Converter.ToString(Pageview.User?.Email)))) 138 { 139 <script> 140 141 klaviyo.identify({ 142 '$email' : '@(Converter.ToString(Pageview.User?.Email))', 143 '$first_name' : '@(Converter.ToString(Pageview.User?.Name))' 144 }); 145 146 </script> 147 } 148 149 <script type="text/javascript"> 150 var item = { 151 "ProductName": "@Dynamicweb.Core.Encoders.HtmlEncoder.JavaScriptStringEncode(Model.Name)", 152 "ProductID": "@Model.Number", 153 "Categories": "@(categories)", 154 "ImageURL": "@defaultProductImage", 155 "URL": "@(productLink)", 156 "Brand": "@(brandName)", 157 "Price": @PriceViewModelExtensions.ToStringInvariant(Model.Price) 158 }; 159 klaviyo.push(["track", "Viewed Product", item]); 160 </script> 161 162 163 <script> 164 165 166 klaviyo.push(["trackViewedItem", { 167 "Title": "@Dynamicweb.Core.Encoders.HtmlEncoder.JavaScriptStringEncode(Model.Name)", 168 "ItemId": "@Model.Number", 169 "Categories": "@(categories)", 170 "ImageUrl": "@defaultProductImage", 171 "Url": "@(productLink)", 172 "Metadata": { 173 "Price": @PriceViewModelExtensions.ToStringInvariant(Model.Price) 174 } 175 }]); 176 </script> 177 178 } 179 } 180 181 <script> 182 window.addEventListener('load', function (event) { 183 swift.Video.init(); 184 }); 185 </script> 186
Error executing template "Designs/Swift/Paragraph/Swift_ProductDetailsImage.cshtml" System.ArgumentNullException: Value cannot be null. Parameter name: source at System.Linq.Enumerable.Where[TSource](IEnumerable`1 source, Func`2 predicate) at CompiledRazorTemplates.Dynamic.RazorEngine_116474503af0489f90f3c2dddabacee1.Execute() in D:\dynamicweb.net\Solutions\Flex Media\danzafe.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_ProductDetailsImage.cshtml:line 78 at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader) at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer) at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter) at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template) at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template) at Dynamicweb.Rendering.Template.RenderRazorTemplate()
1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 @using Dynamicweb.Frontend 4 @using System.IO 5 6 @functions { 7 public ProductViewModel product { get; set; } = new ProductViewModel(); 8 public string galleryLayout { get; set; } 9 public string[] supportedImageFormats { get; set; } 10 public string[] supportedVideoFormats { get; set; } 11 public string[] supportedDocumentFormats { get; set; } 12 public string[] allSupportedFormats { get; set; } 13 14 public class RatioSettings { 15 public string Ratio { get; set; } 16 public string CssClass { get; set; } 17 public string CssVariable { get; set; } 18 public string Fill { get; set; } 19 } 20 21 public RatioSettings GetRatioSettings(string size = "desktop") { 22 var ratioSettings = new RatioSettings(); 23 24 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 25 ratio = ratio != "0" ? ratio : ""; 26 string cssClass = ratio != "" && ratio != "fill" ? " ratio" : ""; 27 string cssVariable = ratio != "" && ratio != "fill" ? "--bs-aspect-ratio: " + ratio : ""; 28 cssClass = ratio == "fill" && size == "mobile" ? " ratio" : cssClass; 29 cssVariable = ratio == "fill" && size == "mobile" ? "--bs-aspect-ratio: 66%" : cssVariable; 30 31 ratioSettings.Ratio = ratio; 32 ratioSettings.CssClass = cssClass; 33 ratioSettings.CssVariable = cssVariable; 34 ratioSettings.Fill = ratio == "fill" ? " h-100" : ""; 35 36 return ratioSettings; 37 } 38 39 public string GetArrowsColor() 40 { 41 var invertColor = Model.Item.GetBoolean("InvertModalArrowsColor"); 42 var arrowsColor = invertColor ? " carousel-dark" : string.Empty; 43 return arrowsColor; 44 } 45 } 46 47 @{ 48 ProductViewModel product = null; 49 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 50 { 51 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 52 } 53 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 54 { 55 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 56 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 57 58 if (productList?.Products is object) 59 { 60 product = productList.Products[0]; 61 } 62 } 63 } 64 65 @if (product is object) { 66 @* Supported formats *@ 67 supportedImageFormats = new string[] { ".jpg", ".jpeg", ".webp", ".png", ".gif", ".bmp", ".tiff" }; 68 supportedVideoFormats = new string[] { "youtu.be", "youtube", "vimeo", ".mp4", ".webm" }; 69 supportedDocumentFormats = new string[] { ".pdf", ".docx", ".xlsx", ".ppt", "pptx" }; 70 allSupportedFormats = supportedImageFormats.Concat(supportedVideoFormats).Concat(supportedDocumentFormats).ToArray(); 71 72 @* Collect the assets *@ 73 var selectedAssetCategories = Model.Item.GetRawValueString("ImageAssets").Split(',').ToList(); 74 bool includeImagePatternImages = Model.Item.GetBoolean("ImagePatternImages"); 75 76 @* Needed image data collection to support both DefaultImage, ImagePatterns and Image Assets *@ 77 string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : ""; 78 IEnumerable<MediaViewModel> assetsImages = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets); 79 assetsImages = assetsImages.OrderByDescending(x => x.Value.Equals(defaultImage)); 80 IEnumerable<MediaViewModel> assetsList = new MediaViewModel[]{}; 81 assetsList = includeImagePatternImages ? assetsList.Union(product.ImagePatternImages) : assetsList; 82 assetsList = assetsList.Union(assetsImages); 83 assetsList = includeImagePatternImages && assetsList.Count() == 0 ? assetsList.Append(product.DefaultImage) : assetsList; 84 85 86 bool defaultImageFallback = Model.Item.GetBoolean("DefaultImageFallback"); 87 bool showOnlyPrimaryImage = Model.Item.GetBoolean("ShowOnlyPrimaryImage"); 88 89 int totalAssets = 0; 90 if (showOnlyPrimaryImage == false) { 91 foreach (MediaViewModel asset in assetsList) { 92 var assetValue = asset.Value; 93 foreach (string format in allSupportedFormats) { 94 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 95 totalAssets++; 96 } 97 } 98 } 99 } 100 101 if((totalAssets == 0 && product.DefaultImage != null && selectedAssetCategories.Count() == 0) || (showOnlyPrimaryImage == true && product.DefaultImage != null) || totalAssets == 0 && defaultImageFallback) 102 { 103 assetsList = new List<MediaViewModel>(){ product.DefaultImage }; 104 totalAssets = 1; 105 } 106 107 108 @* Theme settings *@ 109 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 110 111 var badgeParms = new Dictionary<string, object>(); 112 badgeParms.Add("size", "h5"); 113 badgeParms.Add("saleBadgeType", Model.Item.GetRawValue("SaleBadgeType")); 114 badgeParms.Add("saleBadgeCssClassName", Model.Item.GetRawValue("SaleBadgeDesign")); 115 badgeParms.Add("newBadgeCssClassName", Model.Item.GetRawValue("NewBadgeDesign")); 116 badgeParms.Add("newPublicationDays", Model.Item.GetInt32("NewPublicationDays")); 117 badgeParms.Add("campaignBadgesValues", Model.Item.GetRawValueString("CampaignBadges")); 118 119 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("SaleBadgeDesign")) && Model.Item.GetRawValueString("SaleBadgeDesign") != "none" ? true : false; 120 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("NewBadgeDesign")) && Model.Item.GetRawValueString("NewBadgeDesign") != "none" ? true : false; 121 DateTime createdDate = product.Created.Value; 122 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false; 123 showBadges = (newBadgeEnabled && Model.Item.GetInt32("NewPublicationDays") == 0) || (newBadgeEnabled && (createdDate.AddDays(Model.Item.GetInt32("NewPublicationDays")) > DateTime.Now)) ? true : showBadges; 124 showBadges = !string.IsNullOrEmpty(Model.Item.GetRawValueString("CampaignBadges")) ? true : showBadges; 125 126 @* Get assets from selected categories or get all assets *@ 127 if (totalAssets != 0) { 128 int assetNumber = 0; 129 int thumbnailNumber = 0; 130 int modalAssetNumber = 0; 131 var assetsListUnique = assetsList.GroupBy(x => x.Value).Select(g => g.First()).ToList(); 132 133 <div class="h-100@(theme) position-relative item_@Model.Item.SystemName.ToLower()"> 134 <div id="SmallScreenImages_@Model.ID" class="carousel@(GetArrowsColor())" data-bs-ride="carousel"> 135 <div class="carousel-inner h-100"> 136 @foreach (MediaViewModel asset in assetsListUnique) { 137 var assetValue = asset.Value; 138 foreach (string format in allSupportedFormats) { 139 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 140 string activeSlide = assetNumber == 0 ? "active" : ""; 141 142 <div class="carousel-item @activeSlide" data-bs-interval="99999"> 143 144 @{ 145 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 146 string size = "mobile"; 147 148 <div class="h-100 @(imageTheme)"> 149 @foreach (string imageFormat in supportedImageFormats) { //Images 150 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) { 151 152 if (product is object) 153 { 154 string productName = product.Name; 155 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 156 string imageLinkPath = Dynamicweb.Context.Current.Server.UrlEncode(imagePath); 157 158 RatioSettings ratioSettings = GetRatioSettings(size); 159 160 var parms = new Dictionary<string, object>(); 161 parms.Add("alt", productName + asset.Keywords); 162 parms.Add("itemprop", "image"); 163 parms.Add("fullwidth", true); 164 parms.Add("columns", Model.GridRowColumnCount); 165 if (!string.IsNullOrEmpty(asset.DisplayName)) { 166 parms.Add("title", asset.DisplayName); 167 } 168 169 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") { 170 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 171 } else { 172 parms.Add("cssClass", "mw-100 mh-100"); 173 } 174 175 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 176 <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"> 177 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 178 </div> 179 </a> 180 } 181 } 182 } 183 @foreach (string imageFormat in supportedVideoFormats) { //Videos 184 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) { 185 if (Model.Item.GetString("OpenVideoInModal") == "true") { 186 187 188 if (product is object) 189 { 190 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 191 192 string videoScreendumpPath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : ""; 193 string videoId = videoScreendumpPath.Substring(videoScreendumpPath.LastIndexOf('/') + 1); 194 videoScreendumpPath = videoScreendumpPath.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || videoScreendumpPath.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "https://img.youtube.com/vi/" + videoId + "/maxresdefault.jpg" : videoScreendumpPath; 195 196 string vimeoJsClass = videoScreendumpPath.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "js-vimeo-video-thumbnail" : ""; 197 videoScreendumpPath = videoScreendumpPath.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "" : videoScreendumpPath; 198 199 string productName = product.Name; 200 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 201 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 202 203 RatioSettings ratioSettings = GetRatioSettings(size); 204 205 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable); cursor: pointer" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 206 <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"> 207 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 208 @if (videoScreendumpPath.IndexOf(".mp4", StringComparison.OrdinalIgnoreCase) < 0) 209 { 210 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@productName" @assetTitle class="@vimeoJsClass mw-100 mh-100" data-video-id="@videoId" style="object-fit: cover;" onload="CheckIfVideoThumbnailExist(this)"> 211 } 212 else 213 { 214 string videoType = Path.GetExtension(asset.Value).ToLower(); 215 216 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 217 <source src="@asset.Value" type="video/@videoType.Replace(".", "")"> 218 </video> 219 } 220 </div> 221 </div> 222 223 <script> 224 function CheckIfVideoThumbnailExist(image) { 225 if (image.width == 120) { 226 const lowQualityImage = "https://img.youtube.com/vi/@(videoId)/hqdefault.jpg" 227 image.src = lowQualityImage; 228 } 229 } 230 </script> 231 } 232 } else { 233 if (product is object) 234 { 235 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Name; 236 string videoId = asset.Value.Substring(asset.Value.LastIndexOf('/') + 1); 237 string type = assetValue.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "youtube" : ""; 238 type = assetValue.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "vimeo" : type; 239 type = assetValue.IndexOf(".mp4", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf(".webm", StringComparison.OrdinalIgnoreCase) >= 0 ? "selfhosted" : type; 240 241 string openInModal = Model.Item.GetString("OpenVideoInModal"); 242 bool autoPlay = Model.Item.GetBoolean("VideoAutoPlay"); 243 244 <div class="h-100" itemscope itemtype="https://schema.org/VideoObject"> 245 <span class="visually-hidden" itemprop="name">@assetName</span> 246 <span class="visually-hidden" itemprop="contentUrl">@asset.Value</span> 247 <span class="visually-hidden" itemprop="thumbnailUrl">@asset.Value</span> 248 249 @if (type != "selfhosted") 250 { 251 <div 252 id="player_@(Pageview.CurrentParagraph.ID)_@(videoId)_@size" 253 class="plyr__video-embed" 254 data-plyr-provider="@(type)" 255 data-plyr-embed-id="@videoId" 256 style="--plyr-color-main: var(--swift-foreground-color); height: 100%"> 257 </div> 258 259 <script type="module" src="/Files/Templates/Designs/Swift/Assets/js/plyr.js"></script> 260 261 <script type="module"> 262 var player = new Plyr('#player_@(Pageview.CurrentParagraph.ID)_@(videoId)_@size', { 263 type: 'video', 264 youtube: { 265 noCookie: true, 266 showinfo: 0 267 }, 268 fullscreen: { 269 enabled: true, 270 iosNative: true, 271 } 272 }); 273 274 @if (autoPlay && openInModal == "false") 275 { 276 <text> 277 player.config.autoplay = true; 278 player.config.muted = true; 279 player.config.volume = 0; 280 player.media.loop = true; 281 282 player.on('ready', function() { 283 if (player.config.autoplay === true) { 284 player.media.play(); 285 } 286 }); 287 </text> 288 } 289 290 @if (openInModal == "true") 291 { 292 <text> 293 var productDetailsGalleryModal = document.querySelector('#modal_@Model.ID') 294 productDetailsGalleryModal.addEventListener('hidden.bs.modal', function (event) { 295 player.media.pause(); 296 }) 297 </text> 298 } 299 </script> 300 } 301 else 302 { 303 string autoPlayAttributes = (autoPlay && openInModal == "false") ? "loop autoplay muted playsinline" : ""; 304 string videoType = Path.GetExtension(assetValue).ToLower(); 305 306 <video preload="auto" @autoPlayAttributes class="h-100 w-100" style="object-fit: cover;" controls> 307 <source src="@assetValue" type="video/@videoType.Replace(".", "")"> 308 </video> 309 } 310 </div> 311 } 312 } 313 } 314 } 315 @foreach (string imageFormat in supportedDocumentFormats) { //Documents 316 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) { 317 if (product is object) 318 { 319 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 320 321 string productName = product.Name; 322 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 323 string imageLinkPath = imagePath; 324 325 RatioSettings ratioSettings = GetRatioSettings(size); 326 327 var parms = new Dictionary<string, object>(); 328 parms.Add("alt", productName + asset.Keywords); 329 parms.Add("itemprop", "image"); 330 parms.Add("fullwidth", true); 331 parms.Add("columns", Model.GridRowColumnCount); 332 if (!string.IsNullOrEmpty(asset.DisplayName)) { 333 parms.Add("title", asset.DisplayName); 334 } 335 336 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") { 337 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 338 } else { 339 parms.Add("cssClass", "mw-100 mh-100"); 340 } 341 342 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download alt="@Translate("Download")"> 343 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 344 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 345 @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) { 346 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 347 } 348 </div> 349 </a> 350 } 351 } 352 } 353 </div> 354 } 355 356 357 </div> 358 assetNumber++; 359 } 360 } 361 } 362 </div> 363 </div> 364 365 @if (totalAssets > 1) { 366 <div id="SmallScreenImagesThumbnails_@Model.ID" class="grid grid-10 gap-2 overflow-x-auto my-3"> 367 @foreach (MediaViewModel asset in assetsListUnique) { 368 var assetValue = asset.Value; 369 foreach (string format in allSupportedFormats) { 370 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 371 string imagePath = Dynamicweb.Context.Current.Server.UrlEncode(assetValue); 372 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; 373 string imagePathThumb = imagePath.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) < 0 && imagePath.IndexOf(".mp4", StringComparison.OrdinalIgnoreCase) < 0 ? $"/Admin/Public/GetImage.ashx?image={imagePath}&width=180&format=webp" : imagePath; 374 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 375 376 string videoId = assetValue.Substring(assetValue.LastIndexOf('/') + 1); 377 string vimeoJsClass = assetValue.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "js-vimeo-video-thumbnail" : ""; 378 379 bool isDocument = false; 380 foreach (string documentFormat in supportedDocumentFormats) { 381 if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) { 382 isDocument = true; 383 } 384 } 385 386 string assetName = asset.Name; 387 assetName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 388 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 389 390 if (!isDocument) { 391 RatioSettings ratioSettings = GetRatioSettings("desktop"); 392 393 <div class="border outline-none @(ratioSettings.CssClass)" style="@(ratioSettings.CssVariable); cursor: pointer" data-bs-target="#SmallScreenImages_@Model.ID" data-bs-slide-to="@thumbnailNumber"> 394 <div class="d-flex align-items-center justify-content-center overflow-hidden position-absolute h-100"> 395 @foreach (string videoFormat in supportedVideoFormats) { //Videos 396 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) { 397 <div class="icon-3 position-absolute text-light" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 398 } 399 } 400 </div> 401 @if (imagePathThumb.IndexOf(".mp4", StringComparison.OrdinalIgnoreCase) < 0) { 402 <img src="@imagePathThumb" alt="@assetName" @assetTitle class="p-0 p-lg-1 @vimeoJsClass w-100 h-100" style="object-fit: contain" data-video-id="@videoId"> 403 } else { 404 string videoType = Path.GetExtension(asset.Value).ToLower(); 405 406 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 407 <source src="@imagePathThumb" type="video/@videoType.Replace(".", "")"> 408 </video> 409 } 410 </div> 411 } else { 412 <a href="@assetValue" class="ratio ratio-4x3 border outline-none" style="cursor: pointer" download title="@asset.Value"> 413 @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) { 414 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 415 <div class="icon-3 position-absolute text-light" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 416 </div> 417 <img src="@imagePathThumb" alt="@assetName" @assetTitle class="p-0 p-lg-1 mw-100 mh-100" style="object-fit: cover;"> 418 } else { 419 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 420 <div class="icon-3 position-absolute" style="z-index: 1">@ReadFile(iconPath + "file-text.svg")</div> 421 </div> 422 } 423 </a> 424 } 425 426 thumbnailNumber++; 427 } 428 } 429 } 430 </div> 431 } 432 433 @if (showBadges) { 434 <div class="position-absolute top-0 left-0 p-2 p-lg-3"> 435 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms) 436 </div> 437 } 438 </div> 439 440 @* Modal with slides *@ 441 <div class="modal fade swift_products-details-images-modal" id="modal_@Model.ID" tabindex="-1" aria-labelledby="productDetailsGalleryModalTitle_@Model.ID" aria-hidden="true"> 442 <div class="modal-dialog modal-dialog-centered modal-xl"> 443 <div class="modal-content"> 444 <div class="modal-header visually-hidden"> 445 <strong class="modal-title" id="productDetailsGalleryModalTitle_@Model.ID">@product.Title</strong> 446 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 447 </div> 448 <div class="modal-body p-2 p-lg-3 h-100"> 449 <div id="ModalCarousel_@Model.ID" class="carousel@(GetArrowsColor()) h-100" data-bs-ride="carousel"> 450 <div class="carousel-inner h-100"> 451 @foreach (MediaViewModel asset in assetsList) { 452 var assetValue = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 453 foreach (string format in supportedImageFormats.Concat(supportedVideoFormats).ToArray()) { 454 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 455 string imagePath = assetValue; 456 string activeSlide = modalAssetNumber == 0 ? "active" : ""; 457 458 var parms = new Dictionary<string, object>(); 459 parms.Add("cssClass", "d-block mw-100 mh-100 m-auto"); 460 parms.Add("fullwidth", true); 461 parms.Add("columns", Model.GridRowColumnCount); 462 463 <div class="carousel-item @activeSlide h-100" data-bs-interval="99999"> 464 @foreach (string imageFormat in supportedImageFormats) { //Images 465 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) { 466 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 467 } 468 } 469 470 @foreach (string videoFormat in supportedVideoFormats) { //Videos 471 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) { 472 if (product is object) 473 { 474 string size = "modal"; 475 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Name; 476 string videoId = asset.Value.Substring(asset.Value.LastIndexOf('/') + 1); 477 string type = assetValue.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "youtube" : ""; 478 type = assetValue.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "vimeo" : type; 479 type = assetValue.IndexOf(".mp4", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf(".webm", StringComparison.OrdinalIgnoreCase) >= 0 ? "selfhosted" : type; 480 481 string openInModal = Model.Item.GetString("OpenVideoInModal"); 482 bool autoPlay = Model.Item.GetBoolean("VideoAutoPlay"); 483 484 <div class="h-100" itemscope itemtype="https://schema.org/VideoObject"> 485 <span class="visually-hidden" itemprop="name">@assetName</span> 486 <span class="visually-hidden" itemprop="contentUrl">@asset.Value</span> 487 <span class="visually-hidden" itemprop="thumbnailUrl">@asset.Value</span> 488 489 @if (type != "selfhosted") 490 { 491 <div 492 id="player_@(Pageview.CurrentParagraph.ID)_@(videoId)_@size" 493 class="plyr__video-embed" 494 data-plyr-provider="@(type)" 495 data-plyr-embed-id="@videoId" 496 style="--plyr-color-main: var(--swift-foreground-color); height: 100%"> 497 </div> 498 499 <script type="module" src="/Files/Templates/Designs/Swift/Assets/js/plyr.js"></script> 500 501 <script type="module"> 502 var player = new Plyr('#player_@(Pageview.CurrentParagraph.ID)_@(videoId)_@size', { 503 type: 'video', 504 youtube: { 505 noCookie: true, 506 showinfo: 0 507 }, 508 fullscreen: { 509 enabled: true, 510 iosNative: true, 511 } 512 }); 513 514 @if (autoPlay && openInModal == "false") 515 { 516 <text> 517 player.config.autoplay = true; 518 player.config.muted = true; 519 player.config.volume = 0; 520 player.media.loop = true; 521 522 player.on('ready', function() { 523 if (player.config.autoplay === true) { 524 player.media.play(); 525 } 526 }); 527 </text> 528 } 529 530 @if (openInModal == "true") 531 { 532 <text> 533 var productDetailsGalleryModal = document.querySelector('#modal_@Model.ID') 534 productDetailsGalleryModal.addEventListener('hidden.bs.modal', function (event) { 535 player.media.pause(); 536 }) 537 </text> 538 } 539 </script> 540 } 541 else 542 { 543 string autoPlayAttributes = (autoPlay && openInModal == "false") ? "loop autoplay muted playsinline" : ""; 544 string videoType = Path.GetExtension(assetValue).ToLower(); 545 546 <video preload="auto" @autoPlayAttributes class="h-100 w-100" style="object-fit: cover;" controls> 547 <source src="@assetValue" type="video/@videoType.Replace(".", "")"> 548 </video> 549 } 550 </div> 551 } 552 } 553 } 554 </div> 555 556 modalAssetNumber++; 557 } 558 } 559 } 560 <button class="carousel-control-prev" type="button" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide="prev"> 561 <span class="carousel-control-prev-icon" aria-hidden="true"></span> 562 <span class="visually-hidden">@Translate("Previous")</span> 563 </button> 564 <button class="carousel-control-next" type="button" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide="next"> 565 <span class="carousel-control-next-icon" aria-hidden="true"></span> 566 <span class="visually-hidden">@Translate("Next")</span> 567 </button> 568 </div> 569 </div> 570 </div> 571 </div> 572 </div> 573 </div> 574 } else if (Pageview.IsVisualEditorMode) { 575 RatioSettings ratioSettings = GetRatioSettings("desktop"); 576 577 <div class="h-100 @theme"> 578 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> 579 <img src="/Files/Images/missing_image.jpg" loading="lazy" decoding="async" class="mh-100 mw-100" style="object-fit: cover;"> 580 </div> 581 </div> 582 } 583 } else if (Pageview.IsVisualEditorMode) { 584 <div class="alert alert-dark m-0">@Translate("No products available")</div> 585 } 586 587 588 589 590 591
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_1915370b24494abf928b628efbbc267f.Execute() in D:\dynamicweb.net\Solutions\Flex Media\danzafe.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_ProductSpecification.cshtml:line 28 at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader) at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer) at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter) at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template) at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template) at Dynamicweb.Rendering.Template.RenderRazorTemplate()
1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 4 @{ 5 ProductViewModel product = null; 6 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 7 { 8 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 9 } 10 else if (Pageview.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 @helper RenderFieldsFromList(Dictionary<string, FieldValueViewModel> fields, string layout) 238 { 239 string size = Model.Item.GetRawValueString("Size", "full"); 240 string gaps = size != "full" ? " gap-1" : string.Empty; 241 bool hideFieldLabels = Model.Item.GetBoolean("HideFieldLabels"); 242 bool hideFieldsWithZeroValue = Model.Item.GetBoolean("HideFieldsWithZeroValue"); 243 244 if (layout == "columns") { 245 <div class="g-col-12"> 246 <div class="grid@(gaps)"> 247 @foreach (var field in fields) 248 { 249 {@RenderField(field.Value, layout)} 250 } 251 </div> 252 </div> 253 } 254 if (layout == "list") { 255 <div class="g-col-12"> 256 <dl class="grid@(gaps)"> 257 @foreach (var field in fields) 258 { 259 {@RenderField(field.Value, layout)} 260 } 261 </dl> 262 </div> 263 } 264 if (layout == "table") 265 { 266 string tableSize = size == "full" ? "" : " table-sm"; 267 <div class="g-col-12"> 268 <table class="table table-striped@(tableSize)"> 269 @foreach (var field in fields) 270 { 271 {@RenderField(field.Value, layout)} 272 } 273 </table> 274 </div> 275 } 276 if (layout == "bullets") 277 { 278 string listSize = size == "full" ? "" : "m-0 p-0 lh-1 fs-7 opacity-75"; 279 string listStyle = size == "full" ? "" : "style=\"list-style-position: inside\""; 280 <div class="g-col-12"> 281 <ul class="@listSize" @listStyle> 282 @foreach (var field in fields) 283 { 284 {@RenderField(field.Value, layout)} 285 } 286 </ul> 287 </div> 288 } 289 if (layout == "commas") 290 { 291 List<string> featuresList = new List<string>(); 292 293 foreach (var field in fields) 294 { 295 string firstListItemValue = string.Empty; //Hack to support field type providers with a single value 296 297 if (field.Value?.Value != null) 298 { 299 if (field.Value.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 300 { 301 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 302 303 //Hack to support field type providers with a single value 304 if (values.FirstOrDefault() != null) 305 { 306 firstListItemValue = values.FirstOrDefault().Value; 307 } 308 } 309 } 310 311 if (!hideFieldsWithZeroValue || (firstListItemValue != "0" && firstListItemValue != "0.0" && field.Value.Value.ToString() != "0" && field.Value.Value.ToString() != "0.0")) 312 { 313 if (field.Value.Value is object && !string.IsNullOrEmpty(field.Value.Value.ToString())) 314 { 315 if (field.Value.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 316 { 317 List<string> options = new List<string>(); 318 foreach (FieldOptionValueViewModel option in field.Value.Value as System.Collections.Generic.List<FieldOptionValueViewModel>) 319 { 320 if (!string.IsNullOrWhiteSpace(option.Value)) 321 { 322 if (option.Value.ToString().Contains("#") && (Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour"))) 323 { 324 string colorSpan = "<span class=\"colorbox-sm\" style=\"background-color: " + option.Value + "\"></span>"; 325 options.Add(colorSpan); 326 } 327 else if (!string.IsNullOrEmpty(option.Value)) 328 { 329 options.Add(option.Name); 330 } 331 } 332 } 333 string optionsString = (string.Join(", ", options.Select(x => x.ToString()).ToArray())); 334 if ((Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour"))) 335 { 336 optionsString = (string.Join(" ", options.Select(x => x.ToString()).ToArray())); 337 } 338 339 if (!string.IsNullOrEmpty(optionsString)) 340 { 341 if (!hideFieldLabels) 342 { 343 featuresList.Add(field.Value.Name + ": " + optionsString); 344 } 345 else 346 { 347 featuresList.Add(optionsString); 348 } 349 } 350 } 351 else 352 { 353 if (!string.IsNullOrWhiteSpace(field.Value.Value.ToString())) 354 { 355 if (field.Value.Value.ToString().Contains("#") && (Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour"))) 356 { 357 string colorSpan = "<span class=\"colorbox-sm\" style=\"background-color: " + field.Value.Value + "\"></span>"; 358 359 if (!hideFieldLabels) 360 { 361 featuresList.Add(field.Value.Name + ": " + colorSpan); 362 } 363 else 364 { 365 featuresList.Add(colorSpan); 366 } 367 } 368 else 369 { 370 if (!hideFieldLabels) 371 { 372 featuresList.Add(field.Value.Name + ": " + field.Value.Value.ToString()); 373 } 374 else 375 { 376 featuresList.Add(field.Value.Value.ToString()); 377 } 378 } 379 } 380 } 381 } 382 } 383 } 384 385 string featuresString = (string.Join(", ", featuresList.Select(x => x.ToString()).ToArray())); 386 387 <div class="g-col-12 opacity-75 fs-7">@featuresString</div> 388 } 389 } 390 391 @helper RenderField(FieldValueViewModel field, string layout) 392 { 393 string size = Model.Item.GetRawValueString("Size", "full"); 394 string fieldValue = field?.Value != null ? field.Value.ToString() : ""; 395 bool hideFieldLabels = Model.Item.GetBoolean("HideFieldLabels"); 396 bool noValues = false; 397 string firstListItemValue = string.Empty; //Hack to support field type providers with a single value 398 bool hideFieldsWithZeroValue = Model.Item.GetBoolean("HideFieldsWithZeroValue"); 399 400 if (!string.IsNullOrEmpty(fieldValue)) 401 { 402 if (field.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 403 { 404 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 405 noValues = values.Count > 0 ? false : true; 406 407 //Hack to support field type providers with a single value 408 if (values.FirstOrDefault() != null) 409 { 410 firstListItemValue = values.FirstOrDefault().Value; 411 } 412 } 413 } 414 415 if (!string.IsNullOrEmpty(fieldValue) && noValues == false) 416 { 417 if (!hideFieldsWithZeroValue || (firstListItemValue != "0" && firstListItemValue != "0.0" && field.Value.ToString() != "0" && field.Value.ToString() != "0.0")) 418 { 419 if (layout == "columns") 420 { 421 422 <div class="grid g-col-6 g-col-lg-4 gap-1"> 423 @if (!hideFieldLabels) 424 { 425 <dt class="g-col-12 g-col-lg-4" t=a>@Translate(field.Name)</dt> 426 } 427 <dd class="g-col-12 g-col-lg-8 mb-0 text-break"> 428 @{ @RenderFieldValue(field) } 429 </dd> 430 </div> 431 } 432 if (layout == "list") 433 { 434 if (!hideFieldLabels) 435 { 436 <dt class="g-col-4" t=b>@Translate(field.Name)</dt> 437 } 438 <dd class="g-col-8 mb-0 text-break"> 439 @{ @RenderFieldValue(field) } 440 </dd> 441 } 442 if (layout == "table") 443 { 444 <tr> 445 @if (!hideFieldLabels) 446 { 447 <th class="w-25 w-lg-50" scope="row">@Translate(field.Name)</th> 448 } 449 <td class="text-break"> 450 @{ @RenderFieldValue(field) } 451 </td> 452 </tr> 453 } 454 if (layout == "bullets") 455 { 456 <li> 457 @if (!hideFieldLabels) 458 { 459 <strong>@Translate(field.Name)</strong> 460 } 461 <span> 462 @{ @RenderFieldValue(field) } 463 </span> 464 </li> 465 } 466 } 467 } 468 } 469 470 @helper RenderFieldValue(FieldValueViewModel field) 471 { 472 string fieldValue = field?.Value != null ? field.Value.ToString() : ""; 473 474 bool isLink = field?.Type == "Link"; 475 bool isColor = false; 476 bool isBrandName = field?.SystemName == "Brand_name"; 477 478 fieldValue = fieldValue == "False" ? Translate("No") : fieldValue; 479 fieldValue = fieldValue == "True" ? Translate("Yes") : fieldValue; 480 481 482 if (field.Value.GetType() == typeof(System.Collections.Generic.List<Dynamicweb.Ecommerce.ProductCatalog.FieldOptionValueViewModel>)) 483 { 484 int valueCount = 0; 485 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 486 int totalValues = values.Count; 487 488 foreach (FieldOptionValueViewModel option in values) 489 { 490 if (!string.IsNullOrEmpty(option.Value)) 491 { 492 if (option.Value.Substring(0, 1) == "#") 493 { 494 isColor = true; 495 } 496 } 497 498 if (!isColor) 499 { 500 @option.Name 501 } 502 else 503 { 504 <span class="colorbox-sm" style="background-color: @option.Value" title="@option.Name"></span> 505 } 506 507 if (valueCount != totalValues && valueCount < (totalValues - 1)) 508 { 509 if (isColor) 510 { 511 <text> </text> 512 } 513 else 514 { 515 <text>, </text> 516 } 517 } 518 valueCount++; 519 } 520 } 521 else 522 { 523 if (fieldValue.Substring(0, 1) == "#") 524 { 525 isColor = true; 526 } 527 528 if (!isColor) 529 { 530 if (isLink) 531 { 532 string linktTitle = !fieldValue.Contains("aspx") ? fieldValue : Translate("Go to link"); 533 string target = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") && fieldValue.Contains("http") ? "target=\"_blank\"" : string.Empty; 534 string rel = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") && fieldValue.Contains("http") ? "rel=\"noopener\"" : string.Empty; 535 536 <a href="@field.Value" title="@field.Name" @target @rel>@linktTitle</a> 537 } 538 else if (isBrandName) 539 { 540 <span itemprop="brand" itemtype="https://schema.org/Brand" itemscope> 541 <span itemprop="name">@fieldValue</span> 542 </span> 543 } 544 else 545 { 546 @fieldValue 547 } 548 549 } 550 else 551 { 552 <span class="colorbox-sm" style="background-color: @fieldValue" title="@fieldValue"></span> 553 } 554 } 555 } 556
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_f4e494d0217341c2973f932a56a75fd4.Execute() in D:\dynamicweb.net\Solutions\Flex Media\danzafe.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_ProductPriceTable.cshtml:line 39 at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader) at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer) at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter) at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template) at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template) at Dynamicweb.Rendering.Template.RenderRazorTemplate()
1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 4 @{ 5 ProductViewModel product = null; 6 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 7 { 8 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 9 } 10 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 11 { 12 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 13 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 14 15 if (productList?.Products is object) 16 { 17 product = productList.Products[0]; 18 } 19 } 20 21 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", ""); 22 bool anonymousUser = Pageview.User == null; 23 bool hidePrice = anonymousUsersLimitations.Contains("price") && anonymousUser; 24 hidePrice = Pageview.IsVisualEditorMode ? false : hidePrice; 25 } 26 27 @if (product is object && !hidePrice) 28 { 29 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]); 30 31 if (Pageview.IsVisualEditorMode && product.Prices.Count == 0) 32 { 33 product.Prices.Add(new PriceListViewModel { Price = new PriceViewModel { Price = 95, PriceFormatted = "€95" }, Quantity = 1 }); 34 product.Prices.Add(new PriceListViewModel { Price = new PriceViewModel { Price = 85, PriceFormatted = "€85" }, Quantity = 2 }); 35 } 36 37 string layout = Model.Item.GetRawValueString("Layout", "list"); 38 39 if (product.Prices.Count > 0) 40 { 41 if (isLazyLoadingForProductInfoEnabled) 42 { 43 <div class="product-prices-container d-none" data-show-if="LiveProductInfo.product.Prices.length > 0"> 44 @if (layout == "list") 45 { 46 <div class="product-prices item_@Model.Item.SystemName.ToLower() list"> 47 <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> 48 </div> 49 } 50 else if (layout == "table") 51 { 52 <div class="item_@Model.Item.SystemName.ToLower() grid"> 53 <table class="table table-sm mt-3 g-col-12 g-col-lg-6"> 54 <thead> 55 <tr> 56 <td>@Translate("QTY")</td> 57 <td>@Translate("pr. PCS")</td> 58 </tr> 59 </thead> 60 <tbody class="product-prices"> 61 <tr class="product-prices-template"> 62 <td class="js-text-price-quantity"></td> 63 <td class="js-text-price-price"></td> 64 </tr> 65 </tbody> 66 </table> 67 </div> 68 } 69 </div> 70 } 71 else 72 { 73 if (layout == "list") 74 { 75 <div class="item_@Model.Item.SystemName.ToLower() list"> 76 @foreach (PriceListViewModel quantityPrice in product.Prices) 77 { 78 string quantityLabel = Translate("PCS"); 79 string quantityPriceSuffix = quantityPrice.Quantity > 1 ? Translate("pr. PCS") : ""; 80 81 <small class="d-block opacity-75"><span>@quantityPrice.Quantity @quantityLabel</span> - <span class="fw-bold">@quantityPrice.Price.PriceFormatted @quantityPriceSuffix</span></small> 82 } 83 </div> 84 } 85 else if (layout == "table") 86 { 87 <div class="item_@Model.Item.SystemName.ToLower() grid"> 88 <table class="table table-sm mt-3 g-col-12 g-col-lg-6"> 89 <thead> 90 <tr> 91 <td>@Translate("QTY")</td> 92 <td>@Translate("pr. PCS")</td> 93 </tr> 94 </thead> 95 <tbody> 96 @foreach (PriceListViewModel quantityPrice in product.Prices) 97 { 98 <tr> 99 <td>@quantityPrice.Quantity</td> 100 <td>@quantityPrice.Price.PriceFormatted</td> 101 </tr> 102 } 103 </tbody> 104 </table> 105 </div> 106 } 107 } 108 } 109 else if (Pageview.IsVisualEditorMode) 110 { 111 <div class="alert alert-dark m-0" role="alert"> 112 <span>@Translate("No products available")</span> 113 </div> 114 } 115 } 116 else if (Pageview.IsVisualEditorMode) 117 { 118 <div class="alert alert-dark m-0" role="alert"> 119 <span>@Translate("No products available")</span> 120 </div> 121 } 122
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_bd5977aff71f42fcb1224e2f38255085.Execute() in D:\dynamicweb.net\Solutions\Flex Media\danzafe.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_ProductPrice.cshtml:line 31 at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader) at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer) at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter) at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template) at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template) at Dynamicweb.Rendering.Template.RenderRazorTemplate()
1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 @using Dynamicweb.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 double VariantDiscountD = productVariant.GetDiscountMatrix(priceContext, product.DefaultUnitId, 1).Price.Price; 99 // string VariantDiscount = productVariant.GetDiscountMatrix(priceContext, product.DefaultUnitId, 1).Price.ToString(); 100 string VariantDiscount = CurrencyTag + productVariant.GetDiscountMatrix(priceContext, product.DefaultUnitId, 1).Price.Price.ToString("#.00"); 101 102 if(VariantDiscountD>0){ 103 if(discountPriceMinD==0 || discountPriceMinD>VariantDiscountD){discountPriceMinD=VariantDiscountD;discountPriceMin=VariantDiscount;} 104 if(discountPriceMaxD==0 || VariantDiscountD>discountPriceMaxD){discountPriceMaxD=VariantDiscountD;discountPriceMax=VariantDiscount;} 105 } 106 } 107 108 109 110 111 112 <div class="@textAlign @liveInfoClass item_@Model.Item.SystemName.ToLower()" data-product-id="@product.Id" data-variant-id="@product.VariantId" @productInfoFeed> 113 @if (showInformativePrice && product.PriceInformative.Price != 0) 114 { 115 <div class="opacity-50"> 116 <span>@Translate("RRP") </span> 117 <span class="text-decoration-line-through text-price">@product.PriceInformative.Price.ToString("#.00")</span> 118 </div> 119 } 120 <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"> 121 <span itemprop="priceCurrency" content="@product.Price.CurrencyCode" class="d-none"></span> 122 123 124 @if (showPricesWithVat == "false" && !neverShowVat) 125 { 126 if (isLazyLoadingForProductInfoEnabled && !Pageview.IsVisualEditorMode) 127 { 128 <span itemprop="price" content="" class="d-none"></span> 129 <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> 130 } 131 else 132 { 133 string beforePrice = !string.IsNullOrEmpty(unitId) ? CurrencyTag + product.GetPrice(unitId).PriceBeforeDiscount.PriceWithoutVat.ToString("#.00") : CurrencyTag + product.PriceBeforeDiscount.PriceWithoutVat.ToString("#.00"); 134 135 <span itemprop="price" content="@product.Price.PriceWithoutVat.ToString().Replace(',','.')" class="d-none"></span> 136 if (product.Price.Price != product.PriceBeforeDiscount.Price) 137 { 138 <span class="text-decoration-line-through opacity-75 @order">@beforePrice</span> 139 } 140 } 141 } 142 else 143 { 144 if (isLazyLoadingForProductInfoEnabled && !Pageview.IsVisualEditorMode) 145 { 146 <span itemprop="price" content="" class="d-none"></span> 147 <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> 148 } 149 else 150 { 151 string beforePrice = !string.IsNullOrEmpty(unitId) ? CurrencyTag + product.GetPrice(unitId).PriceBeforeDiscount.Price.ToString("#.00") : CurrencyTag + product.PriceBeforeDiscount.Price.ToString("#.00"); 152 153 <span itemprop="price" content="@product.Price.Price.ToString().Replace(',','.')" class="d-none"></span> 154 155 if (product.Price.Price != product.PriceBeforeDiscount.Price) 156 { 157 <span class="text-decoration-line-through opacity-75 @order"> 158 <span class="text-price" t=a>@beforePrice</span> 159 </span> 160 } 161 } 162 } 163 164 @if (showPricesWithVat == "false" && !neverShowVat) 165 { 166 if (isLazyLoadingForProductInfoEnabled && !Pageview.IsVisualEditorMode) 167 { 168 <span class="text-price js-text-price"> 169 <span class="spinner-border" role="status"></span> 170 </span> 171 } 172 else 173 { 174 string price = !string.IsNullOrEmpty(unitId) ? product.GetPrice(unitId).Price.PriceWithoutVat.ToString("#.00") : product.Price.PriceWithoutVat.ToString("#.00"); 175 176 if (product?.VariantInfo?.VariantInfo != null) 177 { 178 priceMin = product?.VariantInfo?.PriceMin?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithoutVat.ToString("#.00") : ""; 179 priceMax = product?.VariantInfo?.PriceMax?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithoutVat.ToString("#.00") : ""; 180 } 181 if (priceMin != priceMax) 182 { 183 price = priceMin + " - " + priceMax; 184 } 185 <span class="@theme @contentPadding"> 186 <span class="text-price" t=b>@price</span> 187 </span> 188 } 189 } 190 else 191 { 192 if (isLazyLoadingForProductInfoEnabled && !Pageview.IsVisualEditorMode) 193 { 194 <span class="text-price js-text-price"> 195 <span class="spinner-border" role="status"></span> 196 </span> 197 } 198 else 199 { 200 // string price = !string.IsNullOrEmpty(unitId) ? product.GetPrice(unitId).Price.PriceFormatted : product.Price.PriceFormatted; 201 string price = !string.IsNullOrEmpty(unitId) ? CurrencyTag + product.GetPrice(unitId).Price.Price.ToString("#.00") : CurrencyTag + product.Price.Price.ToString("#.00"); 202 203 double priceD = !string.IsNullOrEmpty(unitId) ? product.GetPrice(unitId).Price.Price : product.Price.Price; 204 double priceMinD= 0; 205 double priceMaxD = 0; 206 if (product?.VariantInfo?.VariantInfo != null) 207 { 208 209 // priceMin = product?.VariantInfo?.PriceMin?.PriceFormatted != null ? product.VariantInfo.PriceMin.PriceFormatted : ""; 210 priceMin = product?.VariantInfo?.PriceMin?.PriceFormatted != null ? CurrencyTag + product.VariantInfo.PriceMin.Price.ToString("#.00") : ""; 211 212 priceMinD = product?.VariantInfo?.PriceMin?.Price != null ? product.VariantInfo.PriceMin.Price : 0; 213 214 // priceMax = product?.VariantInfo?.PriceMax?.PriceFormatted != null ? product.VariantInfo.PriceMax.PriceFormatted : ""; 215 priceMax = product?.VariantInfo?.PriceMin?.PriceFormatted != null ? CurrencyTag + product.VariantInfo.PriceMax.Price.ToString("#.00") : ""; 216 217 priceMaxD = product?.VariantInfo?.PriceMax?.Price != null ? product.VariantInfo.PriceMax.Price : 0; 218 } 219 220 if (priceMin != priceMax){ 221 price = priceMin + " - " + priceMax; 222 223 if(priceMinD>discountPriceMinD && priceMaxD>discountPriceMaxD){ 224 price = discountPriceMin + " - " + discountPriceMax; 225 } 226 } 227 if(priceD == 0 && priceMinD > 0 && priceMinD == priceMaxD){ 228 price = priceMin; 229 } 230 231 232 233 <span class="@theme @contentPadding"> 234 <span class="text-price" t=c>@price</span> 235 </span> 236 } 237 } 238 239 @* Stock state for Schema.org, start *@ 240 @{ 241 Uri url = Dynamicweb.Context.Current.Request.Url; 242 } 243 244 <link itemprop="url" href="@url"> 245 246 @{ 247 bool IsNeverOutOfStock = product.NeverOutOfstock; 248 } 249 250 @if (IsNeverOutOfStock) 251 { 252 <span itemprop="availability" class="d-none">InStock</span> 253 <span class="d-none">@Translate("Available in stock")</span> 254 } 255 else 256 { 257 if (product.StockLevel > 0) 258 { 259 <span itemprop="availability" class="d-none">InStock</span> 260 } 261 else 262 { 263 <span itemprop="availability" class="d-none">OutOfStock</span> 264 } 265 } 266 @* Stock state for Schema.org, stop *@ 267 268 </div> 269 270 @if (showPricesWithVat == "false" && !neverShowVat) 271 { 272 if (isLazyLoadingForProductInfoEnabled && !Pageview.IsVisualEditorMode) 273 { 274 <small class="opacity-85 fst-normal js-text-price-with-vat d-none" data-suffix="@Translate("Incl. VAT")"></small> 275 } 276 else 277 { 278 string price = !string.IsNullOrEmpty(unitId) ? product.GetPrice(unitId).Price.PriceWithVat.ToString("#.00") : product.Price.PriceWithVat.ToString("#.00"); 279 280 if (product?.VariantInfo?.VariantInfo != null) 281 { 282 priceMin = product?.VariantInfo?.PriceMin?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithVat.ToString("#.00") : ""; 283 priceMax = product?.VariantInfo?.PriceMax?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithVat.ToString("#.00") : ""; 284 } 285 if (priceMin != priceMax) 286 { 287 price = priceMin + " - " + priceMax; 288 } 289 <small class="opacity-85 fst-normal">@price @Translate("Incl. VAT")</small> 290 } 291 } 292 </div> 293 } 294 else if (Pageview.IsVisualEditorMode) 295 { 296 <div class="alert alert-dark m-0" role="alert"> 297 <span>@Translate("No products available")</span> 298 </div> 299 } 300
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_13f25aac779b47c7aab5cefd987a0732.Execute() in D:\dynamicweb.net\Solutions\Flex Media\danzafe.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_ProductStock.cshtml:line 93 at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader) at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer) at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter) at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template) at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template) at Dynamicweb.Rendering.Template.RenderRazorTemplate()
1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 @using Dynamicweb.Ecommerce.Stocks 4 @using Dynamicweb.Ecommerce.Products 5 @using Dynamicweb.Ecommerce 6 @using System 7 8 9 @{ 10 ProductViewModel product = null; 11 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 12 { 13 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 14 } 15 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 16 { 17 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 18 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 19 20 if (productList?.Products is object) 21 { 22 product = productList.Products[0]; 23 } 24 } 25 26 bool isErpConnectionDown = !Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsWebServiceConnectionAvailable"]); 27 bool hideStock = Model.Item.GetBoolean("HideStockState") || (Pageview.AreaSettings.GetBoolean("ErpDownHideStock") && isErpConnectionDown); 28 } 29 30 @if (product is object && !hideStock) 31 { 32 //bool IsNeverOutOfStock = product.NeverOutOfstock; 33 bool IsNeverOutOfStock = false; 34 35 string horizontalAlign = Model.Item.GetRawValueString("HorizontalAlignment", ""); 36 horizontalAlign = horizontalAlign == "center" ? "text-center" : horizontalAlign; 37 horizontalAlign = horizontalAlign == "end" ? "text-end" : horizontalAlign; 38 bool hasExpectedDelivery = product.ExpectedDelivery != null && product.ExpectedDelivery > DateTime.Now; 39 string expectedDeliveryDate = product.ExpectedDelivery?.ToShortDateString() ?? ""; 40 41 string liveInfoClass = ""; 42 string productInfoFeed = ""; 43 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]); 44 45 46 if (!IsNeverOutOfStock) 47 { 48 string deliveryLabel = !string.IsNullOrEmpty(product.StockDeliveryText) ? $"{product.StockDeliveryText}" : ""; 49 deliveryLabel += !string.IsNullOrEmpty(product.StockDeliveryValue) ? $" {product.StockDeliveryValue}" : ""; 50 51 52 string firstUnitId = product?.UnitOptions?.FirstOrDefault() != null ? product.UnitOptions.FirstOrDefault().Id : ""; 53 string defaultUnitId = !string.IsNullOrEmpty(product.DefaultUnitId) ? product.DefaultUnitId : firstUnitId; 54 string unitId = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("UnitId")) ? Dynamicweb.Context.Current.Request.Form.Get("UnitId") : defaultUnitId; 55 56 var reserveMode = Dynamicweb.Ecommerce.Frontend.Cart.ProductReserve.Mode; 57 double? reservedAmount = 0; 58 if (reserveMode == Dynamicweb.Ecommerce.Frontend.Cart.ProductReserveMode.AddToCart) 59 { 60 reservedAmount = Dynamicweb.Ecommerce.Frontend.Cart.ProductReserve.GetReservedAmount(product.Id, product.VariantId, 0, unitId); 61 } 62 double? currentStockLevel = product.StockLevel - reservedAmount; 63 64 string stockStateLabel = currentStockLevel > 0 ? Translate("In stock") : Translate("Out of stock"); 65 stockStateLabel = !string.IsNullOrEmpty(product.StockStatus) ? product.StockStatus : stockStateLabel; 66 67 string stockStateCss = currentStockLevel > 0 ? "text-success" : "text-danger"; 68 string stockStateIconCss = currentStockLevel > 0 ? "bg-success" : "bg-danger"; 69 70 string stockLabel = ""; 71 string stockLabelWebshop = Translate("In local stock"); 72 string stockLabelSupplier = Translate("In remote stock"); 73 string deliveryLabelSupplier = Translate("1-2 days delivery time from remote warehouse"); 74 75 var todaysDate = DateTime.Today; 76 // var todaysDate = DateTime.Now.AddDays(+1); 77 var parameterDate = product.ExpectedDelivery; 78 string ExpDelivery = ""; 79 80 if(parameterDate >= todaysDate){ 81 ExpDelivery = string.Format("{0:dd/MM/yyyy}", product.ExpectedDelivery); 82 } 83 // PRODUCTEXPECTEDDELIVERY "Forventes på lager den" 84 85 double StockWebshop = 0; 86 double StockSupplier = 0; 87 88 89 Dictionary<string, FieldValueViewModel> fields = product.ProductFields; 90 91 string supplierDeliveryTime = ""; 92 93 foreach (var field in fields) { 94 95 96 if (field.Value.SystemName == "CDLevLtid"){ 97 supplierDeliveryTime = field.Value?.Value != null ? field.Value.Value.ToString() : ""; 98 99 } 100 101 } 102 103 104 105 //Product productObject = Services.Products.GetProductById(product.Id, product.VariantId, Pageview.Area.EcomLanguageId); 106 //var stockLocations = Dynamicweb.Ecommerce.Stocks.StockService.GetStockLocation(1); 107 //Product productObject = Services.Products.GetProductById(product.Id, product.VariantId, product.LanguageId); 108 IEnumerable<StockLocation> stockLocations = Services.StockService.GetStockLocations(); 109 //double unitStock = productObject.GetUnitStock(stockLocation, unitId); 110 //if (stockLocations.Any() && !string.IsNullOrEmpty(product.VariantId)) 111 //{ 112 // foreach (var stockLocation in stockLocations) 113 // { 114 115 // if (stockLocation.ID == 1) 116 // { 117 // StockWebshop = productObject.GetUnitStock(stockLocation); 118 119 // } 120 // if (stockLocation.ID == 2) 121 // { 122 // StockSupplier = productObject.GetUnitStock(stockLocation); 123 124 // } 125 // } 126 //} 127 //else 128 //{ 129 // foreach (var unit in product.StockUnits) 130 // { 131 // if (unit.StockLocationId == 1) { StockWebshop = unit.StockLevel; } 132 // if (unit.StockLocationId == 2) { StockSupplier = unit.StockLevel; } 133 // } 134 //} 135 136 137 var stockUnits = Dynamicweb.Ecommerce.Services.StockService.GetStockUnits(product.Id, product.VariantId).ToList(); 138 if (stockUnits.Any()) 139 { 140 var webShopStockUnit = stockUnits.FirstOrDefault(su => su.StockLocationId == 1); 141 if (webShopStockUnit != null) 142 { 143 StockWebshop = webShopStockUnit.StockQuantity; 144 145 } 146 var supplierStockUnit = stockUnits.FirstOrDefault(su => su.StockLocationId == 2); 147 if (supplierStockUnit != null) 148 { 149 StockSupplier = supplierStockUnit.StockQuantity; 150 } 151 } 152 153 154 155 if (StockWebshop > 0){ 156 157 currentStockLevel = StockWebshop; 158 stockLabel = Translate("In stock"); 159 deliveryLabel = ""; 160 stockStateCss = "text-success"; 161 162 } else if (StockSupplier > 0){ 163 164 currentStockLevel = StockSupplier; 165 stockLabel = Translate("In stock"); 166 deliveryLabel = Translate("1-2 days delivery time from remote warehouse"); 167 stockStateCss = "text-success"; 168 169 } else{ 170 171 currentStockLevel = 0; 172 stockLabel = Translate("Out of stock"); 173 deliveryLabel = supplierDeliveryTime + Translate(" days delivery time"); 174 stockStateCss = "text-danger"; 175 176 177 } 178 179 180 181 182 <div class="js-stock-state @horizontalAlign item_@Model.Item.SystemName.ToLower()"> 183 <div class="small"> 184 185 @{ 186 bool disableShowStock = false; 187 188 if (product.VariantInfo.VariantInfo != null && string.IsNullOrEmpty(product.VariantId)){ 189 disableShowStock = true; 190 } 191 } 192 193 194 195 196 @if (!Model.Item.GetBoolean("HideInventory") && !disableShowStock) 197 { 198 199 @* 200 if (currentStockLevel > 0){ 201 <span class="@stockStateCss js-text-stock">@currentStockLevel</span> 202 <span class="@stockStateCss">@stockLabel</span> 203 <div class="opacity-75">@deliveryLabel</div> 204 } else { 205 <div class="opacity-75">@deliveryLabel</div> 206 } 207 208 <hr> 209 *@ 210 211 if(StockWebshop>0){ 212 <span class="@stockStateCss js-text-stock">@StockWebshop</span> 213 <span class="@stockStateCss">@stockLabelWebshop</span> 214 } 215 if(StockWebshop>0 && StockSupplier>0){ 216 <span class="@stockStateCss">@Translate("and")</span> 217 } 218 if(StockSupplier>0){ 219 <span class="@stockStateCss js-text-stock">@StockSupplier</span> 220 <span class="@stockStateCss">@stockLabelSupplier</span> 221 if(StockWebshop<=0){ 222 <div class="opacity-75">@deliveryLabelSupplier</div> 223 } 224 } 225 if(StockWebshop<=0 && StockSupplier<=0){ 226 <div class="opacity-75">@deliveryLabel</div> 227 } 228 229 230 } 231 @if(!string.IsNullOrEmpty(ExpDelivery)){ 232 <div class="fs-6 fw-bold"><br>@Translate("Expected delivery day", "Forventes på lager den") @ExpDelivery</div> 233 } 234 235 236 237 </div> 238 239 240 </div> 241 242 } 243 else if (Pageview.IsVisualEditorMode) 244 { 245 <div class="alert alert-info">@Translate("No products available")</div> 246 } 247 } 248
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_22a35ceffe5842628a3e5af0c4ab14eb.Execute() in D:\dynamicweb.net\Solutions\Flex Media\danzafe.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_ProductAddToCart.cshtml:line 107 at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader) at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer) at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter) at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template) at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template) at Dynamicweb.Rendering.Template.RenderRazorTemplate()
1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 4 @* 5 @using Dynamicweb.Ecommerce.Stocks 6 @using Dynamicweb.Ecommerce.Products 7 @using Dynamicweb.Ecommerce 8 *@ 9 10 @using Dynamicweb.Core.Encoders 11 @using Dynamicweb.Ecommerce.CustomerExperienceCenter.Favorites 12 13 @using System.Linq 14 @using Dynamicweb.Core 15 @using Dynamicweb.Environment 16 17 18 19 @{ 20 bool isDetailPage = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("ProductID")); 21 ProductViewModel product = null; 22 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 23 { 24 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 25 } 26 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 27 { 28 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 29 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 30 31 if (productList?.Products is object) 32 { 33 product = productList.Products[0]; 34 } 35 } 36 37 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", ""); 38 bool anonymousUser = Pageview.User == null; 39 bool isErpConnectionDown = !Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsWebServiceConnectionAvailable"]); 40 bool hideAddToCart = anonymousUsersLimitations.Contains("cart") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHideAddToCart") && isErpConnectionDown; 41 hideAddToCart = Pageview.IsVisualEditorMode ? false : hideAddToCart; 42 } 43 44 45 46 @if (product is object && !hideAddToCart) { 47 string horizontalAlign = Model.Item.GetRawValueString("HorizontalAlignment", ""); 48 horizontalAlign = horizontalAlign == "center" ? "justify-content-center" : horizontalAlign; 49 horizontalAlign = horizontalAlign == "end" ? "justify-content-end" : horizontalAlign; 50 horizontalAlign = horizontalAlign == "full" ? "" : horizontalAlign; 51 52 bool favoritesSelector = !string.IsNullOrEmpty(Model.Item.GetString("ShowAddToFavorites")) ? Model.Item.GetBoolean("ShowAddToFavorites") : false; 53 bool quantitySelector = !string.IsNullOrEmpty(Model.Item.GetString("ShowQuantitySelector")) ? Model.Item.GetBoolean("ShowQuantitySelector") : false; 54 bool unitsSelector = !string.IsNullOrEmpty(Model.Item.GetString("ShowUnitsSelector")) ? Model.Item.GetBoolean("ShowUnitsSelector") : false; 55 bool hideInventory = !string.IsNullOrEmpty(Model.Item.GetString("HideInventory")) ? Model.Item.GetBoolean("HideInventory") : false; 56 bool hideStockState = !string.IsNullOrEmpty(Model.Item.GetString("HideStockState")) ? Model.Item.GetBoolean("HideStockState") : false; 57 58 string buttonSize = Model.Item.GetRawValueString("ButtonSize", "regular"); 59 string inputSize = string.Empty; 60 61 Dictionary<string, FieldValueViewModel> fields = product.ProductFields; 62 63 64 switch (buttonSize) 65 { 66 case "small": 67 inputSize = " input-group-sm"; 68 buttonSize = " btn-sm"; 69 break; 70 case "regular": 71 buttonSize = string.Empty; 72 break; 73 case "large": 74 inputSize = " input-group-lg"; 75 buttonSize = " btn-lg"; 76 break; 77 } 78 79 80 81 string iconPath = "/Files/icons/"; 82 string url = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("CartService")); 83 if (!url.Contains("LayoutTemplate")) 84 { 85 url += url.Contains("?") ? "&LayoutTemplate=Swift_MiniCart.cshtml" : "?LayoutTemplate=Swift_MiniCart.cshtml"; 86 } 87 88 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]); 89 string disableAddToCart = (product.StockLevel <= 0) ? "disabled" : ""; 90 bool isNeverOutOfStock = product.NeverOutOfstock; 91 disableAddToCart = isNeverOutOfStock && !isLazyLoadingForProductInfoEnabled ? "" : disableAddToCart; 92 93 string whenVariantsExist = Model.Item.GetRawValueString("WhenVariantsExist", "hide"); 94 95 string flexFill = Model.Item.GetRawValueString("HorizontalAlignment", "") == "full" ? "flex-fill" : ""; 96 string fullWidth = Model.Item.GetRawValueString("HorizontalAlignment", "") == "full" ? "w-100" : ""; 97 string addToCartIcon = Model.Item.GetRawValueString("Icon", iconPath + "shopping-cart.svg"); 98 string addToCartLabel = !addToCartIcon.Contains("_none") ? $"<span class=\"icon-2\">{ReadFile(addToCartIcon)}</span>" : ""; 99 addToCartLabel += !addToCartIcon.Contains("_none") && !Model.Item.GetBoolean("HideButtonText") ? " " : ""; 100 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>" : ""; 101 102 103 string Surface = ""; 104 string hasLockKeyCode = ""; 105 int CylinderCount = 1; 106 bool KeyCutField = false; 107 foreach (var field in fields) { 108 if (field.Value.SystemName == "HasLockKeyCode"){ 109 hasLockKeyCode = field.Value?.Value != null ? field.Value.Value.ToString() : ""; 110 } 111 if (field.Value.SystemName == "CylinderCount"){ 112 CylinderCount = Convert.ToInt32(field.Value?.Value != null ? field.Value.Value : 1); 113 } 114 if (field.Value.SystemName == "KeyCutField"){ 115 string KeyCutField_string = field.Value?.Value != null ? field.Value.Value.ToString() : ""; 116 if(KeyCutField_string=="True"){KeyCutField = true;} 117 } 118 if (field.Value.SystemName == "Surface"){ 119 Surface = field.Value?.Value != null ? field.Value.Value.ToString() : ""; 120 } 121 } 122 if(CylinderCount<1){CylinderCount=1;} 123 <span style="display:none" id="cylindercountvalue" value="@CylinderCount"></span> 124 125 if ((product.VariantInfo.VariantInfo == null || whenVariantsExist == "disable") && isDetailPage) { 126 string unitId = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("UnitId")) ? Dynamicweb.Context.Current.Request.Form.Get("UnitId") : product.DefaultUnitId; 127 if (string.IsNullOrEmpty(unitId) && product?.UnitOptions != null) { 128 if (product.UnitOptions.FirstOrDefault<UnitOptionViewModel>() != null) { 129 unitId = product.UnitOptions.FirstOrDefault<UnitOptionViewModel>().Id; 130 } 131 } 132 133 134 135 // Flex 136 137 138 // IEnumerable<StockLocation> stockLocations = Services.StockService.GetStockLocations(); 139 140 double StockWebshop = 0; 141 double StockSupplier = 0; 142 var stockUnits = Dynamicweb.Ecommerce.Services.StockService.GetStockUnits(product.Id, product.VariantId).ToList(); 143 if (stockUnits.Any()) 144 { 145 var webShopStockUnit = stockUnits.FirstOrDefault(su => su.StockLocationId == 1); 146 if (webShopStockUnit != null) 147 { 148 StockWebshop = webShopStockUnit.StockQuantity; 149 150 } 151 var supplierStockUnit = stockUnits.FirstOrDefault(su => su.StockLocationId == 2); 152 if (supplierStockUnit != null) 153 { 154 StockSupplier = supplierStockUnit.StockQuantity; 155 } 156 } 157 158 double? unitStockLevel = 0; 159 160 bool HasStock = false; 161 foreach (var unitOption in product.UnitOptions){ 162 if (unitOption.StockLevel > 0){ 163 unitStockLevel = unitOption.StockLevel; 164 HasStock = true; 165 } 166 } 167 168 if (StockWebshop > 0){ 169 HasStock = true; 170 } else if (StockSupplier > 0){ 171 HasStock = true; 172 } 173 174 175 disableAddToCart = HasStock && !isLazyLoadingForProductInfoEnabled ? "" : disableAddToCart; 176 177 string minQty = product.PurchaseMinimumQuantity != 1 ? $"min=\"{product.PurchaseMinimumQuantity.ToString()}\"" : "min=\"1\""; 178 string stepQty = product.PurchaseQuantityStep > 1 ? product.PurchaseQuantityStep.ToString() : "1"; 179 string valueQty = product.PurchaseMinimumQuantity > product.PurchaseQuantityStep ? product.PurchaseMinimumQuantity.ToString() : stepQty; 180 disableAddToCart = product.VariantInfo.VariantInfo != null && string.IsNullOrEmpty(product.VariantId) ? "disabled" : disableAddToCart; 181 disableAddToCart = product.Discontinued && !product.NeverOutOfstock && !HasStock ? "disabled" : disableAddToCart; 182 183 184 185 <small style="display: none;">CylinderCount:@CylinderCount</small> 186 187 <small style="display: none;">product.Id:@product.Id product.VariantId:@product.VariantId StockWebshop:@StockWebshop HasStock:@HasStock disableAddToCart:@disableAddToCart product.StockLevel:@product.StockLevel unitStockLevel:@unitStockLevel</small> 188 189 190 // <small>@product.StockLevel</small> 191 // <small>@HasStock . @product.Discontinued . @product.NeverOutOfstock = @disableAddToCart </small> 192 // <small>@product.VariantInfo.VariantInfo</small> 193 // <small>@isDetailPage</small> 194 195 bool needKey = false; 196 string KeyGroupID = ""; 197 string KeyList = ""; 198 string DefaultKey = ""; 199 string ReCodeList = ""; 200 string DefaultReCode = ""; 201 string KeyId = ""; 202 string KeyName = ""; 203 string KeyPrice = ""; 204 string keyCode = ""; 205 string KeyWay = ""; 206 string KeyWayType = ""; 207 bool hasKeyWayNew = false; 208 // bool hasKeyWayInCart = false; //.. 209 bool hasKeyInCart = false; 210 bool hasKeyWithCodeInCart = false; 211 212 213 214 foreach (var Groups in product.Groups){ 215 // <small>@Groups.Id</small> 216 if(string.IsNullOrEmpty(KeyGroupID)){ 217 string HasKeyId = Dynamicweb.Ecommerce.Services.ProductGroups.GetGroup(Groups.Id)?.ProductGroupFieldValues.FirstOrDefault(f => f.ProductGroupField.SystemName == "KeyProductIds")?.Value.ToString(); 218 string HasReCodeId = Dynamicweb.Ecommerce.Services.ProductGroups.GetGroup(Groups.Id)?.ProductGroupFieldValues.FirstOrDefault(f => f.ProductGroupField.SystemName == "ReCodeProductIds")?.Value.ToString(); 219 if(!string.IsNullOrEmpty(HasKeyId) || !string.IsNullOrEmpty(HasReCodeId)){ 220 KeyGroupID = Groups.Id; 221 } 222 } 223 } 224 225 226 if(!string.IsNullOrEmpty(KeyGroupID)){ 227 KeyList = Dynamicweb.Ecommerce.Services.ProductGroups.GetGroup(KeyGroupID)?.ProductGroupFieldValues.FirstOrDefault(f => f.ProductGroupField.SystemName == "KeyProductIds")?.Value.ToString(); 228 ReCodeList = Dynamicweb.Ecommerce.Services.ProductGroups.GetGroup(KeyGroupID)?.ProductGroupFieldValues.FirstOrDefault(f => f.ProductGroupField.SystemName == "ReCodeProductIds")?.Value.ToString(); 229 KeyWayType = Dynamicweb.Ecommerce.Services.ProductGroups.GetGroup(KeyGroupID)?.ProductGroupFieldValues.FirstOrDefault(f => f.ProductGroupField.SystemName == "KeyWayType")?.Value.ToString(); 230 } 231 List<string> KeyCodes = new List<string>(); 232 string[] Keys = KeyList.Split(','); 233 DefaultKey = Keys[0]; 234 List<string> ReCodes = new List<string> 235 (); 236 string[] ReCode = ReCodeList.Split(','); 237 DefaultReCode = ReCode[0]; 238 239 if((KeyList.Length>0 || ReCodeList.Length>0) && isDetailPage && !KeyCutField){needKey = true;} 240 241 // else{hasLockKeyCode="";} 242 243 244 // <small>bob @needKey</small> 245 // <small>@KeyList.Length @ReCodeList.Length @KeyWayType.Length</small> 246 // <small><strong>@DefaultKey</strong></small> 247 248 // finding keycode in cart 249 if(needKey || KeyCutField){ 250 if (Dynamicweb.Ecommerce.Common.Context.Cart != null) { 251 var CartList = Dynamicweb.Ecommerce.Common.Context.Cart.OrderLines; 252 foreach (var Orderline in CartList){ 253 bool matchKey = false; 254 if(Orderline.OrderLineFieldValues.Count>0){ 255 var KeyWayObj = Orderline.OrderLineFieldValues.FirstOrDefault(fv => fv.OrderLineFieldSystemName == "KeyWayType"); 256 if(!string.IsNullOrEmpty(KeyWayObj?.Value)){KeyWay = KeyWayObj.Value;}else{KeyWay="";} 257 var keyCodeObj = Orderline.OrderLineFieldValues.FirstOrDefault(fv => fv.OrderLineFieldSystemName == "LockKeyCode"); 258 if(!string.IsNullOrEmpty(keyCodeObj?.Value)){keyCode = keyCodeObj.Value;}else{keyCode="";} 259 260 if(KeyWayType==KeyWay){ 261 matchKey = true; 262 } 263 264 } 265 foreach (var Fields in Orderline.Product.OrderLineFields){ 266 if(Fields.SystemName=="LockKeyCode" && matchKey){ 267 bool isNew = true; 268 foreach(string K in KeyCodes){if(K==keyCode){isNew=false;}} 269 if(!string.IsNullOrEmpty(keyCode) && isNew){KeyCodes.Add(keyCode);} 270 if(string.IsNullOrEmpty(keyCode)){hasKeyWayNew=true;} 271 } 272 } 273 // finding keycode in cart - end 274 275 foreach (var Key in Keys){ 276 if(Key == Orderline.ProductId && matchKey){ 277 hasKeyInCart = true; 278 if(!string.IsNullOrEmpty(keyCode)){ 279 hasKeyWithCodeInCart = true; 280 } 281 } 282 } 283 284 285 286 287 // foreach (var Fields in Orderline.Product.OrderLineFields){ 288 // if(Fields.SystemName == "KeyWayType" && matchKey){ 289 // <small style="font-size:.6em;">@Orderline.Id @Fields.Name @KeyWayType @keyCode<br/></small> 290 // } 291 // } 292 293 } 294 } 295 } 296 297 298 299 if(needKey || KeyCutField){ // options from cart 300 <datalist id="knowenkeys"> 301 @if(hasKeyWayNew){<option value="">Leave blank to match one in cart</option>} 302 @foreach(string c in KeyCodes){<option value="@c">@c</option>} 303 </datalist> 304 } 305 306 307 if(needKey && 1==2){ // test 308 <div style="position:relative; z-index:10;"> 309 <div style="font-size:.7em;color:#aaa;"> 310 <small>KeyCutField: @KeyCutField<br /></small> 311 <small>hasLockKeyCode: @hasLockKeyCode<br /></small> 312 <small>CylinderCount: @CylinderCount<br /></small> 313 <small>need key: @needKey <br/></small> 314 <small>key in cart: @hasKeyInCart <br/></small> 315 <small>key w code in cart: @hasKeyWithCodeInCart</small> 316 </div> 317 </div> 318 } 319 320 321 322 323 if(needKey && 1==2){ // select options 324 <div class="mt-1 mb-1" style="position:relative; z-index:10;"> 325 @* <label>temp: for adding to cart if recode</label>*@ 326 <select style="font-size: 0.66em;"> 327 328 329 @foreach (var Key in Keys){ 330 var KeyProduct = Dynamicweb.Ecommerce.Services.Products.GetProductById(Key,"", product.LanguageId); 331 332 if(KeyProduct != null){ 333 KeyId = KeyProduct.Id.ToString(); 334 KeyName = KeyProduct.Name.ToString(); 335 KeyPrice = KeyProduct.Price.ToString(); 336 // something = KeyProduct.ProductFieldValues.GetProductFieldValue("Width").Value.ToString(); 337 } 338 <option value="@KeyId" @if(KeyId==DefaultKey){<text>selected="selected" </text>}>@KeyName @KeyPrice</option> 339 340 // <small><br/>@KeyId @KeyName @KeyPrice </small> 341 342 } 343 344 </select> 345 </div> 346 } 347 348 if(needKey && 1==2){ 349 if (hasLockKeyCode == "True" && whenVariantsExist != "disable"){ 350 <div class="js-stock-state text-center"> 351 <div class="small">@Translate("hasLockKeyCode_text")</div> 352 </div> 353 } 354 } 355 356 357 358 359 var reserveMode = Dynamicweb.Ecommerce.Frontend.Cart.ProductReserve.Mode; 360 361 if (unitsSelector && product.UnitOptions.Count > 0) { 362 <form f=a method="post" action="/Default.aspx?ID=@(Pageview.Page.ID)&ProductId=@product.Id" id="UnitSelectorForm_@(product.Id)_@(product.VariantId)_@Model.ID"> 363 <input type="hidden" name="redirect" value="false"> 364 <input type="hidden" name="VariantID" value="@product.VariantId"> 365 <input type="hidden" name="UnitID" class="js-unit-id" value="@unitId"> 366 </form> 367 } 368 369 <div class="d-flex @horizontalAlign @fullWidth js-input-group item_@Model.Item.SystemName.ToLower()"> 370 <form f=b method="post" action="@url" class="@fullWidth" style="z-index: 1"> 371 <input type="hidden" name="redirect" value="false"> 372 <input type="hidden" name="ProductCurrency" value="@Dynamicweb.Ecommerce.Common.Context.Currency.Code"> 373 <input type="hidden" name="ProductPrice" value="@PriceViewModelExtensions.ToStringInvariant(product.Price)"> 374 <input type="hidden" name="ProductReferer" value="component_ProductAddToCart"> 375 376 @if(!needKey){ 377 <input type="hidden" name="cartcmd" value="add"> 378 <input type="hidden" name="ProductId" value="@product.Id"> 379 <input type="hidden" name="ProductName" value="@HtmlEncoder.HtmlEncode(product.Name)"> 380 <input type="hidden" name="VariantID" value="@product.VariantId"> 381 <input type="hidden" name="ProductVariantName" value="@product.VariantName"> 382 <input id="@(product.Id)_StockLocationId" type="hidden" name="StockLocationId" value=""> 383 if (quantitySelector || (!anonymousUser && product.VariantInfo.VariantInfo != null) || (!anonymousUser && favoritesSelector)) 384 { 385 <input type="hidden" id="Unit_@(product.Id)_@product.VariantId" name="UnitID" value="@unitId" /> 386 } 387 <input type="hidden" name="EcomOrderLineFieldInput_Surface" value="@Surface" /> 388 389 } 390 @if(needKey){ 391 <input name="cartcmd" value="addmulti" type="hidden" /> 392 <input type="hidden" name="ProductLoopCounter1" value="1" /> 393 <input type="hidden" name="ProductId1" value="@product.Id" /> 394 <input type="hidden" name="ProductName1" value="@product.Name" /> 395 <input type="hidden" name="VariantID1" value="@product.VariantId"> 396 <input type="hidden" name="ProductVariantName1" value="@product.VariantName" /> 397 <input id="@(product.Id)_StockLocationId" type="hidden" name="StockLocationId1" value="" /> 398 399 if (!string.IsNullOrEmpty(KeyWayType)){ 400 <input type="hidden" name="EcomOrderLineFieldInput_KeyWayType1" value="@KeyWayType" /> 401 } 402 if (quantitySelector || (!anonymousUser && product.VariantInfo.VariantInfo != null) || (!anonymousUser && favoritesSelector)) 403 { 404 <input type="hidden" id="Unit_@(product.Id)_@product.VariantId" name="UnitID1" value="@unitId" /> 405 } 406 <input type="hidden" name="EcomOrderLineFieldInput_Surface1" value="@Surface" /> 407 <input type="hidden" name="EcomOrderLineFieldInput_QuantityPrItem1" value="@CylinderCount" /> 408 409 410 411 <input type="hidden" name="ProductLoopCounter2" value="2" /> 412 <input type="hidden" name="ProductId2" value="@DefaultReCode" /> 413 <input id="Quantity2" type="hidden" name="Quantity2" value="1" disabled="" /> 414 <input type="hidden" name="EcomOrderLineFieldInput_ForProductId2" value="@product.Id" /> 415 <input type="hidden" name="EcomOrderLineFieldInput_ForProductVariantId2" value="@product.VariantId" /> 416 <input type="hidden" name="EcomOrderLineFieldInput_KeyWayType2" value="@KeyWayType" /> 417 <input type="hidden" name="EcomOrderLineFieldInput_LockKeyCode2" id="EcomOrderLineFieldInput_LockKeyCode2" value="" /> 418 419 <input type="hidden" name="ProductLoopCounter3" value="3" /> 420 <input type="hidden" name="ProductId3" value="@DefaultKey" /> 421 <input id="Quantity3" type="hidden" name="Quantity3" value="1" disabled="" /> 422 <input type="hidden" name="EcomOrderLineFieldInput_IsCodeKey3" value="True" /> 423 <input type="hidden" name="EcomOrderLineFieldInput_ForProductId3" value="@product.Id" /> 424 <input type="hidden" name="EcomOrderLineFieldInput_ForProductVariantId3" value="@product.VariantId" /> 425 <input type="hidden" name="EcomOrderLineFieldInput_KeyWayType3" value="@KeyWayType" /> 426 <input type="hidden" name="EcomOrderLineFieldInput_LockKeyCode3" id="EcomOrderLineFieldInput_LockKeyCode3" value="" /> 427 428 } 429 430 431 432 433 @if (reserveMode == Dynamicweb.Ecommerce.Frontend.Cart.ProductReserveMode.AddToCart) 434 { 435 <input type="hidden" name="GetReservedAmount" value="true"> 436 } 437 438 @if (!string.IsNullOrEmpty(product.VariantId)) 439 { 440 // <input type="hidden" name="VariantId" value="@product.VariantId"> 441 } 442 443 444 445 446 447 448 @if (!product.NeverOutOfstock) 449 { 450 <input type="hidden" name="Stock" value="@unitStockLevel" u="@unitStockLevel" p="@product.StockLevel" sw="@StockWebshop" > 451 452 <template class="js-out-of-stock-notice"> 453 <div class="modal-header"> 454 <h1 class="modal-title fs-5">@Translate("Stock limit")</h1> 455 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 456 </div> 457 <div class="modal-body"> 458 @Translate("There are not enough products in stock. The product might be sold out or discontinued. Please adjust the quantity.") 459 </div> 460 </template> 461 } 462 463 464 465 466 467 468 469 470 @if (stepQty != "1") 471 { 472 <template class="js-step-quantity-warning"> 473 <div class="modal-header"> 474 <h1 class="modal-title fs-5">@Translate("The quantity is not valid")</h1> 475 </div> 476 <div class="modal-body"> 477 @Translate("Please select a quantity that is dividable by") @stepQty 478 </div> 479 </template> 480 } 481 @if (product.PurchaseMinimumQuantity != 1) 482 { 483 <template class="js-min-quantity-warning"> 484 <div class="modal-header"> 485 <h1 class="modal-title fs-5">@Translate("The product could not be added to the cart")</h1> 486 </div> 487 <div class="modal-body"> 488 @Translate("The quantity is not valid. You must buy at least") @product.PurchaseMinimumQuantity 489 </div> 490 </template> 491 } 492 493 494 <div class="d-flex flex-row w-100"> 495 @if (!quantitySelector) 496 { 497 if(!needKey){ 498 <input id="Quantity_@(product.Id)_@product.VariantId" class="swift_quantity_field" name="Quantity" value="@valueQty" type="hidden" @disableAddToCart> 499 } 500 if(needKey){ 501 <input id="Quantity_@(product.Id)_@product.VariantId" class="swift_quantity_field" name="Quantity1" value="@valueQty" type="hidden" @disableAddToCart> 502 } 503 } 504 505 @if (unitsSelector && product.UnitOptions.Count > 0) 506 { 507 string selectedUnitName = !string.IsNullOrEmpty(unitId) && product?.UnitOptions != null ? unitId : product.UnitOptions.FirstOrDefault<UnitOptionViewModel>().Name; 508 509 foreach (var unitOption in product.UnitOptions) 510 { 511 if (unitOption.Id == unitId) 512 { 513 selectedUnitName = unitOption.Name; 514 } 515 } 516 517 <div class="d-flex flex-column gap-2 w-100"> 518 <div class="input-group input-primary-button-group flex-nowrap@(inputSize)"> 519 @if (!anonymousUser && favoritesSelector) 520 { 521 @RenderPartial("Components/ToggleFavorite.cshtml", product) 522 } 523 524 @if (quantitySelector) 525 { 526 if(!needKey){ 527 <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> 528 } 529 if(needKey){ 530 <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> 531 } 532 } 533 534 <button class="btn btn-secondary @flexFill dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false"> 535 @selectedUnitName 536 </button> 537 538 <ul class="dropdown-menu swift_unit-field"> 539 @foreach (var unitOption in product.UnitOptions) 540 { 541 var selectedUnit = unitOption.Id == unitId ? "selected" : ""; 542 543 <li> 544 <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'); 545 document.querySelector('#Unit_@(product.Id)_@product.VariantId').value = this.getAttribute('data-value'); 546 swift.PageUpdater.Update(document.querySelector('#UnitSelectorForm_@(product.Id)_@(product.VariantId)_@Model.ID'))"> 547 <span>@unitOption.Name</span> 548 <span> 549 @if (unitOption.StockLevel > 0) 550 { 551 if (!Model.Item.GetBoolean("HideInventory")) 552 { 553 <span class="small text-success" u=a>@unitOption.StockLevel @Translate("In stock")</span> 554 } 555 else 556 { 557 <span class="small text-success" u=b>@Translate("In stock")</span> 558 } 559 } 560 else 561 { 562 <span class="small text-danger" u=c>@Translate("Out of Stock")</span> 563 } 564 </span> 565 </button> 566 </li> 567 } 568 </ul> 569 </div> 570 <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"> 571 @if (!Model.Item.GetBoolean("HideButtonText")) 572 { 573 <span class="text-nowrap d-flex align-items-center justify-content-center gap-2"> 574 @addToCartLabel 575 </span> 576 } 577 else 578 { 579 @addToCartLabel 580 } 581 </button> 582 </div> 583 } 584 else 585 { 586 if (!anonymousUser && favoritesSelector) 587 { 588 @RenderPartial("Components/ToggleFavorite.cshtml", product) 589 } 590 591 592 <div class="input-group input-primary-button-group flex-nowrap@(inputSize)"> 593 @if (quantitySelector) 594 { 595 if(!needKey){ 596 <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> 597 } 598 if(needKey){ 599 <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> 600 } 601 } 602 603 <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"> 604 @if (!Model.Item.GetBoolean("HideButtonText")) 605 { 606 <span class="text-nowrap d-flex align-items-center justify-content-center gap-2"> 607 @addToCartLabel 608 </span> 609 } 610 else 611 { 612 @addToCartLabel 613 } 614 </button> 615 </div> 616 } 617 </div> 618 619 620 621 @if (hasLockKeyCode == "True" && whenVariantsExist == "disable"){ 622 623 <p> </p> 624 <p><input id="LockKeyCode" name="EcomOrderLineFieldInput_ReCoding1" type="checkbox" value="true" onChange="checkboxElement(this,'KeyCodeSection');" /> <label for="LockKeyCode">@Translate("Ønsker den omkodet")</label></p> 625 626 <div id="KeyCodeSection" class="hide"> 627 @* <small style="font-size: .7em;color: #999;">@DefaultKey @DefaultReCode</small>*@ 628 629 <div class="mt-4 input-group"> 630 <label for="EcomOrderLineFieldInput_LockKeyCode1">@Translate("Ønsker du låsen/låsene kodet til eksisterende system") 631 <br /> 632 <input list="knowenkeys" style="max-width: 26rem;" class="form-control" type="text" name="EcomOrderLineFieldInput_LockKeyCode1" id="EcomOrderLineFieldInput_LockKeyCode1" onchange="matchCartOption(this)" onkeyup="matchCartOption(this)"> 633 </label> 634 635 </div> 636 637 </div> 638 639 <script> 640 641 // var CylinderCount = @CylinderCount; 642 var CylinderCount = document.getElementById("cylindercountvalue").getAttribute("value"); 643 // alert(document.getElementById("cylindercountvalue").getAttribute("value")) 644 645 function checkboxElement(a,b){ 646 c=document.getElementById(b).classList; 647 if(a.checked){c.remove("hide");}else{c.add("hide");} 648 wantReCode(); 649 } 650 651 function wantReCode(){ 652 a=document.getElementById("LockKeyCode").checked; 653 if(a){ 654 document.getElementById("Quantity2").disabled=false; 655 document.getElementById("EcomOrderLineFieldInput_LockKeyCode1").disabled=false; 656 657 }else{ 658 document.getElementById("Quantity2").disabled=true; 659 document.getElementById("EcomOrderLineFieldInput_LockKeyCode1").disabled=true; 660 } 661 ReCodeQuantity(); 662 matchCartOption(document.getElementById('EcomOrderLineFieldInput_LockKeyCode1')); 663 } 664 665 function matchCartOption(a){ 666 m=false;l=a.getAttribute("list");v=a.value; 667 b=document.getElementById(l).getElementsByTagName("option"); 668 c=document.getElementById("LockKeyCode").checked; 669 document.getElementById("EcomOrderLineFieldInput_LockKeyCode2").value=v; 670 document.getElementById("EcomOrderLineFieldInput_LockKeyCode3").value=v; 671 for(i=0;i<b.length;i++){if(b[i].value==v){m=true;}} 672 if(c && !m){ 673 // if wants recode and no match in cart 674 // enable "add key to cart" 675 document.getElementById("Quantity3").disabled=false; 676 }else{ 677 // disable "add key to cart" 678 document.getElementById("Quantity3").disabled=true; 679 } 680 ReCodeQuantity(); 681 } 682 683 function ReCodeQuantity(){ 684 var Qcount = document.getElementById("Quantity1").value * document.getElementById("cylindercountvalue").getAttribute("value"); 685 // alert(Qcount) 686 687 // document.getElementById("Quantity2").value=document.getElementById("Quantity1").value; 688 document.getElementById("Quantity2").value=Qcount; 689 690 } 691 692 document.addEventListener("DOMContentLoaded", function(){ 693 checkboxElement(document.getElementById('LockKeyCode'),'KeyCodeSection'); 694 }); 695 696 ReCodeQuantity(); 697 698 699 // checkboxElement(document.getElementById('LockKeyCode'),'KeyCodeSection'); 700 // wantReCode(); 701 // ReCodeQuantity(); 702 // matchCartOption(document.getElementById('EcomOrderLineFieldInput_LockKeyCode1')); 703 // alert("bob") 704 705 </script> 706 <style>.hide{display: none;}</style> 707 708 709 } 710 711 712 713 @if (KeyCutField && whenVariantsExist == "disable"){ 714 <div id="KeyCodeSection"> 715 716 <div class="mt-4 input-group"> 717 <label for="EcomOrderLineFieldInput_LockKeyCode">@Translate("Ønsker du nøglen/nøglernerne kodet til eksisterende system") 718 <br /> 719 <input list="knowenkeys" style="max-width: 26rem;" class="form-control" type="text" name="EcomOrderLineFieldInput_LockKeyCode" id="EcomOrderLineFieldInput_LockKeyCode"> 720 <input type="hidden" name="EcomOrderLineFieldInput_KeyWayType" value="@KeyWayType" /> 721 </label> 722 </div> 723 724 </div> 725 726 727 } 728 729 730 731 </form> 732 </div> 733 } else if (whenVariantsExist == "modal") { 734 string buttonText = Translate("Select"); 735 string variantId = !string.IsNullOrWhiteSpace(product.VariantId) ? product.VariantId : product.DefaultVariantId; 736 737 string variantSelectorServicePageId = !string.IsNullOrEmpty(Model.Item.GetString("VariantSelectorServicePageId")) ? Model.Item.GetLink("VariantSelectorServicePageId").PageId.ToString() : ""; 738 variantSelectorServicePageId = variantSelectorServicePageId != "" ? variantSelectorServicePageId : GetPageIdByNavigationTag("VariantSelectorService").ToString(); 739 740 // Flex 741 if (hasLockKeyCode == "True"){ 742 <div class="js-stock-state text-center"> 743 <div class="small">@Translate("hasLockKeyCode_text")</div> 744 </div> 745 } 746 747 748 <div class="d-flex @horizontalAlign w-100 item_@Model.Item.SystemName.ToLower()"> 749 750 751 752 @if (!anonymousUser && favoritesSelector) 753 { 754 @RenderPartial("Components/ToggleFavorite.cshtml", product) 755 } 756 <form f=c action="/Default.aspx?ID=@variantSelectorServicePageId" data-response-target-element="DynamicModalContent" data-preloader="inline" style="z-index: 1" class="@fullWidth"> 757 <input type="hidden" name="ProductID" value="@product.Id"> 758 <input type="hidden" name="VariantID" value="@variantId"> 759 <input type="hidden" name="QuantitySelector" value="@quantitySelector.ToString()"> 760 <input type="hidden" name="HideInventory" value="@hideInventory.ToString()"> 761 <input type="hidden" name="HideStockState" value="@hideStockState.ToString()"> 762 <input type="hidden" name="VariantSelectorServicePage" value="@variantSelectorServicePageId"> 763 <input type="hidden" name="ViewType" value="ModalContent"> 764 @if (isLazyLoadingForProductInfoEnabled) 765 { 766 @* If lazy loading is enabled, bypass it because we're loading a modal window, so render everything as if it was server-side *@ 767 <input type="hidden" name="getproductinfo" value="true"> 768 } 769 770 @{ 771 string link = product.GetProductLink(GetPageIdByNavigationTag("Shop"), false); 772 string googleTagManagerID = Pageview.AreaSettings.GetString("GoogleTagManagerID"); 773 string googleAnalyticsMeasurementID = Pageview.AreaSettings.GetString("GoogleAnalyticsMeasurementID"); 774 bool allowTracking = true; 775 if (CookieManager.IsCookieManagementActive) 776 { 777 var cookieOptInLevel = CookieManager.GetCookieOptInLevel(); 778 allowTracking = cookieOptInLevel == CookieOptInLevel.All || (cookieOptInLevel == CookieOptInLevel.Functional && CookieManager.GetCookieOptInCategories().Contains("Statistical")); 779 } 780 allowTracking = true; 781 782 string clickProductLink = string.Empty; 783 if ((!string.IsNullOrWhiteSpace(googleAnalyticsMeasurementID) || !string.IsNullOrWhiteSpace(googleTagManagerID)) && allowTracking) 784 { 785 clickProductLink = "onclick=\"return clickProductLink('" + @product.Id + "', '" + @product.Name + "', '" + @product.VariantName + "', '" + @product.Price.CurrencyCode + "', '" + @product.Price.Price + "')\""; 786 } 787 788 789 790 791 } 792 793 <a type="button" href="@(link)" class="btn btn-primary@(buttonSize) @fullWidth" title="@Translate("Select")" id="OpenVariantSelectorModal@(product.Id)_@Pageview.CurrentParagraph.ID">@buttonText</a> 794 795 796 @*<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> 797 *@ 798 799 800 801 802 </form> 803 </div> 804 } 805 } else if (Pageview.IsVisualEditorMode) { 806 <div class="alert alert-dark m-0">@Translate("No products available")</div> 807 } 808
Fri fragt over 799,- Dag til dag levering på lagervarer Over 120.000 tilfredse kunder |
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_1915370b24494abf928b628efbbc267f.Execute() in D:\dynamicweb.net\Solutions\Flex Media\danzafe.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_ProductSpecification.cshtml:line 28 at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader) at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer) at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter) at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template) at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template) at Dynamicweb.Rendering.Template.RenderRazorTemplate()
1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 4 @{ 5 ProductViewModel product = null; 6 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 7 { 8 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 9 } 10 else if (Pageview.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 @helper RenderFieldsFromList(Dictionary<string, FieldValueViewModel> fields, string layout) 238 { 239 string size = Model.Item.GetRawValueString("Size", "full"); 240 string gaps = size != "full" ? " gap-1" : string.Empty; 241 bool hideFieldLabels = Model.Item.GetBoolean("HideFieldLabels"); 242 bool hideFieldsWithZeroValue = Model.Item.GetBoolean("HideFieldsWithZeroValue"); 243 244 if (layout == "columns") { 245 <div class="g-col-12"> 246 <div class="grid@(gaps)"> 247 @foreach (var field in fields) 248 { 249 {@RenderField(field.Value, layout)} 250 } 251 </div> 252 </div> 253 } 254 if (layout == "list") { 255 <div class="g-col-12"> 256 <dl class="grid@(gaps)"> 257 @foreach (var field in fields) 258 { 259 {@RenderField(field.Value, layout)} 260 } 261 </dl> 262 </div> 263 } 264 if (layout == "table") 265 { 266 string tableSize = size == "full" ? "" : " table-sm"; 267 <div class="g-col-12"> 268 <table class="table table-striped@(tableSize)"> 269 @foreach (var field in fields) 270 { 271 {@RenderField(field.Value, layout)} 272 } 273 </table> 274 </div> 275 } 276 if (layout == "bullets") 277 { 278 string listSize = size == "full" ? "" : "m-0 p-0 lh-1 fs-7 opacity-75"; 279 string listStyle = size == "full" ? "" : "style=\"list-style-position: inside\""; 280 <div class="g-col-12"> 281 <ul class="@listSize" @listStyle> 282 @foreach (var field in fields) 283 { 284 {@RenderField(field.Value, layout)} 285 } 286 </ul> 287 </div> 288 } 289 if (layout == "commas") 290 { 291 List<string> featuresList = new List<string>(); 292 293 foreach (var field in fields) 294 { 295 string firstListItemValue = string.Empty; //Hack to support field type providers with a single value 296 297 if (field.Value?.Value != null) 298 { 299 if (field.Value.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 300 { 301 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 302 303 //Hack to support field type providers with a single value 304 if (values.FirstOrDefault() != null) 305 { 306 firstListItemValue = values.FirstOrDefault().Value; 307 } 308 } 309 } 310 311 if (!hideFieldsWithZeroValue || (firstListItemValue != "0" && firstListItemValue != "0.0" && field.Value.Value.ToString() != "0" && field.Value.Value.ToString() != "0.0")) 312 { 313 if (field.Value.Value is object && !string.IsNullOrEmpty(field.Value.Value.ToString())) 314 { 315 if (field.Value.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 316 { 317 List<string> options = new List<string>(); 318 foreach (FieldOptionValueViewModel option in field.Value.Value as System.Collections.Generic.List<FieldOptionValueViewModel>) 319 { 320 if (!string.IsNullOrWhiteSpace(option.Value)) 321 { 322 if (option.Value.ToString().Contains("#") && (Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour"))) 323 { 324 string colorSpan = "<span class=\"colorbox-sm\" style=\"background-color: " + option.Value + "\"></span>"; 325 options.Add(colorSpan); 326 } 327 else if (!string.IsNullOrEmpty(option.Value)) 328 { 329 options.Add(option.Name); 330 } 331 } 332 } 333 string optionsString = (string.Join(", ", options.Select(x => x.ToString()).ToArray())); 334 if ((Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour"))) 335 { 336 optionsString = (string.Join(" ", options.Select(x => x.ToString()).ToArray())); 337 } 338 339 if (!string.IsNullOrEmpty(optionsString)) 340 { 341 if (!hideFieldLabels) 342 { 343 featuresList.Add(field.Value.Name + ": " + optionsString); 344 } 345 else 346 { 347 featuresList.Add(optionsString); 348 } 349 } 350 } 351 else 352 { 353 if (!string.IsNullOrWhiteSpace(field.Value.Value.ToString())) 354 { 355 if (field.Value.Value.ToString().Contains("#") && (Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour"))) 356 { 357 string colorSpan = "<span class=\"colorbox-sm\" style=\"background-color: " + field.Value.Value + "\"></span>"; 358 359 if (!hideFieldLabels) 360 { 361 featuresList.Add(field.Value.Name + ": " + colorSpan); 362 } 363 else 364 { 365 featuresList.Add(colorSpan); 366 } 367 } 368 else 369 { 370 if (!hideFieldLabels) 371 { 372 featuresList.Add(field.Value.Name + ": " + field.Value.Value.ToString()); 373 } 374 else 375 { 376 featuresList.Add(field.Value.Value.ToString()); 377 } 378 } 379 } 380 } 381 } 382 } 383 } 384 385 string featuresString = (string.Join(", ", featuresList.Select(x => x.ToString()).ToArray())); 386 387 <div class="g-col-12 opacity-75 fs-7">@featuresString</div> 388 } 389 } 390 391 @helper RenderField(FieldValueViewModel field, string layout) 392 { 393 string size = Model.Item.GetRawValueString("Size", "full"); 394 string fieldValue = field?.Value != null ? field.Value.ToString() : ""; 395 bool hideFieldLabels = Model.Item.GetBoolean("HideFieldLabels"); 396 bool noValues = false; 397 string firstListItemValue = string.Empty; //Hack to support field type providers with a single value 398 bool hideFieldsWithZeroValue = Model.Item.GetBoolean("HideFieldsWithZeroValue"); 399 400 if (!string.IsNullOrEmpty(fieldValue)) 401 { 402 if (field.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 403 { 404 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 405 noValues = values.Count > 0 ? false : true; 406 407 //Hack to support field type providers with a single value 408 if (values.FirstOrDefault() != null) 409 { 410 firstListItemValue = values.FirstOrDefault().Value; 411 } 412 } 413 } 414 415 if (!string.IsNullOrEmpty(fieldValue) && noValues == false) 416 { 417 if (!hideFieldsWithZeroValue || (firstListItemValue != "0" && firstListItemValue != "0.0" && field.Value.ToString() != "0" && field.Value.ToString() != "0.0")) 418 { 419 if (layout == "columns") 420 { 421 422 <div class="grid g-col-6 g-col-lg-4 gap-1"> 423 @if (!hideFieldLabels) 424 { 425 <dt class="g-col-12 g-col-lg-4" t=a>@Translate(field.Name)</dt> 426 } 427 <dd class="g-col-12 g-col-lg-8 mb-0 text-break"> 428 @{ @RenderFieldValue(field) } 429 </dd> 430 </div> 431 } 432 if (layout == "list") 433 { 434 if (!hideFieldLabels) 435 { 436 <dt class="g-col-4" t=b>@Translate(field.Name)</dt> 437 } 438 <dd class="g-col-8 mb-0 text-break"> 439 @{ @RenderFieldValue(field) } 440 </dd> 441 } 442 if (layout == "table") 443 { 444 <tr> 445 @if (!hideFieldLabels) 446 { 447 <th class="w-25 w-lg-50" scope="row">@Translate(field.Name)</th> 448 } 449 <td class="text-break"> 450 @{ @RenderFieldValue(field) } 451 </td> 452 </tr> 453 } 454 if (layout == "bullets") 455 { 456 <li> 457 @if (!hideFieldLabels) 458 { 459 <strong>@Translate(field.Name)</strong> 460 } 461 <span> 462 @{ @RenderFieldValue(field) } 463 </span> 464 </li> 465 } 466 } 467 } 468 } 469 470 @helper RenderFieldValue(FieldValueViewModel field) 471 { 472 string fieldValue = field?.Value != null ? field.Value.ToString() : ""; 473 474 bool isLink = field?.Type == "Link"; 475 bool isColor = false; 476 bool isBrandName = field?.SystemName == "Brand_name"; 477 478 fieldValue = fieldValue == "False" ? Translate("No") : fieldValue; 479 fieldValue = fieldValue == "True" ? Translate("Yes") : fieldValue; 480 481 482 if (field.Value.GetType() == typeof(System.Collections.Generic.List<Dynamicweb.Ecommerce.ProductCatalog.FieldOptionValueViewModel>)) 483 { 484 int valueCount = 0; 485 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 486 int totalValues = values.Count; 487 488 foreach (FieldOptionValueViewModel option in values) 489 { 490 if (!string.IsNullOrEmpty(option.Value)) 491 { 492 if (option.Value.Substring(0, 1) == "#") 493 { 494 isColor = true; 495 } 496 } 497 498 if (!isColor) 499 { 500 @option.Name 501 } 502 else 503 { 504 <span class="colorbox-sm" style="background-color: @option.Value" title="@option.Name"></span> 505 } 506 507 if (valueCount != totalValues && valueCount < (totalValues - 1)) 508 { 509 if (isColor) 510 { 511 <text> </text> 512 } 513 else 514 { 515 <text>, </text> 516 } 517 } 518 valueCount++; 519 } 520 } 521 else 522 { 523 if (fieldValue.Substring(0, 1) == "#") 524 { 525 isColor = true; 526 } 527 528 if (!isColor) 529 { 530 if (isLink) 531 { 532 string linktTitle = !fieldValue.Contains("aspx") ? fieldValue : Translate("Go to link"); 533 string target = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") && fieldValue.Contains("http") ? "target=\"_blank\"" : string.Empty; 534 string rel = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") && fieldValue.Contains("http") ? "rel=\"noopener\"" : string.Empty; 535 536 <a href="@field.Value" title="@field.Name" @target @rel>@linktTitle</a> 537 } 538 else if (isBrandName) 539 { 540 <span itemprop="brand" itemtype="https://schema.org/Brand" itemscope> 541 <span itemprop="name">@fieldValue</span> 542 </span> 543 } 544 else 545 { 546 @fieldValue 547 } 548 549 } 550 else 551 { 552 <span class="colorbox-sm" style="background-color: @fieldValue" title="@fieldValue"></span> 553 } 554 } 555 } 556
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_1915370b24494abf928b628efbbc267f.Execute() in D:\dynamicweb.net\Solutions\Flex Media\danzafe.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_ProductSpecification.cshtml:line 28 at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader) at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer) at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter) at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template) at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template) at Dynamicweb.Rendering.Template.RenderRazorTemplate()
1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 4 @{ 5 ProductViewModel product = null; 6 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 7 { 8 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 9 } 10 else if (Pageview.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 @helper RenderFieldsFromList(Dictionary<string, FieldValueViewModel> fields, string layout) 238 { 239 string size = Model.Item.GetRawValueString("Size", "full"); 240 string gaps = size != "full" ? " gap-1" : string.Empty; 241 bool hideFieldLabels = Model.Item.GetBoolean("HideFieldLabels"); 242 bool hideFieldsWithZeroValue = Model.Item.GetBoolean("HideFieldsWithZeroValue"); 243 244 if (layout == "columns") { 245 <div class="g-col-12"> 246 <div class="grid@(gaps)"> 247 @foreach (var field in fields) 248 { 249 {@RenderField(field.Value, layout)} 250 } 251 </div> 252 </div> 253 } 254 if (layout == "list") { 255 <div class="g-col-12"> 256 <dl class="grid@(gaps)"> 257 @foreach (var field in fields) 258 { 259 {@RenderField(field.Value, layout)} 260 } 261 </dl> 262 </div> 263 } 264 if (layout == "table") 265 { 266 string tableSize = size == "full" ? "" : " table-sm"; 267 <div class="g-col-12"> 268 <table class="table table-striped@(tableSize)"> 269 @foreach (var field in fields) 270 { 271 {@RenderField(field.Value, layout)} 272 } 273 </table> 274 </div> 275 } 276 if (layout == "bullets") 277 { 278 string listSize = size == "full" ? "" : "m-0 p-0 lh-1 fs-7 opacity-75"; 279 string listStyle = size == "full" ? "" : "style=\"list-style-position: inside\""; 280 <div class="g-col-12"> 281 <ul class="@listSize" @listStyle> 282 @foreach (var field in fields) 283 { 284 {@RenderField(field.Value, layout)} 285 } 286 </ul> 287 </div> 288 } 289 if (layout == "commas") 290 { 291 List<string> featuresList = new List<string>(); 292 293 foreach (var field in fields) 294 { 295 string firstListItemValue = string.Empty; //Hack to support field type providers with a single value 296 297 if (field.Value?.Value != null) 298 { 299 if (field.Value.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 300 { 301 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 302 303 //Hack to support field type providers with a single value 304 if (values.FirstOrDefault() != null) 305 { 306 firstListItemValue = values.FirstOrDefault().Value; 307 } 308 } 309 } 310 311 if (!hideFieldsWithZeroValue || (firstListItemValue != "0" && firstListItemValue != "0.0" && field.Value.Value.ToString() != "0" && field.Value.Value.ToString() != "0.0")) 312 { 313 if (field.Value.Value is object && !string.IsNullOrEmpty(field.Value.Value.ToString())) 314 { 315 if (field.Value.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 316 { 317 List<string> options = new List<string>(); 318 foreach (FieldOptionValueViewModel option in field.Value.Value as System.Collections.Generic.List<FieldOptionValueViewModel>) 319 { 320 if (!string.IsNullOrWhiteSpace(option.Value)) 321 { 322 if (option.Value.ToString().Contains("#") && (Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour"))) 323 { 324 string colorSpan = "<span class=\"colorbox-sm\" style=\"background-color: " + option.Value + "\"></span>"; 325 options.Add(colorSpan); 326 } 327 else if (!string.IsNullOrEmpty(option.Value)) 328 { 329 options.Add(option.Name); 330 } 331 } 332 } 333 string optionsString = (string.Join(", ", options.Select(x => x.ToString()).ToArray())); 334 if ((Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour"))) 335 { 336 optionsString = (string.Join(" ", options.Select(x => x.ToString()).ToArray())); 337 } 338 339 if (!string.IsNullOrEmpty(optionsString)) 340 { 341 if (!hideFieldLabels) 342 { 343 featuresList.Add(field.Value.Name + ": " + optionsString); 344 } 345 else 346 { 347 featuresList.Add(optionsString); 348 } 349 } 350 } 351 else 352 { 353 if (!string.IsNullOrWhiteSpace(field.Value.Value.ToString())) 354 { 355 if (field.Value.Value.ToString().Contains("#") && (Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour"))) 356 { 357 string colorSpan = "<span class=\"colorbox-sm\" style=\"background-color: " + field.Value.Value + "\"></span>"; 358 359 if (!hideFieldLabels) 360 { 361 featuresList.Add(field.Value.Name + ": " + colorSpan); 362 } 363 else 364 { 365 featuresList.Add(colorSpan); 366 } 367 } 368 else 369 { 370 if (!hideFieldLabels) 371 { 372 featuresList.Add(field.Value.Name + ": " + field.Value.Value.ToString()); 373 } 374 else 375 { 376 featuresList.Add(field.Value.Value.ToString()); 377 } 378 } 379 } 380 } 381 } 382 } 383 } 384 385 string featuresString = (string.Join(", ", featuresList.Select(x => x.ToString()).ToArray())); 386 387 <div class="g-col-12 opacity-75 fs-7">@featuresString</div> 388 } 389 } 390 391 @helper RenderField(FieldValueViewModel field, string layout) 392 { 393 string size = Model.Item.GetRawValueString("Size", "full"); 394 string fieldValue = field?.Value != null ? field.Value.ToString() : ""; 395 bool hideFieldLabels = Model.Item.GetBoolean("HideFieldLabels"); 396 bool noValues = false; 397 string firstListItemValue = string.Empty; //Hack to support field type providers with a single value 398 bool hideFieldsWithZeroValue = Model.Item.GetBoolean("HideFieldsWithZeroValue"); 399 400 if (!string.IsNullOrEmpty(fieldValue)) 401 { 402 if (field.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 403 { 404 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 405 noValues = values.Count > 0 ? false : true; 406 407 //Hack to support field type providers with a single value 408 if (values.FirstOrDefault() != null) 409 { 410 firstListItemValue = values.FirstOrDefault().Value; 411 } 412 } 413 } 414 415 if (!string.IsNullOrEmpty(fieldValue) && noValues == false) 416 { 417 if (!hideFieldsWithZeroValue || (firstListItemValue != "0" && firstListItemValue != "0.0" && field.Value.ToString() != "0" && field.Value.ToString() != "0.0")) 418 { 419 if (layout == "columns") 420 { 421 422 <div class="grid g-col-6 g-col-lg-4 gap-1"> 423 @if (!hideFieldLabels) 424 { 425 <dt class="g-col-12 g-col-lg-4" t=a>@Translate(field.Name)</dt> 426 } 427 <dd class="g-col-12 g-col-lg-8 mb-0 text-break"> 428 @{ @RenderFieldValue(field) } 429 </dd> 430 </div> 431 } 432 if (layout == "list") 433 { 434 if (!hideFieldLabels) 435 { 436 <dt class="g-col-4" t=b>@Translate(field.Name)</dt> 437 } 438 <dd class="g-col-8 mb-0 text-break"> 439 @{ @RenderFieldValue(field) } 440 </dd> 441 } 442 if (layout == "table") 443 { 444 <tr> 445 @if (!hideFieldLabels) 446 { 447 <th class="w-25 w-lg-50" scope="row">@Translate(field.Name)</th> 448 } 449 <td class="text-break"> 450 @{ @RenderFieldValue(field) } 451 </td> 452 </tr> 453 } 454 if (layout == "bullets") 455 { 456 <li> 457 @if (!hideFieldLabels) 458 { 459 <strong>@Translate(field.Name)</strong> 460 } 461 <span> 462 @{ @RenderFieldValue(field) } 463 </span> 464 </li> 465 } 466 } 467 } 468 } 469 470 @helper RenderFieldValue(FieldValueViewModel field) 471 { 472 string fieldValue = field?.Value != null ? field.Value.ToString() : ""; 473 474 bool isLink = field?.Type == "Link"; 475 bool isColor = false; 476 bool isBrandName = field?.SystemName == "Brand_name"; 477 478 fieldValue = fieldValue == "False" ? Translate("No") : fieldValue; 479 fieldValue = fieldValue == "True" ? Translate("Yes") : fieldValue; 480 481 482 if (field.Value.GetType() == typeof(System.Collections.Generic.List<Dynamicweb.Ecommerce.ProductCatalog.FieldOptionValueViewModel>)) 483 { 484 int valueCount = 0; 485 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 486 int totalValues = values.Count; 487 488 foreach (FieldOptionValueViewModel option in values) 489 { 490 if (!string.IsNullOrEmpty(option.Value)) 491 { 492 if (option.Value.Substring(0, 1) == "#") 493 { 494 isColor = true; 495 } 496 } 497 498 if (!isColor) 499 { 500 @option.Name 501 } 502 else 503 { 504 <span class="colorbox-sm" style="background-color: @option.Value" title="@option.Name"></span> 505 } 506 507 if (valueCount != totalValues && valueCount < (totalValues - 1)) 508 { 509 if (isColor) 510 { 511 <text> </text> 512 } 513 else 514 { 515 <text>, </text> 516 } 517 } 518 valueCount++; 519 } 520 } 521 else 522 { 523 if (fieldValue.Substring(0, 1) == "#") 524 { 525 isColor = true; 526 } 527 528 if (!isColor) 529 { 530 if (isLink) 531 { 532 string linktTitle = !fieldValue.Contains("aspx") ? fieldValue : Translate("Go to link"); 533 string target = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") && fieldValue.Contains("http") ? "target=\"_blank\"" : string.Empty; 534 string rel = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") && fieldValue.Contains("http") ? "rel=\"noopener\"" : string.Empty; 535 536 <a href="@field.Value" title="@field.Name" @target @rel>@linktTitle</a> 537 } 538 else if (isBrandName) 539 { 540 <span itemprop="brand" itemtype="https://schema.org/Brand" itemscope> 541 <span itemprop="name">@fieldValue</span> 542 </span> 543 } 544 else 545 { 546 @fieldValue 547 } 548 549 } 550 else 551 { 552 <span class="colorbox-sm" style="background-color: @fieldValue" title="@fieldValue"></span> 553 } 554 } 555 } 556
Error executing template "Designs/Swift/Paragraph/Swift_ProductDetailsMediaTable.cshtml" System.ArgumentNullException: Value cannot be null. Parameter name: source at System.Linq.Enumerable.Where[TSource](IEnumerable`1 source, Func`2 predicate) at CompiledRazorTemplates.Dynamic.RazorEngine_457d921174194192b3cdcf0c568215d1.Execute() in D:\dynamicweb.net\Solutions\Flex Media\danzafe.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_ProductDetailsMediaTable.cshtml:line 71 at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader) at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer) at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter) at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template) at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template) at Dynamicweb.Rendering.Template.RenderRazorTemplate()
1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 @using Dynamicweb.Frontend 4 @using System.IO 5 6 @functions { 7 public ProductViewModel product { get; set; } = new ProductViewModel(); 8 public string[] supportedImageFormats { get; set; } 9 public string[] supportedVideoFormats { get; set; } 10 public string[] supportedDocumentFormats { get; set; } 11 public string[] allSupportedFormats { get; set; } 12 13 public class RatioSettings 14 { 15 public string Ratio { get; set; } 16 public string CssClass { get; set; } 17 public string CssVariable { get; set; } 18 public string Fill { get; set; } 19 } 20 21 public RatioSettings GetRatioSettings() 22 { 23 var ratioSettings = new RatioSettings(); 24 25 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 26 ratio = ratio != "0" ? ratio : ""; 27 string cssClass = ratio != "" && ratio != "fill" ? " ratio" : ""; 28 string cssVariable = ratio != "" && ratio != "fill" ? "--bs-aspect-ratio: " + ratio : ""; 29 30 ratioSettings.Ratio = ratio; 31 ratioSettings.CssClass = cssClass; 32 ratioSettings.CssVariable = cssVariable; 33 ratioSettings.Fill = ratio == "fill" ? " h-100" : ""; 34 35 return ratioSettings; 36 } 37 } 38 39 @{ 40 @* Get the product data *@ 41 ProductViewModel product = null; 42 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 43 { 44 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 45 } 46 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 47 { 48 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 49 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 50 51 if (productList?.Products is object) 52 { 53 product = productList.Products[0]; 54 } 55 } 56 } 57 58 @if (product is object) { 59 @* Supported formats *@ 60 supportedImageFormats = new string[] { ".jpg", ".jpeg", ".webp", ".png", ".gif", ".bmp", ".tiff" }; 61 supportedVideoFormats = new string[] { "youtu.be", "youtube", "vimeo", ".mp4", ".webm" }; 62 supportedDocumentFormats = new string[] { ".pdf", ".docx", ".xlsx", ".ppt", "pptx" }; 63 allSupportedFormats = supportedImageFormats.Concat(supportedVideoFormats).Concat(supportedDocumentFormats).ToArray(); 64 65 @* Collect the assets *@ 66 var selectedAssetCategories = Model.Item.GetRawValueString("ImageAssets").Split(',').ToList(); 67 bool includeImagePatternImages = Model.Item.GetBoolean("ImagePatternImages"); 68 69 @* Needed image data collection to support both DefaultImage, ImagePatterns and Image Assets *@ 70 string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : ""; 71 IEnumerable<MediaViewModel> assetsImages = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets); 72 assetsImages = assetsImages.OrderByDescending(x => x.Value.Equals(defaultImage)); 73 IEnumerable<MediaViewModel> assetsList = new MediaViewModel[]{}; 74 75 assetsList = assetsList.Union(assetsImages); 76 assetsList = includeImagePatternImages ? assetsList.Union(product.ImagePatternImages) : assetsList; 77 assetsList = includeImagePatternImages && assetsList.Count() == 0 ? assetsList.Append(product.DefaultImage) : assetsList; 78 79 bool defaultImageFallback = Model.Item.GetBoolean("DefaultImageFallback"); 80 bool showOnlyPrimaryImage = Model.Item.GetBoolean("ShowOnlyPrimaryImage"); 81 82 int totalAssets = 0; 83 if (showOnlyPrimaryImage == false) { 84 foreach (MediaViewModel asset in assetsList) { 85 var assetValue = asset.Value; 86 foreach (string format in allSupportedFormats) { 87 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 88 totalAssets++; 89 } 90 } 91 } 92 } 93 94 if((totalAssets == 0 && product.DefaultImage != null && selectedAssetCategories.Count() == 0) || (showOnlyPrimaryImage == true && product.DefaultImage != null)) 95 { 96 assetsList = new List<MediaViewModel>(){ product.DefaultImage }; 97 totalAssets = 1; 98 } 99 100 int videoNumber = 0; 101 102 @* Layout settings *@ 103 string spacing = Model.Item.GetRawValueString("Spacing", "p-0"); 104 spacing = spacing == "none" ? "p-0" : spacing; 105 spacing = spacing == "small" ? "p-3" : spacing; 106 spacing = spacing == "large" ? "p-5" : spacing; 107 108 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 109 110 bool hideThumbnails = Model.Item.GetBoolean("HideThumbnails"); 111 112 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 113 int modalVideoNumber = 0; 114 115 @* Get assets from selected categories or get all assets *@ 116 117 if (totalAssets != 0 && assetsList.Any()) 118 { 119 <div class="@spacing@(theme) item_@Model.Item.SystemName.ToLower()"> 120 @if (!string.IsNullOrEmpty(Model.Item.GetString("Title")) && !Model.Item.GetBoolean("HideTitle")) 121 { 122 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "h3"); 123 124 <h3 class="@titleFontSize mb-3"> 125 @Model.Item.GetString("Title") 126 </h3> 127 } 128 129 <div class="table-responsive"> 130 <table class="table table-hover align-middle mb-0" style="table-layout: fixed;"> 131 <thead> 132 <tr> 133 @if (!hideThumbnails) 134 { 135 <th style="width:60px"> </th> 136 } 137 <th>@Translate("Name")</th> 138 <th class="text-end d-none d-lg-table-cell">@Translate("Download")</th> 139 <th class="text-end" style="width:100px">@Translate("File type")</th> 140 </tr> 141 </thead> 142 <tbody class="border-top-0"> 143 @foreach (MediaViewModel asset in assetsList) 144 { 145 var assetValue = asset.Value; 146 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Value.Substring( asset.Value.LastIndexOf('/') + 1); 147 148 bool isVideo = false; 149 foreach (string format in supportedVideoFormats) 150 { //Videos 151 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 152 { 153 isVideo = true; 154 } 155 } 156 157 if (!isVideo) 158 { 159 string filePath = Dynamicweb.Context.Current.Server.MapPath(assetValue); 160 long fileSize = 0; 161 162 if (File.Exists(filePath)) { 163 fileSize = new System.IO.FileInfo(filePath) != null ? new System.IO.FileInfo(filePath).Length / 1024 : 0; 164 165 foreach (string format in allSupportedFormats) 166 { 167 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 168 { 169 <tr> 170 @if (!hideThumbnails) 171 { 172 @RenderAsset(asset) 173 } 174 <td> 175 <a href="@assetValue" class="text-decoration-none text-break" download="@assetName" title="@assetName"> 176 @assetName 177 </a> 178 </td> 179 <td class="text-end d-none d-lg-table-cell"> 180 <a href="@assetValue" class="text-decoration-none" download="@assetName" title="@assetName"> 181 @fileSize KB <div class="icon-2" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 182 </a> 183 </td> 184 <td class="text-end">@format</td> 185 </tr> 186 } 187 } 188 } 189 } 190 else 191 { 192 string videoType = asset.Value.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || asset.Value.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "Youtube" : ""; 193 videoType = asset.Value.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "Vimeo" : videoType; 194 195 <tr data-bs-toggle="modal" data-bs-target="#modal_@(Model.ID)_@videoNumber" style="cursor: pointer"> 196 @if (!hideThumbnails) 197 { 198 @RenderAsset(asset) 199 } 200 <td> 201 @assetName 202 </td> 203 <td class="d-none d-lg-table-cell"> </td> 204 <td align="right">@videoType</td> 205 </tr> 206 207 videoNumber++; 208 } 209 } 210 </tbody> 211 </table> 212 </div> 213 214 @foreach (MediaViewModel asset in assetsList) 215 { 216 var assetName = asset.Value.ToLower(); 217 218 foreach (string format in supportedVideoFormats) 219 { //Videos 220 if (assetName.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 221 { 222 <div class="modal fade js-video-modal" id="modal_@(Model.ID)_@modalVideoNumber" tabindex="-1" aria-labelledby="productDetailsTableModalTitle_@(Model.ID)_@modalVideoNumber" aria-hidden="true"> 223 <div class="modal-dialog modal-dialog-centered modal-xl"> 224 <div class="modal-content"> 225 <div class="modal-header visually-hidden"> 226 <h5 class="modal-title" id="productDetailsTableModalTitle_@(Model.ID)_@modalVideoNumber">@product.Title</h5> 227 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 228 </div> 229 <div class="modal-body p-2 p-lg-3 h-100"> 230 @{ @RenderVideoPlayer(asset) } 231 </div> 232 </div> 233 </div> 234 </div> 235 236 modalVideoNumber++; 237 } 238 } 239 } 240 </div> 241 } 242 else if (Pageview.IsVisualEditorMode) 243 { 244 <div class="h-100 @theme"> 245 <div class="alert alert-dark m-0"> 246 @Translate("No assets are available") 247 </div> 248 </div> 249 } 250 } 251 252 @helper RenderAsset(MediaViewModel asset) 253 { 254 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 255 string assetValue = asset.Value; 256 257 <td class="@(theme) px-0"> 258 @foreach (string format in supportedImageFormats) 259 { //Images 260 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 261 { 262 @RenderImage(asset) 263 } 264 } 265 @foreach (string format in supportedVideoFormats) 266 { //Videos 267 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 268 { 269 @RenderVideoScreendump(asset) 270 } 271 } 272 @foreach (string format in supportedDocumentFormats) 273 { //Documents 274 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 275 { 276 @RenderDocument(asset) 277 } 278 } 279 </td> 280 } 281 282 @helper RenderImage(MediaViewModel asset) 283 { 284 string productName = product.Name; 285 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 286 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 287 string imageLinkPath = imagePath; 288 imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; 289 290 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 291 292 RatioSettings ratioSettings = GetRatioSettings(); 293 294 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download> 295 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 296 <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle itemprop="image"> 297 </div> 298 </a> 299 } 300 301 @helper RenderVideoScreendump(MediaViewModel asset) 302 { 303 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 304 305 string videoScreendumpPath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : ""; 306 string videoId = videoScreendumpPath.Substring(videoScreendumpPath.LastIndexOf('/') + 1); 307 videoScreendumpPath = videoScreendumpPath.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || videoScreendumpPath.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "https://img.youtube.com/vi/" + videoId + "/mqdefault.jpg" : videoScreendumpPath; 308 309 string vimeoJsClass = videoScreendumpPath.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "js-vimeo-video-thumbnail" : ""; 310 videoScreendumpPath = videoScreendumpPath.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "" : videoScreendumpPath; 311 312 string productName = product.Name; 313 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 314 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 315 316 RatioSettings ratioSettings = GetRatioSettings(); 317 318 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> 319 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 320 <div class="icon-2 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 321 @if (videoScreendumpPath.IndexOf(".mp4", StringComparison.OrdinalIgnoreCase) < 0) { 322 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@productName" @assetTitle class="@vimeoJsClass mw-100 mh-100" data-video-id="@videoId" style="object-fit: cover;"> 323 } else { 324 string videoType = Path.GetExtension(asset.Value).ToLower(); 325 326 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 327 <source src="@asset.Value" type="video/@videoType.Replace(".", "")"> 328 </video> 329 } 330 </div> 331 </div> 332 } 333 334 @helper RenderDocument(MediaViewModel asset) 335 { 336 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 337 string productName = product.Name; 338 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 339 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 340 string imageLinkPath = imagePath; 341 imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; 342 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 343 344 RatioSettings ratioSettings = GetRatioSettings(); 345 346 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download alt="@productName"> 347 @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) { 348 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 349 <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle /> 350 </div> 351 } else { 352 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 353 <div class="icon-3 position-absolute" style="z-index: 1">@ReadFile(iconPath + "file-text.svg")</div> 354 </div> 355 } 356 </a> 357 } 358 359 @helper RenderVideoPlayer(MediaViewModel asset) 360 { 361 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Name; 362 string assetValue = asset.Value; 363 string videoId = asset.Value.Substring(asset.Value.LastIndexOf('/') + 1); 364 string type = assetValue.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "youtube" : ""; 365 type = assetValue.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "vimeo" : type; 366 type = assetValue.IndexOf(".mp4", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf(".webm", StringComparison.OrdinalIgnoreCase) >= 0 ? "selfhosted" : type; 367 368 <div class="h-100" itemscope itemtype="https://schema.org/VideoObject"> 369 <span class="visually-hidden" itemprop="name">@assetName</span> 370 <span class="visually-hidden" itemprop="contentUrl">@asset.Value</span> 371 <span class="visually-hidden" itemprop="thumbnailUrl">@asset.Value</span> 372 @if (type != "selfhosted") 373 { 374 <div id="player_@(Pageview.CurrentParagraph.ID)_@(videoId)" 375 class="plyr__video-embed" 376 data-plyr-provider="@(type)" 377 data-plyr-embed-id="@videoId" 378 style="--plyr-color-main: var(--swift-foreground-color); height: 100%"> 379 </div> 380 381 <script type="module" src="/Files/Templates/Designs/Swift/Assets/js/plyr.js"></script> 382 <script type="module"> 383 var player = new Plyr('#player_@(Pageview.CurrentParagraph.ID)_@(videoId)', { 384 type: 'video', 385 youtube: { 386 noCookie: true, 387 showinfo: 0 388 }, 389 fullscreen: { 390 enabled: true, 391 iosNative: true, 392 } 393 }); 394 395 document.querySelectorAll('.js-video-modal').forEach(function (modal) { 396 modal.addEventListener('hidden.bs.modal', function (event) { 397 player.media.pause(); 398 }) 399 }); 400 </script> 401 } 402 else 403 { 404 string videoType = Path.GetExtension(assetValue).ToLower(); 405 406 <video preload="auto" class="h-100 w-100" style="object-fit: cover;" controls> 407 <source src="@assetValue" type="video/@videoType.Replace(".", "")"> 408 </video> 409 } 410 </div> 411 } 412
Error executing template "Designs/Swift/Paragraph/Swift_ProductDetailsMediaTable.cshtml" System.ArgumentNullException: Value cannot be null. Parameter name: source at System.Linq.Enumerable.Where[TSource](IEnumerable`1 source, Func`2 predicate) at CompiledRazorTemplates.Dynamic.RazorEngine_457d921174194192b3cdcf0c568215d1.Execute() in D:\dynamicweb.net\Solutions\Flex Media\danzafe.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_ProductDetailsMediaTable.cshtml:line 71 at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader) at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer) at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter) at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template) at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template) at Dynamicweb.Rendering.Template.RenderRazorTemplate()
1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 @using Dynamicweb.Frontend 4 @using System.IO 5 6 @functions { 7 public ProductViewModel product { get; set; } = new ProductViewModel(); 8 public string[] supportedImageFormats { get; set; } 9 public string[] supportedVideoFormats { get; set; } 10 public string[] supportedDocumentFormats { get; set; } 11 public string[] allSupportedFormats { get; set; } 12 13 public class RatioSettings 14 { 15 public string Ratio { get; set; } 16 public string CssClass { get; set; } 17 public string CssVariable { get; set; } 18 public string Fill { get; set; } 19 } 20 21 public RatioSettings GetRatioSettings() 22 { 23 var ratioSettings = new RatioSettings(); 24 25 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 26 ratio = ratio != "0" ? ratio : ""; 27 string cssClass = ratio != "" && ratio != "fill" ? " ratio" : ""; 28 string cssVariable = ratio != "" && ratio != "fill" ? "--bs-aspect-ratio: " + ratio : ""; 29 30 ratioSettings.Ratio = ratio; 31 ratioSettings.CssClass = cssClass; 32 ratioSettings.CssVariable = cssVariable; 33 ratioSettings.Fill = ratio == "fill" ? " h-100" : ""; 34 35 return ratioSettings; 36 } 37 } 38 39 @{ 40 @* Get the product data *@ 41 ProductViewModel product = null; 42 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 43 { 44 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 45 } 46 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 47 { 48 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 49 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 50 51 if (productList?.Products is object) 52 { 53 product = productList.Products[0]; 54 } 55 } 56 } 57 58 @if (product is object) { 59 @* Supported formats *@ 60 supportedImageFormats = new string[] { ".jpg", ".jpeg", ".webp", ".png", ".gif", ".bmp", ".tiff" }; 61 supportedVideoFormats = new string[] { "youtu.be", "youtube", "vimeo", ".mp4", ".webm" }; 62 supportedDocumentFormats = new string[] { ".pdf", ".docx", ".xlsx", ".ppt", "pptx" }; 63 allSupportedFormats = supportedImageFormats.Concat(supportedVideoFormats).Concat(supportedDocumentFormats).ToArray(); 64 65 @* Collect the assets *@ 66 var selectedAssetCategories = Model.Item.GetRawValueString("ImageAssets").Split(',').ToList(); 67 bool includeImagePatternImages = Model.Item.GetBoolean("ImagePatternImages"); 68 69 @* Needed image data collection to support both DefaultImage, ImagePatterns and Image Assets *@ 70 string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : ""; 71 IEnumerable<MediaViewModel> assetsImages = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets); 72 assetsImages = assetsImages.OrderByDescending(x => x.Value.Equals(defaultImage)); 73 IEnumerable<MediaViewModel> assetsList = new MediaViewModel[]{}; 74 75 assetsList = assetsList.Union(assetsImages); 76 assetsList = includeImagePatternImages ? assetsList.Union(product.ImagePatternImages) : assetsList; 77 assetsList = includeImagePatternImages && assetsList.Count() == 0 ? assetsList.Append(product.DefaultImage) : assetsList; 78 79 bool defaultImageFallback = Model.Item.GetBoolean("DefaultImageFallback"); 80 bool showOnlyPrimaryImage = Model.Item.GetBoolean("ShowOnlyPrimaryImage"); 81 82 int totalAssets = 0; 83 if (showOnlyPrimaryImage == false) { 84 foreach (MediaViewModel asset in assetsList) { 85 var assetValue = asset.Value; 86 foreach (string format in allSupportedFormats) { 87 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 88 totalAssets++; 89 } 90 } 91 } 92 } 93 94 if((totalAssets == 0 && product.DefaultImage != null && selectedAssetCategories.Count() == 0) || (showOnlyPrimaryImage == true && product.DefaultImage != null)) 95 { 96 assetsList = new List<MediaViewModel>(){ product.DefaultImage }; 97 totalAssets = 1; 98 } 99 100 int videoNumber = 0; 101 102 @* Layout settings *@ 103 string spacing = Model.Item.GetRawValueString("Spacing", "p-0"); 104 spacing = spacing == "none" ? "p-0" : spacing; 105 spacing = spacing == "small" ? "p-3" : spacing; 106 spacing = spacing == "large" ? "p-5" : spacing; 107 108 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 109 110 bool hideThumbnails = Model.Item.GetBoolean("HideThumbnails"); 111 112 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 113 int modalVideoNumber = 0; 114 115 @* Get assets from selected categories or get all assets *@ 116 117 if (totalAssets != 0 && assetsList.Any()) 118 { 119 <div class="@spacing@(theme) item_@Model.Item.SystemName.ToLower()"> 120 @if (!string.IsNullOrEmpty(Model.Item.GetString("Title")) && !Model.Item.GetBoolean("HideTitle")) 121 { 122 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "h3"); 123 124 <h3 class="@titleFontSize mb-3"> 125 @Model.Item.GetString("Title") 126 </h3> 127 } 128 129 <div class="table-responsive"> 130 <table class="table table-hover align-middle mb-0" style="table-layout: fixed;"> 131 <thead> 132 <tr> 133 @if (!hideThumbnails) 134 { 135 <th style="width:60px"> </th> 136 } 137 <th>@Translate("Name")</th> 138 <th class="text-end d-none d-lg-table-cell">@Translate("Download")</th> 139 <th class="text-end" style="width:100px">@Translate("File type")</th> 140 </tr> 141 </thead> 142 <tbody class="border-top-0"> 143 @foreach (MediaViewModel asset in assetsList) 144 { 145 var assetValue = asset.Value; 146 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Value.Substring( asset.Value.LastIndexOf('/') + 1); 147 148 bool isVideo = false; 149 foreach (string format in supportedVideoFormats) 150 { //Videos 151 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 152 { 153 isVideo = true; 154 } 155 } 156 157 if (!isVideo) 158 { 159 string filePath = Dynamicweb.Context.Current.Server.MapPath(assetValue); 160 long fileSize = 0; 161 162 if (File.Exists(filePath)) { 163 fileSize = new System.IO.FileInfo(filePath) != null ? new System.IO.FileInfo(filePath).Length / 1024 : 0; 164 165 foreach (string format in allSupportedFormats) 166 { 167 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 168 { 169 <tr> 170 @if (!hideThumbnails) 171 { 172 @RenderAsset(asset) 173 } 174 <td> 175 <a href="@assetValue" class="text-decoration-none text-break" download="@assetName" title="@assetName"> 176 @assetName 177 </a> 178 </td> 179 <td class="text-end d-none d-lg-table-cell"> 180 <a href="@assetValue" class="text-decoration-none" download="@assetName" title="@assetName"> 181 @fileSize KB <div class="icon-2" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 182 </a> 183 </td> 184 <td class="text-end">@format</td> 185 </tr> 186 } 187 } 188 } 189 } 190 else 191 { 192 string videoType = asset.Value.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || asset.Value.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "Youtube" : ""; 193 videoType = asset.Value.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "Vimeo" : videoType; 194 195 <tr data-bs-toggle="modal" data-bs-target="#modal_@(Model.ID)_@videoNumber" style="cursor: pointer"> 196 @if (!hideThumbnails) 197 { 198 @RenderAsset(asset) 199 } 200 <td> 201 @assetName 202 </td> 203 <td class="d-none d-lg-table-cell"> </td> 204 <td align="right">@videoType</td> 205 </tr> 206 207 videoNumber++; 208 } 209 } 210 </tbody> 211 </table> 212 </div> 213 214 @foreach (MediaViewModel asset in assetsList) 215 { 216 var assetName = asset.Value.ToLower(); 217 218 foreach (string format in supportedVideoFormats) 219 { //Videos 220 if (assetName.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 221 { 222 <div class="modal fade js-video-modal" id="modal_@(Model.ID)_@modalVideoNumber" tabindex="-1" aria-labelledby="productDetailsTableModalTitle_@(Model.ID)_@modalVideoNumber" aria-hidden="true"> 223 <div class="modal-dialog modal-dialog-centered modal-xl"> 224 <div class="modal-content"> 225 <div class="modal-header visually-hidden"> 226 <h5 class="modal-title" id="productDetailsTableModalTitle_@(Model.ID)_@modalVideoNumber">@product.Title</h5> 227 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 228 </div> 229 <div class="modal-body p-2 p-lg-3 h-100"> 230 @{ @RenderVideoPlayer(asset) } 231 </div> 232 </div> 233 </div> 234 </div> 235 236 modalVideoNumber++; 237 } 238 } 239 } 240 </div> 241 } 242 else if (Pageview.IsVisualEditorMode) 243 { 244 <div class="h-100 @theme"> 245 <div class="alert alert-dark m-0"> 246 @Translate("No assets are available") 247 </div> 248 </div> 249 } 250 } 251 252 @helper RenderAsset(MediaViewModel asset) 253 { 254 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 255 string assetValue = asset.Value; 256 257 <td class="@(theme) px-0"> 258 @foreach (string format in supportedImageFormats) 259 { //Images 260 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 261 { 262 @RenderImage(asset) 263 } 264 } 265 @foreach (string format in supportedVideoFormats) 266 { //Videos 267 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 268 { 269 @RenderVideoScreendump(asset) 270 } 271 } 272 @foreach (string format in supportedDocumentFormats) 273 { //Documents 274 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 275 { 276 @RenderDocument(asset) 277 } 278 } 279 </td> 280 } 281 282 @helper RenderImage(MediaViewModel asset) 283 { 284 string productName = product.Name; 285 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 286 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 287 string imageLinkPath = imagePath; 288 imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; 289 290 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 291 292 RatioSettings ratioSettings = GetRatioSettings(); 293 294 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download> 295 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 296 <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle itemprop="image"> 297 </div> 298 </a> 299 } 300 301 @helper RenderVideoScreendump(MediaViewModel asset) 302 { 303 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 304 305 string videoScreendumpPath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : ""; 306 string videoId = videoScreendumpPath.Substring(videoScreendumpPath.LastIndexOf('/') + 1); 307 videoScreendumpPath = videoScreendumpPath.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || videoScreendumpPath.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "https://img.youtube.com/vi/" + videoId + "/mqdefault.jpg" : videoScreendumpPath; 308 309 string vimeoJsClass = videoScreendumpPath.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "js-vimeo-video-thumbnail" : ""; 310 videoScreendumpPath = videoScreendumpPath.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "" : videoScreendumpPath; 311 312 string productName = product.Name; 313 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 314 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 315 316 RatioSettings ratioSettings = GetRatioSettings(); 317 318 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> 319 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 320 <div class="icon-2 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 321 @if (videoScreendumpPath.IndexOf(".mp4", StringComparison.OrdinalIgnoreCase) < 0) { 322 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@productName" @assetTitle class="@vimeoJsClass mw-100 mh-100" data-video-id="@videoId" style="object-fit: cover;"> 323 } else { 324 string videoType = Path.GetExtension(asset.Value).ToLower(); 325 326 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 327 <source src="@asset.Value" type="video/@videoType.Replace(".", "")"> 328 </video> 329 } 330 </div> 331 </div> 332 } 333 334 @helper RenderDocument(MediaViewModel asset) 335 { 336 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 337 string productName = product.Name; 338 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 339 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 340 string imageLinkPath = imagePath; 341 imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; 342 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 343 344 RatioSettings ratioSettings = GetRatioSettings(); 345 346 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download alt="@productName"> 347 @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) { 348 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 349 <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle /> 350 </div> 351 } else { 352 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 353 <div class="icon-3 position-absolute" style="z-index: 1">@ReadFile(iconPath + "file-text.svg")</div> 354 </div> 355 } 356 </a> 357 } 358 359 @helper RenderVideoPlayer(MediaViewModel asset) 360 { 361 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Name; 362 string assetValue = asset.Value; 363 string videoId = asset.Value.Substring(asset.Value.LastIndexOf('/') + 1); 364 string type = assetValue.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "youtube" : ""; 365 type = assetValue.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "vimeo" : type; 366 type = assetValue.IndexOf(".mp4", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf(".webm", StringComparison.OrdinalIgnoreCase) >= 0 ? "selfhosted" : type; 367 368 <div class="h-100" itemscope itemtype="https://schema.org/VideoObject"> 369 <span class="visually-hidden" itemprop="name">@assetName</span> 370 <span class="visually-hidden" itemprop="contentUrl">@asset.Value</span> 371 <span class="visually-hidden" itemprop="thumbnailUrl">@asset.Value</span> 372 @if (type != "selfhosted") 373 { 374 <div id="player_@(Pageview.CurrentParagraph.ID)_@(videoId)" 375 class="plyr__video-embed" 376 data-plyr-provider="@(type)" 377 data-plyr-embed-id="@videoId" 378 style="--plyr-color-main: var(--swift-foreground-color); height: 100%"> 379 </div> 380 381 <script type="module" src="/Files/Templates/Designs/Swift/Assets/js/plyr.js"></script> 382 <script type="module"> 383 var player = new Plyr('#player_@(Pageview.CurrentParagraph.ID)_@(videoId)', { 384 type: 'video', 385 youtube: { 386 noCookie: true, 387 showinfo: 0 388 }, 389 fullscreen: { 390 enabled: true, 391 iosNative: true, 392 } 393 }); 394 395 document.querySelectorAll('.js-video-modal').forEach(function (modal) { 396 modal.addEventListener('hidden.bs.modal', function (event) { 397 player.media.pause(); 398 }) 399 }); 400 </script> 401 } 402 else 403 { 404 string videoType = Path.GetExtension(assetValue).ToLower(); 405 406 <video preload="auto" class="h-100 w-100" style="object-fit: cover;" controls> 407 <source src="@assetValue" type="video/@videoType.Replace(".", "")"> 408 </video> 409 } 410 </div> 411 } 412
Error executing template "Designs/Swift/Paragraph/Swift_ProductDetailsMediaTable.cshtml" System.ArgumentNullException: Value cannot be null. Parameter name: source at System.Linq.Enumerable.Where[TSource](IEnumerable`1 source, Func`2 predicate) at CompiledRazorTemplates.Dynamic.RazorEngine_457d921174194192b3cdcf0c568215d1.Execute() in D:\dynamicweb.net\Solutions\Flex Media\danzafe.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_ProductDetailsMediaTable.cshtml:line 71 at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader) at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer) at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter) at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template) at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template) at Dynamicweb.Rendering.Template.RenderRazorTemplate()
1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 @using Dynamicweb.Frontend 4 @using System.IO 5 6 @functions { 7 public ProductViewModel product { get; set; } = new ProductViewModel(); 8 public string[] supportedImageFormats { get; set; } 9 public string[] supportedVideoFormats { get; set; } 10 public string[] supportedDocumentFormats { get; set; } 11 public string[] allSupportedFormats { get; set; } 12 13 public class RatioSettings 14 { 15 public string Ratio { get; set; } 16 public string CssClass { get; set; } 17 public string CssVariable { get; set; } 18 public string Fill { get; set; } 19 } 20 21 public RatioSettings GetRatioSettings() 22 { 23 var ratioSettings = new RatioSettings(); 24 25 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 26 ratio = ratio != "0" ? ratio : ""; 27 string cssClass = ratio != "" && ratio != "fill" ? " ratio" : ""; 28 string cssVariable = ratio != "" && ratio != "fill" ? "--bs-aspect-ratio: " + ratio : ""; 29 30 ratioSettings.Ratio = ratio; 31 ratioSettings.CssClass = cssClass; 32 ratioSettings.CssVariable = cssVariable; 33 ratioSettings.Fill = ratio == "fill" ? " h-100" : ""; 34 35 return ratioSettings; 36 } 37 } 38 39 @{ 40 @* Get the product data *@ 41 ProductViewModel product = null; 42 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 43 { 44 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 45 } 46 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 47 { 48 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 49 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 50 51 if (productList?.Products is object) 52 { 53 product = productList.Products[0]; 54 } 55 } 56 } 57 58 @if (product is object) { 59 @* Supported formats *@ 60 supportedImageFormats = new string[] { ".jpg", ".jpeg", ".webp", ".png", ".gif", ".bmp", ".tiff" }; 61 supportedVideoFormats = new string[] { "youtu.be", "youtube", "vimeo", ".mp4", ".webm" }; 62 supportedDocumentFormats = new string[] { ".pdf", ".docx", ".xlsx", ".ppt", "pptx" }; 63 allSupportedFormats = supportedImageFormats.Concat(supportedVideoFormats).Concat(supportedDocumentFormats).ToArray(); 64 65 @* Collect the assets *@ 66 var selectedAssetCategories = Model.Item.GetRawValueString("ImageAssets").Split(',').ToList(); 67 bool includeImagePatternImages = Model.Item.GetBoolean("ImagePatternImages"); 68 69 @* Needed image data collection to support both DefaultImage, ImagePatterns and Image Assets *@ 70 string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : ""; 71 IEnumerable<MediaViewModel> assetsImages = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets); 72 assetsImages = assetsImages.OrderByDescending(x => x.Value.Equals(defaultImage)); 73 IEnumerable<MediaViewModel> assetsList = new MediaViewModel[]{}; 74 75 assetsList = assetsList.Union(assetsImages); 76 assetsList = includeImagePatternImages ? assetsList.Union(product.ImagePatternImages) : assetsList; 77 assetsList = includeImagePatternImages && assetsList.Count() == 0 ? assetsList.Append(product.DefaultImage) : assetsList; 78 79 bool defaultImageFallback = Model.Item.GetBoolean("DefaultImageFallback"); 80 bool showOnlyPrimaryImage = Model.Item.GetBoolean("ShowOnlyPrimaryImage"); 81 82 int totalAssets = 0; 83 if (showOnlyPrimaryImage == false) { 84 foreach (MediaViewModel asset in assetsList) { 85 var assetValue = asset.Value; 86 foreach (string format in allSupportedFormats) { 87 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 88 totalAssets++; 89 } 90 } 91 } 92 } 93 94 if((totalAssets == 0 && product.DefaultImage != null && selectedAssetCategories.Count() == 0) || (showOnlyPrimaryImage == true && product.DefaultImage != null)) 95 { 96 assetsList = new List<MediaViewModel>(){ product.DefaultImage }; 97 totalAssets = 1; 98 } 99 100 int videoNumber = 0; 101 102 @* Layout settings *@ 103 string spacing = Model.Item.GetRawValueString("Spacing", "p-0"); 104 spacing = spacing == "none" ? "p-0" : spacing; 105 spacing = spacing == "small" ? "p-3" : spacing; 106 spacing = spacing == "large" ? "p-5" : spacing; 107 108 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 109 110 bool hideThumbnails = Model.Item.GetBoolean("HideThumbnails"); 111 112 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 113 int modalVideoNumber = 0; 114 115 @* Get assets from selected categories or get all assets *@ 116 117 if (totalAssets != 0 && assetsList.Any()) 118 { 119 <div class="@spacing@(theme) item_@Model.Item.SystemName.ToLower()"> 120 @if (!string.IsNullOrEmpty(Model.Item.GetString("Title")) && !Model.Item.GetBoolean("HideTitle")) 121 { 122 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "h3"); 123 124 <h3 class="@titleFontSize mb-3"> 125 @Model.Item.GetString("Title") 126 </h3> 127 } 128 129 <div class="table-responsive"> 130 <table class="table table-hover align-middle mb-0" style="table-layout: fixed;"> 131 <thead> 132 <tr> 133 @if (!hideThumbnails) 134 { 135 <th style="width:60px"> </th> 136 } 137 <th>@Translate("Name")</th> 138 <th class="text-end d-none d-lg-table-cell">@Translate("Download")</th> 139 <th class="text-end" style="width:100px">@Translate("File type")</th> 140 </tr> 141 </thead> 142 <tbody class="border-top-0"> 143 @foreach (MediaViewModel asset in assetsList) 144 { 145 var assetValue = asset.Value; 146 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Value.Substring( asset.Value.LastIndexOf('/') + 1); 147 148 bool isVideo = false; 149 foreach (string format in supportedVideoFormats) 150 { //Videos 151 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 152 { 153 isVideo = true; 154 } 155 } 156 157 if (!isVideo) 158 { 159 string filePath = Dynamicweb.Context.Current.Server.MapPath(assetValue); 160 long fileSize = 0; 161 162 if (File.Exists(filePath)) { 163 fileSize = new System.IO.FileInfo(filePath) != null ? new System.IO.FileInfo(filePath).Length / 1024 : 0; 164 165 foreach (string format in allSupportedFormats) 166 { 167 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 168 { 169 <tr> 170 @if (!hideThumbnails) 171 { 172 @RenderAsset(asset) 173 } 174 <td> 175 <a href="@assetValue" class="text-decoration-none text-break" download="@assetName" title="@assetName"> 176 @assetName 177 </a> 178 </td> 179 <td class="text-end d-none d-lg-table-cell"> 180 <a href="@assetValue" class="text-decoration-none" download="@assetName" title="@assetName"> 181 @fileSize KB <div class="icon-2" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 182 </a> 183 </td> 184 <td class="text-end">@format</td> 185 </tr> 186 } 187 } 188 } 189 } 190 else 191 { 192 string videoType = asset.Value.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || asset.Value.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "Youtube" : ""; 193 videoType = asset.Value.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "Vimeo" : videoType; 194 195 <tr data-bs-toggle="modal" data-bs-target="#modal_@(Model.ID)_@videoNumber" style="cursor: pointer"> 196 @if (!hideThumbnails) 197 { 198 @RenderAsset(asset) 199 } 200 <td> 201 @assetName 202 </td> 203 <td class="d-none d-lg-table-cell"> </td> 204 <td align="right">@videoType</td> 205 </tr> 206 207 videoNumber++; 208 } 209 } 210 </tbody> 211 </table> 212 </div> 213 214 @foreach (MediaViewModel asset in assetsList) 215 { 216 var assetName = asset.Value.ToLower(); 217 218 foreach (string format in supportedVideoFormats) 219 { //Videos 220 if (assetName.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 221 { 222 <div class="modal fade js-video-modal" id="modal_@(Model.ID)_@modalVideoNumber" tabindex="-1" aria-labelledby="productDetailsTableModalTitle_@(Model.ID)_@modalVideoNumber" aria-hidden="true"> 223 <div class="modal-dialog modal-dialog-centered modal-xl"> 224 <div class="modal-content"> 225 <div class="modal-header visually-hidden"> 226 <h5 class="modal-title" id="productDetailsTableModalTitle_@(Model.ID)_@modalVideoNumber">@product.Title</h5> 227 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 228 </div> 229 <div class="modal-body p-2 p-lg-3 h-100"> 230 @{ @RenderVideoPlayer(asset) } 231 </div> 232 </div> 233 </div> 234 </div> 235 236 modalVideoNumber++; 237 } 238 } 239 } 240 </div> 241 } 242 else if (Pageview.IsVisualEditorMode) 243 { 244 <div class="h-100 @theme"> 245 <div class="alert alert-dark m-0"> 246 @Translate("No assets are available") 247 </div> 248 </div> 249 } 250 } 251 252 @helper RenderAsset(MediaViewModel asset) 253 { 254 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 255 string assetValue = asset.Value; 256 257 <td class="@(theme) px-0"> 258 @foreach (string format in supportedImageFormats) 259 { //Images 260 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 261 { 262 @RenderImage(asset) 263 } 264 } 265 @foreach (string format in supportedVideoFormats) 266 { //Videos 267 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 268 { 269 @RenderVideoScreendump(asset) 270 } 271 } 272 @foreach (string format in supportedDocumentFormats) 273 { //Documents 274 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 275 { 276 @RenderDocument(asset) 277 } 278 } 279 </td> 280 } 281 282 @helper RenderImage(MediaViewModel asset) 283 { 284 string productName = product.Name; 285 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 286 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 287 string imageLinkPath = imagePath; 288 imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; 289 290 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 291 292 RatioSettings ratioSettings = GetRatioSettings(); 293 294 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download> 295 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 296 <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle itemprop="image"> 297 </div> 298 </a> 299 } 300 301 @helper RenderVideoScreendump(MediaViewModel asset) 302 { 303 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 304 305 string videoScreendumpPath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : ""; 306 string videoId = videoScreendumpPath.Substring(videoScreendumpPath.LastIndexOf('/') + 1); 307 videoScreendumpPath = videoScreendumpPath.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || videoScreendumpPath.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "https://img.youtube.com/vi/" + videoId + "/mqdefault.jpg" : videoScreendumpPath; 308 309 string vimeoJsClass = videoScreendumpPath.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "js-vimeo-video-thumbnail" : ""; 310 videoScreendumpPath = videoScreendumpPath.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "" : videoScreendumpPath; 311 312 string productName = product.Name; 313 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 314 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 315 316 RatioSettings ratioSettings = GetRatioSettings(); 317 318 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> 319 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 320 <div class="icon-2 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 321 @if (videoScreendumpPath.IndexOf(".mp4", StringComparison.OrdinalIgnoreCase) < 0) { 322 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@productName" @assetTitle class="@vimeoJsClass mw-100 mh-100" data-video-id="@videoId" style="object-fit: cover;"> 323 } else { 324 string videoType = Path.GetExtension(asset.Value).ToLower(); 325 326 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 327 <source src="@asset.Value" type="video/@videoType.Replace(".", "")"> 328 </video> 329 } 330 </div> 331 </div> 332 } 333 334 @helper RenderDocument(MediaViewModel asset) 335 { 336 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 337 string productName = product.Name; 338 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 339 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 340 string imageLinkPath = imagePath; 341 imagePath = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=60&format=webp"; 342 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 343 344 RatioSettings ratioSettings = GetRatioSettings(); 345 346 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download alt="@productName"> 347 @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) { 348 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 349 <img loading="lazy" src="@imagePath" class="mw-100 mh-100" alt="@productName" @assetTitle /> 350 </div> 351 } else { 352 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 353 <div class="icon-3 position-absolute" style="z-index: 1">@ReadFile(iconPath + "file-text.svg")</div> 354 </div> 355 } 356 </a> 357 } 358 359 @helper RenderVideoPlayer(MediaViewModel asset) 360 { 361 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Name; 362 string assetValue = asset.Value; 363 string videoId = asset.Value.Substring(asset.Value.LastIndexOf('/') + 1); 364 string type = assetValue.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "youtube" : ""; 365 type = assetValue.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "vimeo" : type; 366 type = assetValue.IndexOf(".mp4", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf(".webm", StringComparison.OrdinalIgnoreCase) >= 0 ? "selfhosted" : type; 367 368 <div class="h-100" itemscope itemtype="https://schema.org/VideoObject"> 369 <span class="visually-hidden" itemprop="name">@assetName</span> 370 <span class="visually-hidden" itemprop="contentUrl">@asset.Value</span> 371 <span class="visually-hidden" itemprop="thumbnailUrl">@asset.Value</span> 372 @if (type != "selfhosted") 373 { 374 <div id="player_@(Pageview.CurrentParagraph.ID)_@(videoId)" 375 class="plyr__video-embed" 376 data-plyr-provider="@(type)" 377 data-plyr-embed-id="@videoId" 378 style="--plyr-color-main: var(--swift-foreground-color); height: 100%"> 379 </div> 380 381 <script type="module" src="/Files/Templates/Designs/Swift/Assets/js/plyr.js"></script> 382 <script type="module"> 383 var player = new Plyr('#player_@(Pageview.CurrentParagraph.ID)_@(videoId)', { 384 type: 'video', 385 youtube: { 386 noCookie: true, 387 showinfo: 0 388 }, 389 fullscreen: { 390 enabled: true, 391 iosNative: true, 392 } 393 }); 394 395 document.querySelectorAll('.js-video-modal').forEach(function (modal) { 396 modal.addEventListener('hidden.bs.modal', function (event) { 397 player.media.pause(); 398 }) 399 }); 400 </script> 401 } 402 else 403 { 404 string videoType = Path.GetExtension(assetValue).ToLower(); 405 406 <video preload="auto" class="h-100 w-100" style="object-fit: cover;" controls> 407 <source src="@assetValue" type="video/@videoType.Replace(".", "")"> 408 </video> 409 } 410 </div> 411 } 412
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_d25d0832938b4bd7858a6a90d39d6ed6.Execute() in D:\dynamicweb.net\Solutions\Flex Media\danzafe.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_ProductComponentSlider.cshtml:line 83 at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader) at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer) at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter) at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template) at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template) at Dynamicweb.Rendering.Template.RenderRazorTemplate()
1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Core 3 @using Dynamicweb.Ecommerce.ProductCatalog 4 5 @{ 6 ProductViewModel product = null; 7 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 8 { 9 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 10 } 11 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 layout = Model.Item.GetRawValueString("Layout") == "slider" ? "ProductSliderComponent" : "ProductGridComponent"; 23 string lazyHeight = Model.Item.GetBoolean("SetMinHeightForLazyLoading") ? "min-height: 360px" : ""; 24 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 25 26 //Link generation 27 string pageId = Model.Item.GetLink("ProductSliderServicePage") != null ? Model.Item.GetLink("ProductSliderServicePage").PageId.ToString() : ""; 28 string servicePageByNavigationTag = GetPageIdByNavigationTag("ProductSliderService") != 0 ? GetPageIdByNavigationTag("ProductSliderService").ToString() : ""; 29 pageId = pageId == "" ? servicePageByNavigationTag : pageId; 30 31 string url = "/Default.aspx?ID=" + pageId; 32 33 if (Pageview.IsVisualEditorMode) 34 { 35 url += "&VisualEdit=True"; 36 } 37 38 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]); 39 if (isLazyLoadingForProductInfoEnabled) 40 { 41 url += "&getproductinfo=true"; 42 } 43 44 url += $"&ProductListPartial={layout}"; //Use this to render either a slider or a grid 45 46 //Source type 47 string sourceType = Model.Item.GetRawValueString("RelationType", "trending"); 48 IList<string> relateFromProductIds = new List<string> { }; 49 IList<string> relateFromProductVariantIds = new List<string> { }; 50 IList<string> relateFromGroupIds = new List<string> { }; 51 bool hasVariants = false; 52 53 ProductListViewModel relateToViewModel = Model.Item.GetValue<ProductListViewModel>("RelateTo"); 54 55 //--- PRODUCTS --- 56 if (sourceType == "variants" || sourceType == "frequently" || sourceType == "selected") 57 { 58 if (relateToViewModel?.Products != null) 59 { 60 hasVariants = relateToViewModel.Products.Any(p => !string.IsNullOrEmpty(p.VariantId)); 61 foreach (var fromProduct in relateToViewModel.Products) 62 { 63 if (hasVariants) 64 { 65 if (!string.IsNullOrEmpty(fromProduct.VariantId)) 66 { 67 relateFromProductVariantIds.Add($"{fromProduct.Id} {fromProduct.VariantId}"); 68 } 69 else 70 { 71 relateFromProductVariantIds.Add($"{fromProduct.Id}"); 72 } 73 } 74 relateFromProductIds.Add($"{fromProduct.Id}"); 75 } 76 } 77 } 78 79 80 // -- flex Related -- 81 if(sourceType == "related"){ 82 int relatedCnt = 0; 83 foreach (var g in product.RelatedGroups){ 84 foreach (var p in g.Products){ 85 relateFromProductIds.Add($"{p.ProductId}"); 86 relatedCnt++; 87 } 88 } 89 if(relatedCnt == 0){sourceType = "most-sold";} 90 } 91 92 //--- GROUPS --- 93 if (sourceType == "most-sold" || sourceType == "trending" || sourceType == "latest" || sourceType == "custom") 94 { 95 var groupsToRelateTo = Model.Item.GetString("RelateTo").Split(',').ToList(); 96 97 foreach (var fromGroup in groupsToRelateTo) 98 { 99 var groupId = fromGroup.Length > 2 ? fromGroup.Remove(0, 2) : fromGroup; 100 101 if (!fromGroup.Contains("p_")) 102 { 103 relateFromGroupIds.Add(groupId); 104 } 105 } 106 107 if (relateToViewModel?.Products != null) 108 { 109 foreach (var fromProduct in relateToViewModel.Products) 110 { 111 var groupId = fromProduct.PrimaryOrDefaultGroup.Id; 112 relateFromGroupIds.Add(groupId); 113 } 114 } 115 } 116 117 118 //Create group id collection and products id collection strings 119 string productIds = relateFromProductIds.Count > 0 ? string.Join(",", relateFromProductIds) : ""; 120 string productVariantIds = relateFromProductVariantIds.Count > 0 ? string.Join(",", relateFromProductVariantIds) : ""; 121 string groupIds = relateFromGroupIds.Count > 0 ? string.Join(",", relateFromGroupIds) : ""; 122 123 if (product is object) 124 { 125 if (string.IsNullOrEmpty(productIds) && (sourceType == "variants" || sourceType == "frequently" || sourceType == "selected" || sourceType == "custom")) 126 { 127 productIds = product.Id; 128 } 129 130 if (string.IsNullOrEmpty(groupIds) && (sourceType == "most-sold" || sourceType == "trending" || sourceType == "latest")) 131 { 132 groupIds = product.PrimaryOrDefaultGroup.Id; 133 } 134 } 135 } 136 137 @*Container element for the request*@ 138 @if (!string.IsNullOrEmpty(productIds) || !string.IsNullOrEmpty(groupIds) || (string.IsNullOrEmpty(productIds) && string.IsNullOrEmpty(groupIds))) 139 { 140 <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()"> 141 <input type="hidden" name="ParagraphId" value="@Model.ID" /> 142 <input type="hidden" name="SortOrder" value="DESC"> 143 <input type="hidden" name="SourceType" value="@sourceType"> 144 145 @if (!string.IsNullOrEmpty(groupIds)) 146 { 147 <input type="hidden" name="GroupId" value="@groupIds"> 148 } 149 @if (sourceType != "frequently" && !string.IsNullOrEmpty(productIds) && !hasVariants) 150 { 151 <input type="hidden" name="MainProductId" value="@productIds"> 152 } 153 @if (sourceType != "frequently" && hasVariants) 154 { 155 <input type="hidden" name="ProductVariantId" value="@productVariantIds"> 156 } 157 158 @if (Model.Item.GetInt32("ProductsCount") != 0) 159 { 160 <input type="hidden" name="PageSize" value="@Model.Item.GetInt32("ProductsCount")"> 161 } 162 163 @if (sourceType == "variants") 164 { 165 <input type="hidden" name="isVariant" value="true"> 166 } 167 @if (sourceType == "most-sold") 168 { 169 <input type="hidden" name="SortBy" value="OrderCount"> 170 } 171 @if (sourceType == "trending") 172 { 173 <input type="hidden" name="SortBy" value="OrderCountGrowth"> 174 } 175 @if (sourceType == "frequently" && !string.IsNullOrEmpty(productIds)) 176 { 177 <input type="hidden" name="BoughtWithProductIds" value="[@productIds]"> 178 } 179 @if (sourceType == "latest") 180 { 181 <input type="hidden" name="SortBy" value="Created"> 182 } 183 </form> 184 185 <script type="module" src="/Files/Templates/Designs/Swift/Assets/js/swiffy-slider.js"></script> 186 <script type="module"> 187 swift.AssetLoader.Load('/Files/Templates/Designs/Swift/Assets/css/swiffy-slider.min.css', 'css'); 188 </script> 189 190 <div class="w-100 h-100@(theme)"> 191 <div id="@Model.ID" class="user-select-none" style="scroll-margin-top:var(--header-height,150px)"></div> 192 <div id="ComponentSliderProducts_@Model.ID" class="h-100" style="@lazyHeight"></div> 193 </div> 194 195 <script type="module"> 196 const productSliderContainer = document.querySelector("#ComponentSliderProducts_@Model.ID"); 197 const productSliderForm = document.querySelector("#ComponentSliderProductsForm_@Model.ID"); 198 199 swift.PageUpdater.Update(productSliderForm); 200 productSliderForm.addEventListener("updated.swift.pageupdater", function(e){ 201 if (e.detail.html === "") { 202 productSliderContainer.closest("[data-col-size]").classList.add("d-none"); 203 } 204 }); 205 </script> 206 } 207 else if (Pageview.IsVisualEditorMode == true) 208 { 209 <div class="alert alert-dark" role="alert"> 210 <span>@Translate("Product component slider: The slider will be rendered here, if there is anything to show")</span> 211 </div> 212 } 213
Mest solgte
Beklager. Der er ikke noget at se her