An error occurred while attaching module (Dynamicweb.Frontend.Content)

System.Data.SqlClient.SqlException (0x80131904): A connection was successfully established with the server, but then an error occurred during the login process. (provider: SSL Provider, error: 0 - An existing connection was forcibly closed by the remote host.) ---> System.ComponentModel.Win32Exception (0x80004005): An existing connection was forcibly closed by the remote host
   ved System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)
   ved System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal& connection)
   ved System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection)
   ved System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
   ved System.Data.SqlClient.SqlConnection.TryOpenInner(TaskCompletionSource`1 retry)
   ved System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)
   ved System.Data.SqlClient.SqlConnection.Open()
   ved Dynamicweb.Data.DatabaseConnectionProvider.CreateConnection(Boolean open)
   ved Dynamicweb.Data.Database.CreateConnection()
   ved Dynamicweb.Data.Database.CreateDataReader(CommandBuilder commandBuilder, IDbConnection connection, IDbTransaction transaction, Int32 commandTimeout)
   ved Dynamicweb.Ecommerce.Products.GroupFacetRepository.GetGroupFacets()
   ved Dynamicweb.Ecommerce.ProductCatalog.ViewEngine.CreateView(IEnumerable`1 facetGroups, IDictionary`2 facetGroupResults, IDictionary`2 facetParameters, IEnumerable`1 queryParameters, Boolean showFacetOptionsWithNoResults)
   ved Dynamicweb.Ecommerce.ProductCatalog.ViewModelFactory.CreateView(IEnumerable`1 facetGroups, IDictionary`2 facetGroupResults, IDictionary`2 facetParameters, IEnumerable`1 queryParameters, Boolean showFacetOptionsWithNoResults)
   ved Dynamicweb.Ecommerce.Indexing.ProductQueryHelper.GetProductsAutoIdsFromIndexQuery(IQuery query, ProductCatalogSettings settings, QuerySettings querySettings, IEnumerable`1& facetViewModel, IEnumerable`1& spellCheckerResult)
   ved Dynamicweb.Ecommerce.ProductCatalog.ProductCatalogFrontend.RenderProductList(ProductCatalogSettings settings, QuerySettings querySettings, IQuery query, String groupId, Boolean feed)
   ved Dynamicweb.Ecommerce.ProductCatalog.ProductCatalogFrontend.GetContent()
   ved Dynamicweb.Frontend.Content.GetModuleOutput(Paragraph paragraph, PageView pageview)
ClientConnectionId:0eb8b6c8-646f-4084-9ce2-c04286559bb1
Error Number:10054,State:0,Class:20

Error executing template "Designs/Swift/Paragraph/Swift_EA_ProductListGroupPoster.cshtml"
System.Data.SqlClient.SqlException (0x80131904): A connection was successfully established with the server, but then an error occurred during the login process. (provider: SSL Provider, error: 0 - An existing connection was forcibly closed by the remote host.) ---> System.ComponentModel.Win32Exception (0x80004005): An existing connection was forcibly closed by the remote host
   ved System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)
   ved System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal& connection)
   ved System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection)
   ved System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
   ved System.Data.SqlClient.SqlConnection.TryOpenInner(TaskCompletionSource`1 retry)
   ved System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)
   ved System.Data.SqlClient.SqlConnection.Open()
   ved Dynamicweb.Data.DatabaseConnectionProvider.CreateConnection(Boolean open)
   ved Dynamicweb.Data.Database.CreateConnection()
   ved Dynamicweb.Data.Database.CreateDataReader(CommandBuilder commandBuilder, IDbConnection connection, IDbTransaction transaction, Int32 commandTimeout)
   ved Dynamicweb.Ecommerce.Products.ProductRepository.GetProductsBySql(CommandBuilder query, Boolean doRefactoring, Boolean bulkFill, Boolean useAssortments)
   ved Dynamicweb.Ecommerce.Products.ProductRepository.GetProductsByGroup(Group group, String productLanguageId, Boolean useAssortments)
   ved Dynamicweb.Ecommerce.ProductCatalog.ViewModelFactory.CreateView(ProductListViewModelSettings settings, String groupId)
   ved CompiledRazorTemplates.Dynamic.RazorEngine_62df565f1a9e481ca7c46f67dd6a8fe4.Execute() i C:\inetpub\wwwroot\plus-prod\Files\Templates\Designs\Swift\Paragraph\Swift_EA_ProductListGroupPoster.cshtml:linje 47
   ved RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader)
   ved RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   ved RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer)
   ved RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   ved Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template)
   ved Dynamicweb.Rendering.TemplateRenderingService.Render(Template template)
   ved Dynamicweb.Rendering.Template.RenderRazorTemplate()
ClientConnectionId:0eb8b6c8-646f-4084-9ce2-c04286559bb1
Error Number:10054,State:0,Class:20

1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using System 3 @using System.Collections.Generic 4 @using System.Linq 5 @using Dynamicweb.Core 6 @using Dynamicweb.Frontend.Navigation 7 @using Dynamicweb.Ecommerce.ProductCatalog 8 @using Dynamicweb.Environment 9 @using Dynamicweb.Frontend 10 11 @{ 12 bool isDetailPage = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("ProductID")); 13 14 ProductListViewModel productList = new ProductListViewModel(); 15 16 ProductListViewModelSettings productSetting = new ProductListViewModelSettings 17 { 18 LanguageId = Dynamicweb.Ecommerce.Common.Context.LanguageID, 19 CurrencyCode = Dynamicweb.Ecommerce.Common.Context.Currency.Code, 20 CountryCode = Dynamicweb.Ecommerce.Common.Context.Country.Code2, 21 ShopId = Pageview.Area.EcomShopId 22 }; 23 24 if (Dynamicweb.Context.Current.Items.Contains("ProductList")) 25 { 26 productList = (ProductListViewModel)Dynamicweb.Context.Current.Items["ProductList"]; 27 } 28 else if (Pageview.Item["DummyProductGroup"] != null) 29 { 30 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 31 ProductListViewModel groupList = pageViewModel.Item.GetValue("DummyProductGroup") != null ? pageViewModel.Item.GetValue("DummyProductGroup") as ProductListViewModel : new ProductListViewModel(); 32 33 if (groupList?.Group?.Id != null) 34 { 35 productList = ViewModelFactory.CreateView(productSetting, groupList.Group.Id); 36 Dynamicweb.Context.Current.Items.Add("ProductList", productList); 37 } 38 else 39 { 40 productList = ViewModelFactory.CreateView(productSetting, Dynamicweb.Ecommerce.Services.ProductGroups.GetGroups(Dynamicweb.Ecommerce.Common.Context.LanguageID).FirstOrDefault().Id); 41 42 Dynamicweb.Context.Current.Items.Add("ProductList", productList); 43 } 44 } 45 else if (Pageview.Item["DummyProductGroup"] == null) 46 { 47 productList = ViewModelFactory.CreateView(productSetting, Dynamicweb.Ecommerce.Services.ProductGroups.GetGroups(Dynamicweb.Ecommerce.Common.Context.LanguageID).FirstOrDefault().Id); 48 Dynamicweb.Context.Current.Items.Add("ProductList", productList); 49 } 50 51 string layout = Model.Item.GetRawValueString("Layout", "align-middle-center-text-center"); 52 string alignment = ""; 53 54 string searchParameter = Dynamicweb.Context.Current.Request.GetString("q"); 55 56 bool hideTitle = Model.Item.GetBoolean("HideGroupTitle"); 57 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "display-4"); 58 59 var navigationSettings = new NavigationSettings(); 60 navigationSettings.StartLevel = 2; 61 navigationSettings.ExpandMode = ExpandMode.Path; 62 navigationSettings.Parameters.Add("HideBreadcrumb", true); 63 navigationSettings.Parameters.Add("TitleFontSize", titleFontSize); 64 65 string descriptionFontSize = Model.Item.GetRawValueString("DescriptionFontSize", "fs-6"); 66 67 string groupImage = productList?.Group?.Assets != null ? productList.Group.Assets.FirstOrDefault(x => x.Name == "LargeImage").Value : ""; 68 69 string groupBannerPreHeader = productList?.Group?.GroupFields != null ? productList.Group.GroupFields.FirstOrDefault(x => x.SystemName == "groupBannerPreHeader").Value.ToString() : ""; ; 70 string groupBannerHeader = productList?.Group?.GroupFields != null ? productList.Group.GroupFields.FirstOrDefault(x => x.SystemName == "groupBannerHeader").Value.ToString() : ""; 71 string groupBannerText = productList?.Group?.GroupFields != null ? productList.Group.GroupFields.FirstOrDefault(x => x.SystemName == "GroupBannerText").Value.ToString() : ""; 72 73 string groupBannerButton1Text = productList?.Group?.GroupFields != null ? productList.Group.GroupFields.FirstOrDefault(x => x.SystemName == "groupBannerButton1Text").Value.ToString() : ""; 74 string groupBannerButton1Link = productList?.Group?.GroupFields != null ? productList.Group.GroupFields.FirstOrDefault(x => x.SystemName == "groupBannerButton1Link").Value.ToString() : ""; 75 76 string groupBannerButton2Text = productList?.Group?.GroupFields != null ? productList.Group.GroupFields.FirstOrDefault(x => x.SystemName == "groupBannerButton2Text").Value.ToString() : ""; 77 string groupBannerButton2Link = productList?.Group?.GroupFields != null ? productList.Group.GroupFields.FirstOrDefault(x => x.SystemName == "groupBannerButton2Link").Value.ToString() : ""; 78 79 string posterHeight = Model.Item.GetRawValueString("PosterHeight", ""); 80 posterHeight = posterHeight == "small" ? "min-vh-25" : posterHeight; 81 posterHeight = posterHeight == "medium" ? "min-vh-50" : posterHeight; 82 posterHeight = posterHeight == "large" ? "min-vh-75" : posterHeight; 83 84 string contentPadding = Model.Item.GetRawValueString("ContentPadding", ""); 85 contentPadding = contentPadding == "none" ? "p-3 px-xl-3 py-xl-4" : contentPadding; 86 contentPadding = contentPadding == "small" ? "p-3 p-xl-4" : contentPadding; 87 contentPadding = contentPadding == "large" ? "p-4 p-xl-5" : contentPadding; 88 89 string maxWidth = Model.Item.GetRawValueString("TextReadability", "max-width-on"); 90 maxWidth = maxWidth == "max-width-on" ? " mw-75ch" : maxWidth; 91 maxWidth = maxWidth == "max-width-off" ? "" : maxWidth; 92 93 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 94 } 95 96 @if (!string.IsNullOrEmpty(groupImage) && !isDetailPage) { 97 var parms = new Dictionary<string, object>(); 98 parms.Add("cssClass", "h-100 w-100"); 99 parms.Add("columns", Model.GridRowColumnCount); 100 101 bool splitHeader = false; 102 103 switch (layout) 104 { 105 case "align-top-left-text-left": 106 alignment = "text-start justify-content-start align-items-start"; 107 break; 108 case "align-top-center-text-center": 109 alignment = "text-center justify-content-center align-items-start"; 110 break; 111 case "align-top-right-text-right": 112 alignment = "text-end justify-content-end align-items-start"; 113 break; 114 case "align-middle-left-text-left": 115 alignment = "text-start justify-content-start align-items-center"; 116 break; 117 case "align-middle-center-text-center": 118 alignment = "text-center justify-content-center align-items-center"; 119 break; 120 case "align-middle-right-text-right": 121 alignment = "text-end justify-content-end align-items-center"; 122 break; 123 case "align-bottom-left-text-left": 124 alignment = "text-start justify-content-start align-items-end"; 125 break; 126 case "align-bottom-center-text-center": 127 alignment = "text-center justify-content-center align-items-end"; 128 break; 129 case "align-bottom-right-text-right": 130 alignment = "text-end justify-content-end align-items-end"; 131 break; 132 case "align-middle-center-text-center-image-right": 133 alignment = "text-start justify-content-start align-items-center"; 134 splitHeader = true; 135 break; 136 } 137 138 if (!splitHeader) 139 { 140 <div class="position-relative h-100@(theme) @(posterHeight) item_@Model.Item.SystemName.ToLower()"> 141 @if (!string.IsNullOrEmpty(groupImage)) { 142 string imageFilter = Model.Item.GetRawValueString("ImageFilter", ""); 143 imageFilter = imageFilter == "no-filter" ? "" : imageFilter; 144 imageFilter = imageFilter == "filter" ? " image-filter" : imageFilter; 145 146 <div class="position-absolute top-0 bottom-0 end-0 start-0@(imageFilter)"> 147 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = groupImage }, parms) 148 </div> 149 } 150 151 @if(!hideTitle || !string.IsNullOrEmpty(groupBannerHeader)) { 152 <div class="container-xl h-100 @(contentPadding)"> 153 <div class="h-100 w-100 position-relative d-flex @(alignment)"> 154 <div class="@(maxWidth)"> 155 @if (!hideTitle) { 156 <div class="@descriptionFontSize mb-0-last-child"> 157 @groupBannerHeader 158 </div> 159 } 160 </div> 161 </div> 162 </div> 163 } 164 </div> 165 } 166 else 167 { 168 <div class="@(posterHeight)"> 169 <div class="order-first order-lg-0"> 170 <div class="h-100 position-relative @theme"> 171 <div class="container-xl @(contentPadding)" style="--bs-aspect-ratio: 100%"> 172 <div class="h-100 position-relative @(alignment)"> 173 @if (!string.IsNullOrEmpty(groupBannerPreHeader)) 174 { 175 <div class="@descriptionFontSize pb-3"> 176 <span class="mw-75ch d-inline-block"> 177 @groupBannerPreHeader 178 </span> 179 </div> 180 } 181 @if (!string.IsNullOrEmpty(groupBannerHeader)) 182 { 183 <h1 class="@titleFontSize mb-0"> 184 <span class="mw-75ch d-inline-block">@groupBannerHeader</span> 185 </h1> 186 } 187 @if (!string.IsNullOrEmpty(groupBannerText)) 188 { 189 <div class="@descriptionFontSize pt-3 pb-3"> 190 <span class="mw-75ch d-inline-block"> 191 @groupBannerText 192 </span> 193 </div> 194 } 195 @if ((!string.IsNullOrEmpty(groupBannerButton1Link) && !string.IsNullOrEmpty(groupBannerButton1Text)) || (!string.IsNullOrEmpty(groupBannerButton2Link) && !string.IsNullOrEmpty(groupBannerButton2Text))) 196 { 197 <div class="w-100"> 198 @if (!string.IsNullOrEmpty(groupBannerButton1Link) && !string.IsNullOrEmpty(groupBannerButton1Text)) 199 { 200 <a href="@groupBannerButton1Link" class="btn btn-primary" type="button">@groupBannerButton1Text</a> 201 } 202 @if (!string.IsNullOrEmpty(groupBannerButton2Link) && !string.IsNullOrEmpty(groupBannerButton2Text)) 203 { 204 <a href="@groupBannerButton2Link" class="btn btn-secondary" type="button">@groupBannerButton2Text</a> 205 } 206 </div> 207 } 208 209 </div> 210 </div> 211 </div> 212 </div> 213 </div> 214 } 215 } else if (Pageview.IsVisualEditorMode) { 216 <div class="alert alert-dark m-0" role="alert"> 217 <span>@Translate("Product group poster: The poster paragraph will be shown here, if any")</span> 218 </div> 219 } 220
Error executing template "Designs/Swift/Paragraph/Swift_ProductListGridView_EA.cshtml"
System.NullReferenceException: Objektreferencen er ikke indstillet til en forekomst af et objekt.
   ved CompiledRazorTemplates.Dynamic.RazorEngine_686eba55c7f84584addf1ea6f2bca6c6.Execute() i C:\inetpub\wwwroot\plus-prod\Files\Templates\Designs\Swift\Paragraph\Swift_ProductListGridView_EA.cshtml:linje 2054
   ved RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader)
   ved RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   ved RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer)
   ved RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   ved Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template)
   ved Dynamicweb.Rendering.TemplateRenderingService.Render(Template template)
   ved Dynamicweb.Rendering.Template.RenderRazorTemplate()

1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using System 3 @using System.Collections.Generic 4 @using Dynamicweb.Ecommerce.ProductCatalog 5 @using Dynamicweb.Ecommerce.CustomerExperienceCenter.Favorites 6 @using System.Linq 7 @using Dynamicweb.Core 8 @using Dynamicweb.Ecommerce 9 @using Dynamicweb.Environment 10 @using Plus.CustomModules.ExcelGenerator.Operations 11 @using Plus.CustomModules.Helpers 12 13 @functions 14 { 15 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Ecommerce.DynamicwebLiveIntegration.TemplatesHelper.IsLazyLoadingForProductInfoEnabled; 16 string liveInfoClass = ""; 17 string productInfoFeed = ""; 18 19 string showPricesWithVat = ""; 20 bool neverShowVat = false; 21 22 bool isDetailPage = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("ProductID")); 23 24 ProductListViewModel productList = new ProductListViewModel(); 25 } 26 27 @{ 28 if (Dynamicweb.Context.Current.Items.Contains("ProductList")) 29 { 30 productList = (ProductListViewModel)Dynamicweb.Context.Current.Items["ProductList"]; 31 } 32 33 showPricesWithVat = Pageview.Area.EcomPricesWithVat.ToLower(); 34 neverShowVat = string.IsNullOrEmpty(showPricesWithVat); 35 36 if (isLazyLoadingForProductInfoEnabled) 37 { 38 if (Dynamicweb.Context.Current.Items.Contains("ProductInfoFeed")) 39 { 40 productInfoFeed = Dynamicweb.Context.Current.Items["ProductInfoFeed"]?.ToString(); 41 if (!string.IsNullOrEmpty(productInfoFeed)) 42 { 43 productInfoFeed = $"data-product-info-feed=\"{productInfoFeed}\""; 44 } 45 } 46 liveInfoClass = "js-live-info"; 47 } 48 49 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 50 string themePadding = theme != string.Empty ? "p-3" : string.Empty; 51 var CartId = Dynamicweb.Ecommerce.Common.Context.Cart == null ? "" : Dynamicweb.Ecommerce.Common.Context.Cart.Id; 52 var CartValue = ""; 53 var CartUrl = ""; 54 var order = Dynamicweb.Ecommerce.Services.Orders.GetById(CartId); 55 56 } 57 58 @if (!isDetailPage) 59 { 60 var productCount = productList.Group != null ? Dynamicweb.Ecommerce.Services.ProductGroups?.GetGroup(productList.Group.Id).Products.Count : 1; 61 62 FacetsInfo.GetFacetsInfo(productList, out var facetsFound, out var selectedFacetsCount); 63 64 //var group = Dynamicweb.Ecommerce.Services.ProductGroups.GetGroup(productList.Group.Id); 65 //var productCount = group.Products.Count; 66 67 if (!string.IsNullOrEmpty(theme)) 68 { 69 if (productCount > 0 || selectedFacetsCount > 0) 70 { 71 <div class="h-100@(theme) @themePadding item_@Model.Item.SystemName.ToLower()" @productInfoFeed> 72 @{@RenderProductList()} 73 </div> 74 } 75 76 } 77 else 78 { 79 if (productCount > 0) 80 { 81 <div class="p-3 item_@Model.Item.SystemName.ToLower() @productCount" @productInfoFeed> 82 @{@RenderProductList()} 83 </div> 84 } 85 86 } 87 } 88 89 @helper RenderProductList() 90 { 91 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", ""); 92 bool anonymousUser = Pageview.User == null; 93 bool hidePrice = anonymousUsersLimitations.Contains("price") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHidePrices") && !Dynamicweb.Ecommerce.DynamicwebLiveIntegration.TemplatesHelper.IsWebServiceConnectionAvailable(); 94 95 string productTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ProductTheme")) ? " theme " + Model.Item.GetRawValueString("ProductTheme").Replace(" ", "").Trim().ToLower() : ""; 96 string productThemePadding = productTheme != string.Empty ? "p-3" : string.Empty; 97 98 string url = Dynamicweb.Context.Current.Request.RawUrl; 99 bool hideFavoritesSelector = !string.IsNullOrEmpty(Model.Item.GetString("HideFavoritesSelector")) ? Model.Item.GetBoolean("HideFavoritesSelector") : false; 100 bool showFavoritesSelectorMasterProduct = !string.IsNullOrEmpty(Model.Item.GetString("ShowFavoritesSelectorMasterProduct")) ? Model.Item.GetBoolean("ShowFavoritesSelectorMasterProduct") : false; 101 string staticVariantsLayout = Model.Item.GetRawValueString("StaticVariantsLayout", "hide"); 102 103 string groupId = productList?.Group?.Id != null ? productList.Group.Id : ""; 104 105 var badgeParms = new Dictionary<string, object>(); 106 badgeParms.Add("saleBadgeType", Model.Item.GetRawValue("SaleBadgeType")); 107 badgeParms.Add("saleBadgeCssClassName", Model.Item.GetRawValue("SaleBadgeDesign")); 108 badgeParms.Add("newBadgeCssClassName", Model.Item.GetRawValue("NewBadgeDesign")); 109 badgeParms.Add("newPublicationDays", Model.Item.GetInt32("NewPublicationDays")); 110 badgeParms.Add("campaignBadgesValues", Model.Item.GetRawValueString("CampaignBadges")); 111 112 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("SaleBadgeDesign")) && Model.Item.GetRawValueString("SaleBadgeDesign") != "none" ? true : false; 113 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("NewBadgeDesign")) && Model.Item.GetRawValueString("NewBadgeDesign") != "none" ? true : false; 114 115 string googleAnalyticsTrackingID = Pageview.AreaSettings.GetString("GoogleAnalyticsTrackingID"); 116 string googleAnalyticsMeasurementID = Pageview.AreaSettings.GetString("GoogleAnalyticsMeasurementID"); 117 var cookieOptInLevel = CookieManager.GetCookieOptInLevel(); 118 bool allowTracking = cookieOptInLevel == CookieOptInLevel.All || (cookieOptInLevel == CookieOptInLevel.Functional && CookieManager.GetCookieOptInCategories().Contains("Statistical")); 119 var bagdeItems = Model.Item?.GetItems("Bagdes") ?? Enumerable.Empty<Dynamicweb.Frontend.ItemViewModel>().ToList(); 120 var mobileGrid = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("GridLayoutMobile", "grid-1")) ? Model.Item.GetRawValueString("GridLayoutMobile", "grid-1") : "grid-1"; 121 122 var favoriteParameters = new Dictionary<string, object>(); 123 if (!anonymousUser && !hideFavoritesSelector) 124 { 125 int defaultFavoriteListId = 0; 126 127 IEnumerable<FavoriteList> favoreiteLists = Pageview.User.GetFavoriteLists(); 128 if (favoreiteLists.Count() == 1) 129 { 130 foreach (FavoriteList list in favoreiteLists) 131 { 132 defaultFavoriteListId = list.ListId; 133 } 134 } 135 136 favoriteParameters.Add("ListId", defaultFavoriteListId); 137 } 138 139 if (productList.TotalProductsCount > 0) 140 { 141 if (Pageview.IsVisualEditorMode) 142 { 143 <div class="alert alert-dark m-0" role="alert"> 144 <span>@Translate("Product list: The list will be shown here, if any")</span> 145 </div> 146 } 147 else 148 { 149 150 int pageSizeSetting = 30; 151 int pageSize = productList.PageSize; 152 pageSize += pageSizeSetting; 153 154 int loadedProducts = productList.PageSize > productList.TotalProductsCount ? productList.TotalProductsCount : productList.PageSize; 155 156 //indsæt check på Grid colums felt på mobile når itemtype er opdateret. 157 158 <div class="grid @mobileGrid grid-md-2 grid-lg-3 grid-xl-4"> 159 160 @foreach (ProductViewModel product in productList.Products) 161 { 162 string variantIdForLink = !string.IsNullOrEmpty(product.VariantId) ? $"&VariantID={product.VariantId}" : ""; 163 variantIdForLink = string.IsNullOrEmpty(variantIdForLink) && !string.IsNullOrEmpty(product.DefaultVariantId) ? $"&VariantID={product.DefaultVariantId}" : variantIdForLink; 164 165 string link = "Default.aspx?ID=" + GetPageIdByNavigationTag("Shop"); 166 link += $"&GroupID={product.PrimaryOrDefaultGroup.Id}"; 167 link += $"&ProductID={product.Id}"; 168 link += variantIdForLink; 169 link = Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl(link); 170 171 string imagePath = product?.DefaultImage?.Value ?? ""; 172 imagePath = Dynamicweb.Context.Current.Server.UrlEncode(imagePath); 173 174 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 175 ratio = ratio != "0" ? ratio : ""; 176 string ratioCssClass = ratio != "" ? " ratio" : ""; 177 string ratioVariable = ratio != "" ? "--bs-aspect-ratio: " + ratio : ""; 178 179 string imagePathXs = "/Admin/Public/GetImage.ashx?width=" + 480 + "&image=" + imagePath + "&format=webp"; 180 string imagePathS = "/Admin/Public/GetImage.ashx?width=" + 640 + "&image=" + imagePath + "&format=webp"; 181 string imagePathFallBack = "/Admin/Public/GetImage.ashx?width=" + 640 + "&image=" + imagePath + "&format=webp"; 182 183 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 184 string imageThemePadding = imageTheme != string.Empty ? "p-3" : string.Empty; 185 string imageOutlineStyle = imageTheme == string.Empty ? "style=\"border: 1px solid transparent\"" : string.Empty; 186 187 string imageId = "ProductImage_" + product.Id + product.VariantId; 188 string priceId = "ProductPrice_" + product.Id + product.VariantId;@* Alternative image *@ var supportedImageFormats = new string[] { ".jpg", ".webp", ".png", ".gif" }; 189 string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : ""; 190 var selectedAssetCategories = Model.Item.GetRawValueString("AlternativeImageAssets"); 191 IEnumerable<MediaViewModel> alternativeImagesList = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets); 192 193 if (alternativeImagesList.FirstOrDefault() != null) 194 { 195 alternativeImagesList = alternativeImagesList.OrderByDescending(x => x.Value.Equals(defaultImage)); 196 197 if (alternativeImagesList.First().Value == defaultImage) 198 { 199 alternativeImagesList = alternativeImagesList.Skip(1); 200 } 201 } 202 203 string alternativeImage = alternativeImagesList.FirstOrDefault() != null ? alternativeImagesList.FirstOrDefault().Value : ""; 204 alternativeImage = !string.IsNullOrEmpty(alternativeImage) ? "/Admin/Public/GetImage.ashx?width=" + 640 + "&image=" + alternativeImage + "&format=webp" : ""; @* Badges *@ DateTime createdDate = product.Created.Value; 205 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false; 206 showBadges = (newBadgeEnabled && Model.Item.GetInt32("NewPublicationDays") == 0) || (newBadgeEnabled && (createdDate.AddDays(Model.Item.GetInt32("NewPublicationDays")) > DateTime.Now)) ? true : showBadges; 207 showBadges = !string.IsNullOrEmpty(Model.Item.GetRawValueString("CampaignBadges")) ? true : showBadges; @* Main features *@ IEnumerable<string> selectedDisplayGroups = Model.Item.GetRawValueString("MainFeatures").Split(',').ToList(); 208 List<CategoryFieldViewModel> mainFeatures = new List<CategoryFieldViewModel>(); 209 210 foreach (var selection in selectedDisplayGroups) 211 { 212 foreach (CategoryFieldViewModel group in product.FieldDisplayGroups.Values) 213 { 214 if (selection == group.Id) 215 { 216 mainFeatures.Add(group); 217 } 218 } 219 } 220 221 FieldValueViewModel pageContent; 222 product.ProductFields.TryGetValue("EFA_PageContentID", out pageContent); 223 var pageContentID = !string.IsNullOrEmpty(pageContent.Value.ToString()) ? Convert.ToInt32(pageContent.Value) : 0; 224 225 if (pageContentID != 0) 226 { 227 var paragraphs = Dynamicweb.Content.Services.Paragraphs.GetParagraphsByPageId(pageContentID); 228 229 if (paragraphs.Count() <= 1) 230 { 231 <article class="position-relative@(productTheme) product-list-item js-product @liveInfoClass" data-product-id="@product.Id"> 232 @{ 233 foreach (var paragraph in paragraphs) 234 { 235 <div class="d-block h-100 align-items-stretch p-3"> 236 @RenderParagraphContent(paragraph.ID) 237 </div> 238 }; 239 } 240 </article> 241 } 242 } 243 else 244 { 245 <article class="position-relative@(productTheme) product-list-item js-product @liveInfoClass" data-product-id="@product.Id" itemscope itemtype="https://schema.org/Product"> 246 @if (!anonymousUser) 247 { 248 if (!hideFavoritesSelector && product.VariantInfo.VariantInfo == null) 249 { 250 <div class="position-absolute top-0 end-0 my-3" style="z-index: 2"> 251 @RenderPartial("Components/ToggleFavorite.cshtml", product, favoriteParameters) 252 </div> 253 } 254 else if (showFavoritesSelectorMasterProduct) 255 { 256 <div class="position-absolute top-0 end-0 my-3" style="z-index: 2"> 257 @RenderPartial("Components/ToggleFavorite.cshtml", product, favoriteParameters) 258 </div> 259 } 260 } 261 262 @if (showBadges) 263 { 264 <div class="position-absolute top-0 left-0 p-1 p-lg-2 ps-0 ps-lg-0" style="z-index: 2"> 265 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms) 266 </div> 267 } 268 269 <div class="d-flex flex-column d-block h-100 align-items-stretch"> 270 @{ 271 string clickProductLink = string.Empty; 272 if (!string.IsNullOrWhiteSpace(googleAnalyticsMeasurementID) && allowTracking) 273 { 274 clickProductLink = "onclick=\"return clickProductLink('" + product.Id + "', '" + product.Name + "', '" + product.VariantName + "', '" + product.Price.CurrencyCode + "', '" + product.Price.Price + "')\""; 275 } 276 } 277 <a href="@link" class="text-decoration-none d-flex flex-column j h-100" @clickProductLink> 278 @if (!string.IsNullOrWhiteSpace(googleAnalyticsMeasurementID) && allowTracking) 279 { 280 <script> 281 function clickProductLink(productId, productName, productVariant, productCurrency, productPrice) { 282 if (typeof gtag !== "undefined") { 283 gtag("event", "select_item", { 284 item_list_id: "product_list_gridview", 285 item_list_name: "Product list (Gridview)", 286 items: [ 287 { 288 item_id: productId, 289 item_name: productName, 290 currency: productCurrency, 291 item_list_id: "product_list_gridview", 292 item_list_name: "Product list (Gridview)", 293 item_variant: productVariant, 294 price: productPrice 295 } 296 ] 297 }); 298 } 299 } 300 </script> 301 } 302 303 <div class="px-3 order-2"> 304 <div class="flex-grow-1"> 305 <h3 class="h6 mb-0 text-break" itemprop="name"> 306 @product.Name 307 @if (!string.IsNullOrEmpty(product.VariantName)) 308 {<text>(@product.VariantName)</text>} 309 </h3> 310 @if (!Model.Item.GetBoolean("HideProductNumber")) 311 { 312 <p class="fs-7 opacity-85 mb-2">@product.Number</p> 313 } 314 @if (mainFeatures.Count > 0) 315 { 316 <ul class="p-0 lh-sm opacity-75" style="list-style-position: inside"> 317 @foreach (CategoryFieldViewModel mainFeatureGroup in mainFeatures) 318 { 319 foreach (var fieldViewModel in mainFeatureGroup.Fields) 320 { 321 var field = fieldViewModel.Value; 322 string fieldValue = field?.Value is object ? field.Value.ToString() : ""; 323 324 if (fieldValue != "") 325 { 326 fieldValue = fieldValue == "False" ? Translate("No") : fieldValue; 327 fieldValue = fieldValue == "True" ? Translate("Yes") : fieldValue; 328 329 if (field.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 330 { 331 fieldValue = ""; 332 333 foreach (FieldOptionValueViewModel option in field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>) 334 { 335 fieldValue = option.Name; 336 } 337 } 338 339 bool isColor = false; 340 if (fieldValue.Contains("#") && (Translate(field.Name) == Translate("Color") || Translate(field.Name) == Translate("Colour"))) 341 { 342 isColor = true; 343 } 344 345 if (!string.IsNullOrEmpty(fieldValue)) 346 { 347 if (!isColor) 348 { 349 <li>@(field.Name): @fieldValue</li> 350 } 351 else 352 { 353 <li class="position-relative"> 354 <span class="colorbox-sm" style="background-color: @fieldValue"></span> 355 </li> 356 } 357 } 358 } 359 } 360 } 361 </ul> 362 363 } 364 </div> 365 366 @if (product.VariantInfo.VariantInfo != null && staticVariantsLayout == "swatches") 367 { 368 var optionCount = product.VariantInfo.VariantInfo.Count(); 369 var showMaxVariants = 5; 370 371 <div class="d-flex flex-row gap-1 align-items-center"> 372 @foreach (VariantInfoViewModel variant in product.VariantInfo.VariantInfo.Take(showMaxVariants)) 373 { 374 <span class="colorbox colorbox-sm rounded-circle border me-1" style="background-color: @variant.OptionColor"></span> 375 376 } 377 @if (optionCount > showMaxVariants) 378 { 379 int left = optionCount - showMaxVariants; 380 <span class="ms-2">+@left</span> 381 382 } 383 </div> 384 } 385 </div> 386 387 <div class="overflow-hidden order-1 @(imageTheme) pb-2"> 388 <div class="d-flex justify-content-center align-items-center"> 389 @{ 390 FieldValueViewModel plusDesignerIndikator; 391 product.ProductFields.TryGetValue("PlusDesignerLinkEA", out plusDesignerIndikator); 392 string target = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") ? "target=\"_blank\"" : string.Empty; 393 string rel = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") ? "rel=\"noopener\"" : string.Empty; 394 395 if (!String.IsNullOrEmpty(plusDesignerIndikator.Value.ToString())) 396 { 397 <div class="plus-designer__container" style="position: absolute; left: 0; margin-bottom: 55%; background: #fff; border: 1px solid #228B64; border-left: none; z-index: 100; width: 75%; max-width: 200px; "> 398 <object> 399 <a href="@plusDesignerIndikator.Value" @target @rel class="text-decoration-none"> 400 <div class="plus-designer__inner p-1 ps-3"> 401 <p class="m-0 opacity-75 fs-7">Try me in</p> 402 <div class="plus-designer__image-container d-flex w-100"> 403 <img class="" src="/Files/Templates/Designs/Swift/Assets/Images/plusdesignerlogo.svg" alt="Alternate Text" style="width:90%" /> 404 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-right" style="width:10%"><polyline points="9 18 15 12 9 6"></polyline></svg> 405 </div> 406 </div> 407 </a> 408 </object> 409 </div> 410 411 } 412 FieldValueViewModel plusBadge; 413 product.ProductFields.TryGetValue("Art", out plusBadge); 414 FieldValueViewModel fscBadge; 415 product.ProductFields.TryGetValue("FSC", out fscBadge); 416 FieldValueViewModel extraBadgeListObject; 417 product.ProductFields.TryGetValue("ExtraPlusBadges", out extraBadgeListObject); 418 FieldValueViewModel fscBadgeLink; 419 product.ProductFields.TryGetValue("FSC_link", out fscBadgeLink); 420 FieldValueViewModel pefcBadgeLink; 421 product.ProductFields.TryGetValue("PEFC_link", out pefcBadgeLink); 422 <div></div> 423 if (!String.IsNullOrEmpty(plusBadge.Value.ToString()) || !String.IsNullOrEmpty(fscBadge.Value.ToString()) || extraBadgeListObject.Value != null) 424 { 425 foreach (var badge in bagdeItems) 426 { 427 if (fscBadge.Value.ToString().Contains(badge.GetString("Title"))) 428 { 429 430 <div class="plus-badge d-flex justify-content-center w-100 fsc-badge-list efa-mod "> 431 <object class="w-100"> 432 <div class="d-flex justify-content-start w-75 h-75 efa-mod m-auto"> 433 @if (fscBadge.Value.ToString().Contains("FSC")) 434 { 435 <a href="@fscBadgeLink.Value.ToString()" target="_blank" class="w-25"><img class="plus-badge_img fsc-badge_img w-100 efa-mod" srcset="@badge.GetString("Image")" src="" alt="@fscBadge.Value.ToString()" /></a> 436 } 437 @if (fscBadge.Value.ToString().Contains("PEFC")) 438 { 439 <a href="@pefcBadgeLink.Value.ToString()" target="_blank" class="w-25"><img class="plus-badge_img fsc-badge_img w-100 efa-mod" srcset="@badge.GetString("Image")" src="" alt="@fscBadge.Value.ToString()" /></a> 440 } 441 </div> 442 </object> 443 </div> 444 } 445 var badgeTitle = badge.GetString("Title"); 446 447 switch (product.LanguageId) 448 { 449 case "LANG3": 450 { 451 if (badgeTitle.StartsWith(plusBadge.Value.ToString()) && badgeTitle.EndsWith("_DE")) 452 { 453 @RenderBadge(badge.GetString("Image"), plusBadge.Value.ToString()) 454 } 455 break; 456 } 457 case "LANG4": 458 { 459 if (badgeTitle.StartsWith(plusBadge.Value.ToString()) && badgeTitle.EndsWith("_SE")) 460 { 461 @RenderBadge(badge.GetString("Image"), plusBadge.Value.ToString()) 462 } 463 break; 464 } 465 case "LANG5": 466 { 467 if (badgeTitle.StartsWith(plusBadge.Value.ToString()) && badgeTitle.EndsWith("_NO")) 468 { 469 @RenderBadge(badge.GetString("Image"), plusBadge.Value.ToString()) 470 } 471 break; 472 } 473 default: 474 { 475 if (badgeTitle.StartsWith(plusBadge.Value.ToString()) && !badgeTitle.EndsWith("_NO") && !badgeTitle.EndsWith("_SE") && !badgeTitle.EndsWith("_DE")) 476 { 477 @RenderBadge(badge.GetString("Image"), plusBadge.Value.ToString()) 478 } 479 break; 480 } 481 } 482 483 if (extraBadgeListObject.Value is List<FieldOptionValueViewModel>) 484 { 485 var extraBadgeList = (List<FieldOptionValueViewModel>)extraBadgeListObject.Value; 486 foreach (var extraBadge in extraBadgeList) 487 { 488 var extraBadgeTitle = extraBadge.Value; 489 490 switch (product.LanguageId) 491 { 492 case "LANG3": 493 { 494 if (badgeTitle.StartsWith(extraBadgeTitle) && badgeTitle.EndsWith("_DE")) 495 { 496 @RenderExtraBadge(System.Web.HttpUtility.UrlPathEncode(badge.GetString("Image")), extraBadge.Value.ToString()) 497 } 498 break; 499 } 500 case "LANG4": 501 { 502 if (badgeTitle.StartsWith(extraBadgeTitle) && badgeTitle.EndsWith("_SE")) 503 { 504 @RenderExtraBadge(System.Web.HttpUtility.UrlPathEncode(badge.GetString("Image")), extraBadge.Value.ToString()) 505 } 506 break; 507 } 508 case "LANG5": 509 { 510 if (badgeTitle.StartsWith(extraBadgeTitle) && badgeTitle.EndsWith("_NO")) 511 { 512 @RenderExtraBadge(System.Web.HttpUtility.UrlPathEncode(badge.GetString("Image")), extraBadge.Value.ToString()) 513 } 514 break; 515 } 516 default: 517 { 518 if (badgeTitle.StartsWith(extraBadgeTitle) && !badgeTitle.EndsWith("_NO") && !badgeTitle.EndsWith("_SE") && !badgeTitle.EndsWith("_DE")) 519 { 520 @RenderExtraBadge(System.Web.HttpUtility.UrlPathEncode(badge.GetString("Image")), extraBadge.Value.ToString()) 521 } 522 break; 523 } 524 } 525 } 526 } 527 } 528 } 529 530 } 531 @if (string.IsNullOrEmpty(alternativeImage)) 532 { 533 <img id="@imageId" itemprop="image" 534 srcset=" 535 @imagePathXs 480w, 536 @imagePathS 640w" 537 sizes="(min-width: 992px) 33vw, 50vw" 538 src="@imagePathFallBack" 539 loading="lazy" 540 decoding="async" 541 class="w-75 mw-100 mh-100 @imageThemePadding" 542 alt="@product.Name"> 543 } 544 else 545 { 546 <img id="@imageId" itemprop="image" 547 src="@imagePathFallBack" 548 loading="lazy" 549 decoding="async" 550 class="w-75 mw-100 mh-100 @imageThemePadding" 551 alt="@product.Name" 552 onmouseover="this.src='@alternativeImage'" 553 onmouseout="this.src='@imagePathFallBack'"> 554 555 } 556 </div> 557 558 @if (product.VariantInfo.VariantInfo != null && staticVariantsLayout == "images") 559 { 560 int variantGroupCount = 0; 561 int showMaxVariantGroups = 2; 562 int showMaxVariants = 3; 563 var productVariantTheme = productTheme != "" ? productTheme : "bg-white"; 564 565 <div class="position-relative"> 566 <div id="StaticVariants_@product.Id" class="static-variants w-100 d-none d-lg-block position-absolute left-0 bottom-0 @productTheme" style="pointer-events: none;"> 567 568 @foreach (var variantGroup in product.VariantGroups()) 569 { 570 int variantsCount = 0; 571 572 <div class="d-flex gap-2 mb-2"> 573 @foreach (var variant in variantGroup.Options) 574 { 575 if (variantGroupCount < showMaxVariantGroups) 576 { 577 var optionsCount = variantGroup.Options.Count(); 578 579 if (variantsCount < showMaxVariants) 580 { 581 string optionWidth = !string.IsNullOrEmpty(variant.Color) ? "w-25" : ""; 582 583 <article class="static-variants-option @optionWidth @(productVariantTheme)" title="@product.Name @variant.Name" style="pointer-events: initial;"> 584 @if (!string.IsNullOrEmpty(variant.Color)) 585 { 586 string defaultProductImage = Dynamicweb.Context.Current.Server.UrlEncode(product.DefaultImage.Value); 587 string variantImage = Dynamicweb.Context.Current.Server.UrlEncode(variant.Image.Value); 588 string defaultPrice = !hidePrice ? product.Price.PriceFormatted : "0"; 589 string variantPrice = !hidePrice ? product.Price.PriceFormatted : "0"; 590 591 if (isLazyLoadingForProductInfoEnabled) 592 { 593 <figure class="w-100 d-block m-0" data-price-formatted="" onmouseover="swift.StaticVariants.SwitchProduct(event, '@product.Id', this.getAttribute('data-price-formatted'), '@variantImage')" onmouseout="swift.StaticVariants.SwitchProduct(event, '@product.Id', this.getAttribute('data-price-formatted'), '@defaultProductImage')"> 594 <div class="d-flex align-items-center justify-content-center"> 595 <img src="/admin/public/GetImage.ashx?image=@variantImage&width=75&height=75&crop=5&FillCanvas=true&format=webp&Quality=70" height="75" width="75" class="p-1 text-small" loading="lazy" decoding="async" alt="@product.Name, @variant.Name"> 596 </div> 597 </figure> 598 } 599 else 600 { 601 <figure class="w-100 d-block m-0" onmouseover="swift.StaticVariants.SwitchProduct(event, '@product.Id', '@defaultPrice', '@variantImage')" onmouseout="swift.StaticVariants.SwitchProduct(event, '@product.Id', '@variantPrice', '@defaultProductImage')"> 602 <div class="d-flex align-items-center justify-content-center"> 603 <img src="/admin/public/GetImage.ashx?image=@variantImage&width=75&height=75&crop=5&FillCanvas=true&format=webp&Quality=70" height="75" width="75" class="p-1 text-small" loading="lazy" decoding="async" alt="@product.Name, @variant.Name"> 604 </div> 605 </figure> 606 } 607 } 608 else 609 { 610 <div class="d-flex align-items-center justify-content-center"> 611 @variant.Name 612 </div> 613 } 614 <div class="visually-hidden"> 615 <h4>@product.Name, @variant.Name</h4> 616 @if (!hidePrice) 617 { 618 if (isLazyLoadingForProductInfoEnabled) 619 { 620 <span class="text-price js-text-price"></span> 621 } 622 else 623 { 624 <span class="text-price">@product.Price.PriceFormatted</span> 625 626 } 627 } 628 </div> 629 </article> 630 } 631 632 variantsCount++; 633 634 if (variantsCount == showMaxVariants && optionsCount != showMaxVariants) 635 { 636 int left = optionsCount - showMaxVariants; 637 <div class="variant-option ms-1 d-flex justify-content-center align-items-center"> 638 <span>+@left</span> 639 </div> 640 } 641 } 642 } 643 </div> 644 variantGroupCount++; 645 } 646 </div> 647 </div> 648 } 649 </div> 650 </a> 651 @if (!hidePrice) 652 { 653 string priceMin = ""; 654 string priceMax = ""; 655 <div itemprop="offers" itemscope itemtype="https://schema.org/Offer"> 656 <div class="px-3"> 657 <span itemprop="priceCurrency" content="@product.Price.CurrencyCode" class="d-none"></span> 658 659 @if (showPricesWithVat == "false" && !neverShowVat) 660 { 661 if (isLazyLoadingForProductInfoEnabled) 662 { 663 <span itemprop="price" content="" class="d-none"></span> 664 <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> } 665 else 666 { 667 string beforePrice = product.PriceBeforeDiscount.PriceWithoutVatFormatted; 668 <span itemprop="price" content="@product.Price.PriceWithoutVat" class="d-none"></span> 669 if (product.Price.Price != product.PriceBeforeDiscount.Price) 670 { 671 <span class="text-decoration-line-through opacity-75 me-3 text-price">@beforePrice</span> } 672 } 673 674 } 675 else 676 { 677 678 if (isLazyLoadingForProductInfoEnabled) 679 { 680 <span itemprop="price" content="" class="d-none"></span> 681 <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> 682 } 683 else 684 { 685 string beforePrice = product.PriceBeforeDiscount.PriceFormatted; 686 string googlepriceraw = product.Price.PriceWithVat.ToString(); 687 string googlepriceformatted = googlepriceraw.Replace(',', '.'); 688 689 <span itemprop="price" content="@googlepriceformatted" class="d-none"></span> 690 if (product.Price.Price != product.PriceBeforeDiscount.Price) 691 { 692 <span class="text-decoration-line-through opacity-75 me-3 text-price">@beforePrice</span> 693 694 } 695 } 696 } 697 698 @if (showPricesWithVat == "false" && !neverShowVat) 699 { 700 if (isLazyLoadingForProductInfoEnabled) 701 { 702 <span class="text-price js-text-price"><span class="spinner-border" role="status"></span></span> } 703 else 704 { 705 string price = product.Price.PriceWithoutVatFormatted; 706 if (product?.VariantInfo?.VariantInfo != null) 707 { 708 priceMin = product?.VariantInfo?.PriceMin?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithoutVatFormatted : ""; 709 priceMax = product?.VariantInfo?.PriceMax?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithoutVatFormatted : ""; 710 } 711 if (priceMin != priceMax) 712 { 713 price = priceMin + " - " + priceMax; 714 } 715 <span class="text-price">@price</span> 716 } 717 } 718 else 719 { 720 if (isLazyLoadingForProductInfoEnabled) 721 { 722 <span class="text-price js-text-price"><span class="spinner-border" role="status"></span></span> 723 } 724 else 725 { 726 string price = product.Price.PriceFormatted; 727 var priceStandardFieldValueConverted = CampaignPrice.GetConvertedPriceWithVat(product); 728 bool campaignPrice = CampaignPrice.CheckCampaign(product, priceStandardFieldValueConverted); 729 var priceBefore = CampaignPrice.PriceBeforeWithVat(product, priceStandardFieldValueConverted); 730 731 if (product?.VariantInfo?.VariantInfo != null) 732 { 733 priceMin = product?.VariantInfo?.PriceMin?.PriceFormatted != null ? product.VariantInfo.PriceMin.PriceFormatted : ""; 734 priceMax = product?.VariantInfo?.PriceMax?.PriceFormatted != null ? product.VariantInfo.PriceMax.PriceFormatted : ""; 735 } 736 if (priceMin != priceMax) 737 { 738 price = priceMin + " - " + priceMax; 739 } 740 741 <div class="d-flex flex-wrap"> 742 @if (campaignPrice) 743 { 744 <span class="text-decoration-line-through opacity-75 me-3 text-price w-100">@priceBefore</span> 745 } 746 <span class="text-price fw-bold w-100">@price</span> 747 </div> 748 749 string ecomCountryCode = !string.IsNullOrEmpty(Pageview.Area.EcomCountryCode) ? Pageview.Area.EcomCountryCode : ""; 750 var countryVat = Services.Countries.GetCountry(ecomCountryCode).Vat > 0 ? Services.Countries.GetCountry(ecomCountryCode).Vat : 0; 751 var productToShow = Services.Products.GetProductById(product.Id, product.VariantId, product.LanguageId); 752 var currency = Dynamicweb.Ecommerce.Common.Context.Currency.Code; 753 string shipId = ecomCountryCode.IsNotNullOrEmpty() ? ShippingBasedOnProductSetting.GetShippingId(ecomCountryCode, productToShow) : ""; 754 product.ProductFields.TryGetValue("ProductNextBackorderDate", out FieldValueViewModel backInStockDateValue); 755 DateTime backInstockDate = backInStockDateValue.Value != null ? DateTime.Parse(backInStockDateValue.Value.ToString()) : DateTime.Today.AddDays(-1); 756 bool hasBackInstockDate = backInStockDateValue != null && backInstockDate >= DateTime.Today; 757 bool hasExpectedDelivery = product.ExpectedDelivery != null && product.ExpectedDelivery >= DateTime.Today; 758 string expectedDeliveryDate = hasBackInstockDate ? backInstockDate.ToShortDateString() : product.ExpectedDelivery?.ToShortDateString() ?? ""; 759 760 <div class="fs-8 mt-1 mb-2 text-start"> 761 @if (!string.IsNullOrWhiteSpace(product.StockStatus)) 762 { 763 <p class="mb-1">@product.StockStatus</p> 764 if (!string.IsNullOrWhiteSpace(product.StockDeliveryText)) 765 { 766 <p class="mb-1">@product.StockDeliveryText</p> 767 } 768 } 769 @if (hasExpectedDelivery || hasBackInstockDate) 770 { 771 <div class="mb-1"> 772 <span>@Translate("Expected in stock"): </span> 773 <span>@expectedDeliveryDate</span> 774 </div> 775 } 776 </div> 777 778 @*if (!string.IsNullOrWhiteSpace(product.StockStatus)) { 779 <div class="">@product.StockStatus</div> 780 <div class="">@product.StockDeliveryText</div> 781 { 782 <div> 783 <span>@Translate("Expected in stock"): </span> 784 <span>@expectedDeliveryDate</span> 785 </div> 786 } 787 } else { 788 789 if (hasExpectedDelivery || hasBackInstockDate) 790 { 791 <div> 792 <span>@Translate("Expected in stock"): </span> 793 <span>@expectedDeliveryDate</span> 794 </div> 795 } 796 }*@ 797 798 @*if (!string.IsNullOrEmpty(ecomCountryCode) && !hidePrice && product.Id != null && shipId != null) 799 { 800 var shippingFeeAmount = ShippingBasedOnProductSetting.GetShippingAmount(shipId, countryVat, productToShow, currency); 801 802 803 804 if (shippingFeeAmount != null) 805 { 806 <div class="fs-8 text-black-50 text-start"> 807 <p>@Translate("Levering fra") @currency @shippingFeeAmount</p> 808 </div> 809 } 810 }*@ 811 } 812 } 813 814 @if (showPricesWithVat == "false" && !neverShowVat) 815 { 816 if (isLazyLoadingForProductInfoEnabled) 817 { 818 <div class="fs-7 opacity-85 text-price js-text-price-with-vat d-none" data-suffix="@Translate("Incl. VAT")"></div> 819 } 820 else 821 { 822 string price = product.Price.PriceWithVatFormatted; 823 if (product?.VariantInfo?.VariantInfo != null) 824 { 825 priceMin = product?.VariantInfo?.PriceMin?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithVatFormatted : ""; 826 priceMax = product?.VariantInfo?.PriceMax?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithVatFormatted : ""; 827 } 828 if (priceMin != priceMax) 829 { 830 price = priceMin + " - " + priceMax; 831 } 832 <div class="fs-7 opacity-85 text-price">@price @Translate("Incl. VAT")</div> 833 834 } 835 } 836 </div> 837 </div> 838 } 839 @RenderAddToCart(product, link, clickProductLink) 840 </div> 841 </article> 842 } 843 844 } 845 </div> 846 847 <div class="my-3" id="LoadMoreButton"> 848 <div class="text-center d-flex flex-column gap-3"> 849 <div class="opacity-85">@loadedProducts @Translate("out of") @productList.TotalProductsCount @Translate("products")</div> 850 @if (productList.PageCount != 1) 851 { 852 string sortBySelection = Dynamicweb.Context.Current.Request?.Form["SortBy"] ?? ""; 853 sortBySelection = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("SortBy")) ? Dynamicweb.Context.Current.Request.QueryString.Get("SortBy") : sortBySelection; 854 855 string searchQuery = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("q")) ? Dynamicweb.Context.Current.Request.QueryString.Get("q") : ""; 856 string searchLayout = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("SearchLayout")) ? Dynamicweb.Context.Current.Request.QueryString.Get("SearchLayout") : ""; 857 858 <form method="get" action="@url" data-response-target-element="content" class="w-100"> 859 @foreach (FacetGroupViewModel facetGroup in productList.FacetGroups) 860 { 861 foreach (FacetViewModel facetItem in facetGroup.Facets) 862 { 863 foreach (FacetOptionViewModel facetOption in facetItem.Options) 864 { 865 if (facetOption.Selected) 866 { 867 <input type="hidden" name="@facetItem.QueryParameter" value="[@facetOption.Value]"> 868 } 869 } 870 } 871 } 872 873 <input type="hidden" name="PageSize" value="@pageSize"> 874 <input type="hidden" name="SortBy" value="@sortBySelection"> 875 <input type="hidden" name="RequestType" value="UpdateList"> 876 877 @if (!string.IsNullOrEmpty(searchQuery)) 878 { 879 <input type="hidden" name="q" value="@searchQuery"> 880 <input type="hidden" name="SearchLayout" value="@searchLayout"> 881 } 882 883 @{ 884 string nextPageLink = "/Default.aspx?ID=" + Pageview.Page.ID + "&PageSize=" + pageSize + "&SortBy=" + sortBySelection; 885 886 foreach (FacetGroupViewModel facetGroup in productList.FacetGroups) 887 { 888 foreach (FacetViewModel facetItem in facetGroup.Facets) 889 { 890 foreach (FacetOptionViewModel facetOption in facetItem.Options) 891 { 892 if (facetOption.Selected) 893 { 894 nextPageLink += "&" + facetItem.QueryParameter + "=[" + facetOption.Value + "]"; 895 } 896 } 897 } 898 } 899 900 nextPageLink += !string.IsNullOrEmpty(searchQuery) ? "&q=" + searchQuery : ""; 901 } 902 903 <a href="@nextPageLink" class="btn btn-primary" onclick="swift.ProductList.Update(event)" id="LoadMoreButton_@Model.ID">@Translate("Load more products")</a> 904 </form> 905 906 } 907 </div> 908 </div> 909 } 910 } 911 else 912 { 913 <div class="alert alert-dark m-0"> 914 @Translate("We did not find anything matching your search result") 915 </div> 916 } 917 } 918 919 @helper RenderBadge(string badge, string plusBadge) 920 { 921 <div class="plus-badge d-flex justify-content-center w-100 efa-mod"> 922 <div class="d-flex justify-content-end w-75 h-75"> 923 <img class="plus-badge_img efa-mod" srcset="@badge" src="" alt="@plusBadge" /> 924 </div> 925 </div> 926 } 927 928 @helper RenderExtraBadge(string badge, string extraBadge) 929 { 930 <div class="plus-badge d-flex justify-content-center w-100 efa-mod"> 931 <div class="d-flex justify-content-start w-75 h-75"> 932 <img class="extra-plus-badge_img efa-mod" srcset="@System.Web.HttpUtility.UrlPathEncode(badge)" src="" alt="@extraBadge" /> 933 </div> 934 </div> 935 } 936 937 938 @helper RenderViewMore(string link, string clickProductLink) 939 { 940 <a href="@link" class="btn btn-secondary flex-fill" @clickProductLink>@Translate("View more")</a> 941 } 942 943 @helper RenderAddToCart(ProductViewModel product, string link, string clickProductLink) 944 { 945 string iconPath = "/Files/icons/"; 946 string url = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("EasyFlowCart")); 947 if (!url.Contains("LayoutTemplate")) 948 { 949 url += url.Contains("?") ? "&LayoutTemplate=Swift_MiniCart.cshtml" : "?LayoutTemplate=Swift_MiniCart.cshtml"; 950 } 951 952 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", ""); 953 bool anonymousUser = Pageview.User == null; 954 955 bool hideAddToCart = !string.IsNullOrEmpty(Model.Item.GetString("HideAddToCart")) ? Model.Item.GetBoolean("HideAddToCart") : false; 956 hideAddToCart = anonymousUsersLimitations.Contains("cart") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHideAddToCart") && !Dynamicweb.Ecommerce.DynamicwebLiveIntegration.TemplatesHelper.IsWebServiceConnectionAvailable() ? true : hideAddToCart; 957 bool quantitySelector = !string.IsNullOrEmpty(Model.Item.GetString("QuantitySelector")) ? Model.Item.GetBoolean("QuantitySelector") : false; 958 959 bool isDiscontinued = product.Discontinued; 960 bool IsNeverOutOfStock = product.NeverOutOfstock; 961 string disableAddToCart = (product.StockLevel <= 0) ? "disabled" : ""; 962 disableAddToCart = isDiscontinued ? "disabled" : disableAddToCart; 963 disableAddToCart = IsNeverOutOfStock ? "" : disableAddToCart; 964 disableAddToCart = isLazyLoadingForProductInfoEnabled ? "disabled" : disableAddToCart; 965 966 string ecomCountryCode = !string.IsNullOrEmpty(Pageview.Area.EcomCountryCode) ? Pageview.Area.EcomCountryCode : ""; 967 var countryVat = Services.Countries.GetCountry(ecomCountryCode).Vat > 0 ? Services.Countries.GetCountry(ecomCountryCode).Vat : 0; 968 var productToShow = Services.Products.GetProductById(product.Id, product.VariantId, product.LanguageId); 969 var currency = Dynamicweb.Ecommerce.Common.Context.Currency.Code; 970 string shipId = ecomCountryCode.IsNotNullOrEmpty() ? ShippingBasedOnProductSetting.GetShippingId(ecomCountryCode, productToShow) : ""; 971 var shippingFeeAmount = string.Empty; 972 973 if (!string.IsNullOrEmpty(ecomCountryCode) && shipId != null) 974 { 975 shippingFeeAmount = ShippingBasedOnProductSetting.GetShippingAmount(shipId, countryVat, productToShow, currency); 976 } 977 978 var addToCartLabel = Model.Item.GetRawValueString("GridLayoutMobile", "grid-1") == "grid-2" ? "" : Translate("Add to cart"); 979 string hideOnMobile = Model.Item.GetRawValueString("GridLayoutMobile") == "grid-2" ? "hide-on-mobile" : ""; 980 string viewMoreIcon = Model.Item.GetRawValueString("ViewMoreIcon"); 981 982 var deliveryCost = Translate("Levering fra") + " " + currency + " " + shippingFeeAmount; 983 var deliveryName = ShippingBasedOnProductSetting.GetShippingName(shipId, product.LanguageId); 984 985 if (!hideAddToCart) 986 { 987 if (product.VariantInfo.VariantInfo == null) 988 { 989 string minQty = product.PurchaseMinimumQuantity != 1 ? "min=\"" + product.PurchaseMinimumQuantity.ToString() + "\"" : "min=\"1\""; 990 string stepQty = product.PurchaseQuantityStep > 1 ? product.PurchaseQuantityStep.ToString() : "1"; 991 string valueQty = product.PurchaseMinimumQuantity > product.PurchaseQuantityStep ? product.PurchaseMinimumQuantity.ToString() : stepQty; 992 string qtyValidCheck = stepQty != "1" ? "onkeyup=\"swift.Cart.QuantityValidate(event)\"" : ""; 993 string cartUrl = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("EasyFlowCart")); 994 string imagePathHidden = product?.DefaultImage.Value.ToString() ?? ""; 995 imagePathHidden = "/Admin/Public/GetImage.ashx?image=" + imagePathHidden + "&width=" + 350 + "&Format=WebP&Quality=70"; 996 <div class="px-3 pb-3"> 997 <form method="post" action="@url"> 998 <input type="hidden" name="redirect" value="false"> 999 <input type="hidden" name="MainProductId" value="None"> 1000 <input type="hidden" name="ProductId" value="@product.Id"> 1001 <input type="hidden" name="ProductName" value="@product.Name"> 1002 <input type="hidden" name="ProductCurrency" value="@Dynamicweb.Ecommerce.Common.Context.Currency.Code"> 1003 <input type="hidden" name="ProductReferer" value="product_list_listview"> 1004 <input type="hidden" name="ProductPrice" value="@product.Price.PriceFormatted"> 1005 <input type="hidden" name="cartcmd" value="add"> 1006 @* #38 EA: sbj *@ 1007 <input type="hidden" name="ProductPriceFormatted" value="@product.Price.PriceFormatted"> 1008 <input type="hidden" name="ProductUnit" value="@Translate("stk.", "stk.")"> 1009 <input type="hidden" name="ProductTotalText" value="@Translate("I alt", "I alt")"> 1010 <input type="hidden" name="ProductImage" value="@imagePathHidden"> 1011 <input type="hidden" name="ProductDeliveryCost" value="@deliveryCost"> 1012 <input type="hidden" name="ProductDeliveryName" value="@deliveryName"> 1013 <input type="hidden" name="cartpage" value="@cartUrl"> 1014 1015 @if (!string.IsNullOrEmpty(product.VariantId)) 1016 { 1017 <input type="hidden" name="VariantId" value="@product.VariantId">} 1018 1019 @if (quantitySelector) 1020 { 1021 <div class="input-group input-primary-button-group"> 1022 <input id="Quantity_@(product.Id)_@product.VariantId" name="Quantity" value="@valueQty" step="@stepQty" @minQty class="form-control" style="max-width: 100px" type="number" onkeydown="swift.Cart.UpdateOnEnterKey(event)" @disableAddToCart> 1023 <div class="d-flex gap-2"> 1024 @RenderViewMore(link, clickProductLink) 1025 @*<a href="@link" class="btn btn-secondary flex-fill" @clickProductLink>@Translate("View more")</a>*@ 1026 @if (disableAddToCart == "disabled") 1027 { 1028 <button name="notify-cookie" type="button" data-href="@link" onclick="addCookie('ProductNotifier',true,1,'@link');" class="btn btn-primary flex-fill js-add-to-cart-button" title="@Translate("Add to cart")" id="AddCookie@(product.Id)"> 1029 <span class="icon-2 d-flex gap-2 align-items-center justify-content-center">@ReadFile(iconPath + "envelope.svg") @Translate("Produkt notifikations label")</span> 1030 </button> 1031 } 1032 else 1033 { 1034 <button type="button" onclick="swift.Cart.Update(event)" class="btn btn-primary flex-fill js-add-to-cart-button" @disableAddToCart title="@Translate("Add to cart")" id="AddToCartButton@(product.Id)"> 1035 <span class="icon-2 d-flex gap-2 align-items-center justify-content-center">@ReadFile(iconPath + "shopping-cart.svg") @Translate("Add to cart")</span> 1036 </button> 1037 } 1038 </div> 1039 </div> 1040 if (stepQty != "1") 1041 { 1042 <div class="invalid-feedback d-none"> 1043 @Translate("Please select a quantity that is dividable by") @stepQty 1044 </div> 1045 1046 } 1047 <label for="Quantity_@(product.Id)_@product.VariantId" class="visually-hidden">@Translate("Quantity")</label> 1048 } 1049 else 1050 { 1051 <input id="Quantity_@(product.Id)_@product.VariantId" name="Quantity" value="@valueQty" type="hidden" @disableAddToCart> 1052 <div class="d-flex gap-2"> 1053 <a href="@link" class="btn btn-secondary flex-fill view-more-btn px-0" @clickProductLink><div class="@hideOnMobile">@ReadFile(viewMoreIcon)<span>@Translate("View more")</span></div></a> 1054 @if (disableAddToCart == "disabled") 1055 { 1056 <button name="notify-cookie" type="button" data-href="@link" class="btn btn-primary flex-fill js-add-to-cart-button" title="@Translate("Produkt notifikations label")" id="AddCookie@(product.Id)"> 1057 <span class="icon-2 d-flex gap-2 align-items-center justify-content-center @hideOnMobile">@ReadFile(iconPath + "envelope.svg") <span class="add-to-cart-label"> @Translate("Produkt notifikations label")</span></span> 1058 </button> 1059 } 1060 else 1061 { 1062 <button type="button" onclick="swift.Cart.Update(event)" class="btn btn-primary flex-fill js-add-to-cart-button px-0" title="@Translate("Add to cart")" id="AddToCartButton@(product.Id)"> 1063 <span class="icon-2 d-flex gap-2 align-items-center justify-content-center @hideOnMobile">@ReadFile(iconPath + "shopping-cart.svg") <span class="add-to-cart-label">@Translate("Add to cart")</span></span> 1064 @*<span class="icon-2 d-flex gap-2 align-items-center justify-content-center d-md-none">@ReadFile(iconPath + "shopping-cart.svg")</span>*@ 1065 </button> 1066 } 1067 </div> 1068 1069 } 1070 </form> 1071 </div> 1072 } 1073 else 1074 { 1075 string buttonWidth = quantitySelector ? "calc(var(--swift-button-primary-padding-x) * 2 + 1rem + 7rem)" : "calc(var(--swift-button-primary-padding-x) * 2 + 1rem)"; @* Set the width of the container to: button-primary-padding-x × 2 + 1rem for the icon + 7rem for the quantity input *@ string buttonText = quantitySelector ? Translate("Select") : "<span class=\"icon-2\">" + @ReadFile(iconPath + "shopping-cart.svg") + "</span>"; 1076 1077 string variantSelectorServicePageId = !string.IsNullOrEmpty(Model.Item.GetString("VariantSelectorServicePageId")) ? Model.Item.GetLink("VariantSelectorServicePageId").PageId.ToString() : ""; 1078 variantSelectorServicePageId = variantSelectorServicePageId != "" ? variantSelectorServicePageId : GetPageIdByNavigationTag("VariantSelectorService").ToString(); 1079 1080 string disableVariantSelector = isLazyLoadingForProductInfoEnabled ? "disabled" : ""; 1081 <div class="px-3 pb-3"> 1082 <form action="/Default.aspx?ID=@variantSelectorServicePageId" data-response-target-element="DynamicModalContent" data-preloader="inline" class="d-inline-block"> 1083 <input type="hidden" name="ProductID" value="@product.Id"> 1084 <input type="hidden" name="QuantitySelector" value="@quantitySelector.ToString()"> 1085 <input type="hidden" name="HideInventory" value="@Model.Item.GetBoolean("HideInventory").ToString()"> 1086 <input type="hidden" name="HideStockState" value="@Model.Item.GetBoolean("HideStockState").ToString()"> 1087 <input type="hidden" name="VariantSelectorServicePage" value="@variantSelectorServicePageId"> 1088 <input type="hidden" name="ViewType" value="ModalContent"> 1089 <button type="button" onclick="swift.PageUpdater.Update(event)" class="btn btn-primary" style="width: @buttonWidth" @disableVariantSelector @disableVariantSelector title="@Translate("Select")" data-bs-toggle="modal" data-bs-target="#DynamicModal" id="OpenVariantSelectorModal@(product.Id)_@Pageview.CurrentParagraph.ID">@buttonText</button> 1090 </form> 1091 </div> 1092 1093 } 1094 } 1095 @RenderModal(product) 1096 1097 } 1098 1099 @helper RenderProduct(ProductInfoViewModel relatedProduct, string productId) 1100 { 1101 var relProduct = relatedProduct.GetProduct(); 1102 var mainProductId = productId.IsNotNullOrEmpty() ? productId : "None"; 1103 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", ""); 1104 bool anonymousUser = Pageview.User == null; 1105 bool hidePrice = anonymousUsersLimitations.Contains("price") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHidePrices") && !Dynamicweb.Ecommerce.DynamicwebLiveIntegration.TemplatesHelper.IsWebServiceConnectionAvailable(); 1106 bool showFavoritesSelectorMasterProduct = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ShowFavoritesSelectorMasterProduct")) ? Convert.ToBoolean(Dynamicweb.Context.Current.Request.Form.Get("ShowFavoritesSelectorMasterProduct")) : false; 1107 1108 string ratio = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ImageAspectRatio")) ? Dynamicweb.Context.Current.Request.Form.Get("ImageAspectRatio") : ""; 1109 string ratioCssClass = ratio != "" ? "ratio" : ""; 1110 string ratioVariable = ratio != "" ? "--bs-aspect-ratio: " + ratio : ""; 1111 1112 string theme = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("Theme")) ? Dynamicweb.Context.Current.Request.Form.Get("Theme") : ""; 1113 string themePadding = theme != string.Empty ? "p-3" : string.Empty; 1114 string imageTheme = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ImageTheme")) ? Dynamicweb.Context.Current.Request.Form.Get("ImageTheme") : ""; 1115 string imageOutlineStyle = imageTheme == string.Empty ? "border: 1px solid transparent;" : string.Empty; 1116 string imageThemePadding = imageTheme != string.Empty ? "p-3" : string.Empty; 1117 string ContentPadding = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ContentPadding")) ? Dynamicweb.Context.Current.Request.Form.Get("ContentPadding") : ""; 1118 1119 string showPricesWithVat = Pageview.Area.EcomPricesWithVat.ToLower(); 1120 bool neverShowVat = string.IsNullOrEmpty(showPricesWithVat); 1121 1122 string variantIdForLink = !string.IsNullOrEmpty(relProduct.VariantId) ? $"&VariantID={relProduct.VariantId}" : ""; 1123 variantIdForLink = string.IsNullOrEmpty(variantIdForLink) && !string.IsNullOrEmpty(relProduct.DefaultVariantId) ? $"&VariantID={relProduct.DefaultVariantId}" : variantIdForLink; 1124 1125 string link = "Default.aspx?ID=" + GetPageIdByNavigationTag("Shop"); 1126 link += $"&GroupID={relProduct.PrimaryOrDefaultGroup.Id}"; 1127 link += $"&ProductID={relProduct.Id}"; 1128 link += variantIdForLink; 1129 link = Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl(link); 1130 1131 string imagePath = relProduct?.DefaultImage.Value.ToString() ?? ""; 1132 imagePath = "/Admin/Public/GetImage.ashx?image=" + imagePath + "&width=" + 350 + "&Format=WebP&Quality=70"; 1133 1134 string saleBadgeType = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeType")) ? Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeType") : ""; 1135 string saleBadgeCssClassName = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeCssClassName")) ? Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeCssClassName") : ""; 1136 string newBadgeCssClassName = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("NewBadgeCssClassName")) ? Dynamicweb.Context.Current.Request.Form.Get("NewBadgeCssClassName") : ""; 1137 int newPublicationDays = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("NewPublicationDays")) ? Convert.ToInt32(Dynamicweb.Context.Current.Request.Form.Get("NewPublicationDays")) : 0; 1138 string campaignBadgesValues = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("CampaignBadgesValues")) ? Dynamicweb.Context.Current.Request.Form.Get("CampaignBadgesValues") : ""; 1139 1140 var badgeParms = new Dictionary<string, object>(); 1141 badgeParms.Add("saleBadgeType", saleBadgeType); 1142 badgeParms.Add("saleBadgeCssClassName", saleBadgeCssClassName); 1143 badgeParms.Add("newBadgeCssClassName", newBadgeCssClassName); 1144 badgeParms.Add("campaignBadgesValues", campaignBadgesValues); 1145 badgeParms.Add("newPublicationDays", newPublicationDays); 1146 1147 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(saleBadgeCssClassName) && saleBadgeCssClassName != "none" ? true : false; 1148 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(newBadgeCssClassName) && newBadgeCssClassName != "none" ? true : false; 1149 DateTime createdDate = relProduct.Created.Value; 1150 bool showBadges = saleBadgeEnabled && relProduct.Discount.Price != 0 ? true : false; 1151 showBadges = (newBadgeEnabled && newPublicationDays == 0) || (newBadgeEnabled && (createdDate.AddDays(newPublicationDays) > DateTime.Now)) ? true : showBadges; 1152 showBadges = !string.IsNullOrEmpty(campaignBadgesValues) ? true : showBadges; 1153 1154 string disableAddToCart = (relProduct.StockLevel <= 0) ? "disabled" : ""; 1155 bool isNeverOutOfStock = relProduct.NeverOutOfstock; 1156 disableAddToCart = isNeverOutOfStock ? "" : disableAddToCart; 1157 1158 string iconPath = "/Files/icons/"; 1159 string addToCartIcon = Model.Item.GetRawValueString("Icon", iconPath + "shopping-cart.svg"); 1160 string addToCartLabel = !addToCartIcon.Contains("_none") ? "<span class=\"icon-2\">" + ReadFile(addToCartIcon) + "</span>" : ""; 1161 addToCartLabel += !addToCartIcon.Contains("_none") && !Model.Item.GetBoolean("HideButtonText") ? " " : ""; 1162 addToCartLabel += !Model.Item.GetBoolean("HideButtonText") ? Translate("Add to cart") : ""; 1163 string flexFill = Model.Item.GetRawValueString("HorizontalAlignment", "") == "full" ? "flex-fill" : ""; 1164 string buttonSize = Model.Item.GetRawValueString("ButtonSize", "regular"); 1165 string inputSize = string.Empty; 1166 1167 string url = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("EasyFlowCart")); 1168 if (!url.Contains("LayoutTemplate")) 1169 { 1170 url += url.Contains("?") ? "&LayoutTemplate=Swift_MiniCart.cshtml" : "?LayoutTemplate=Swift_MiniCart.cshtml"; 1171 } 1172 string cartUrl = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("EasyFlowCart")); 1173 string imagePathHidden = relProduct?.DefaultImage.Value.ToString() ?? ""; 1174 imagePathHidden = "/Admin/Public/GetImage.ashx?image=" + imagePathHidden + "&width=" + 350 + "&Format=WebP&Quality=70"; 1175 1176 switch (buttonSize) 1177 { 1178 case "small": 1179 inputSize = " input-group-sm"; 1180 buttonSize = " btn-sm"; 1181 break; 1182 case "regular": 1183 buttonSize = string.Empty; 1184 break; 1185 case "large": 1186 inputSize = " input-group-lg"; 1187 buttonSize = " btn-lg"; 1188 break; 1189 } 1190 1191 string ecomCountryCode = !string.IsNullOrEmpty(Pageview.Area.EcomCountryCode) ? Pageview.Area.EcomCountryCode : ""; 1192 var countryVat = Services.Countries.GetCountry(ecomCountryCode).Vat > 0 ? Services.Countries.GetCountry(ecomCountryCode).Vat : 0; 1193 var productToShow = Services.Products.GetProductById(relProduct.Id, relProduct.VariantId, relProduct.LanguageId); 1194 var currency = Dynamicweb.Ecommerce.Common.Context.Currency.Code; 1195 string shipId = ecomCountryCode.IsNotNullOrEmpty() ? ShippingBasedOnProductSetting.GetShippingId(ecomCountryCode, productToShow) : ""; 1196 var shippingFeeAmount = string.Empty; 1197 1198 if (!string.IsNullOrEmpty(ecomCountryCode) && shipId != null) 1199 { 1200 shippingFeeAmount = ShippingBasedOnProductSetting.GetShippingAmount(shipId, countryVat, productToShow, currency); 1201 } 1202 1203 var deliveryCost = Translate("Levering fra") + " " + currency + " " + shippingFeeAmount; 1204 var deliveryName = ShippingBasedOnProductSetting.GetShippingName(shipId, relProduct.LanguageId); 1205 1206 <div class="text-decoration-none d-block h-100"> 1207 <div class="h-100 d-flex flex-column justify-content-between@(theme)"> 1208 @{ 1209 FieldValueViewModel plusDesignerModalIndikator; 1210 relProduct.ProductFields.TryGetValue("PlusDesignerLinkEA", out plusDesignerModalIndikator); 1211 string target = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") ? "target=\"_blank\"" : string.Empty; 1212 string rel = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") ? "rel=\"noopener\"" : string.Empty; 1213 1214 if (!string.IsNullOrEmpty(plusDesignerModalIndikator.Value.ToString())) 1215 { 1216 <div class="plus-designer__container" style="position: absolute; left: 0; bottom: 10px; background: #fff; border: 1px solid #228B64; border-left: none; z-index: 100; width: 75%; max-width: 200px; "> 1217 <object> 1218 <a href="@plusDesignerModalIndikator.Value" @target @rel class="text-decoration-none"> 1219 <div class="plus-designer__inner p-1 ps-3"> 1220 <p class="m-0 opacity-75 fs-7">Try me in</p> 1221 <div class="plus-designer__image-container d-flex w-100"> 1222 <img class="" src="/Files/Templates/Designs/Swift/Assets/Images/plusdesignerlogo.svg" alt="Alternate Text" style="width:90%" /> 1223 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-right" style="width:10%"><polyline points="9 18 15 12 9 6"></polyline></svg> 1224 </div> 1225 </div> 1226 </a> 1227 </object> 1228 </div> 1229 1230 } 1231 1232 } 1233 <div class="@(imageTheme)" style="@imageOutlineStyle"> 1234 <div class="@(ratioCssClass) position-relative m-auto mw200 efa-mod" style="@ratioVariable"> 1235 @if (showBadges) 1236 { 1237 <div class="position-absolute top-0 left-0 p-1 p-lg-2" style="z-index: 2"> 1238 @{ 1239 @RenderPartial("Components/EcommerceBadge.cshtml", relProduct, badgeParms) 1240 } 1241 </div> 1242 } 1243 <img loading="lazy" decoding="async" src="@imagePath" class="h-100 w-100 @(imageThemePadding)" style="object-fit: contain;" alt="@relProduct.Name"> 1244 </div> 1245 </div> 1246 <div class="flex-fill p-3 pb-0 d-flex flex-column justify-content-between @themePadding"> 1247 <h3 class="h6 opacity-85">@relProduct.Name @relProduct.VariantName</h3> 1248 1249 @if (!hidePrice) 1250 { 1251 <div> 1252 <p class="h6 m-0"> 1253 @if (showPricesWithVat == "false" && !neverShowVat) 1254 { 1255 if (relProduct.Price.Price != relProduct.PriceBeforeDiscount.Price) 1256 { 1257 <span class="text-decoration-line-through opacity-75 me-1"> 1258 @relProduct.PriceBeforeDiscount.PriceWithoutVatFormatted 1259 </span> 1260 } 1261 } 1262 else 1263 { 1264 if (relProduct.Price.Price != relProduct.PriceBeforeDiscount.Price) 1265 { 1266 <span class="text-decoration-line-through opacity-75 me-1"> 1267 @relProduct.PriceBeforeDiscount.PriceFormatted 1268 </span> 1269 } 1270 } 1271 1272 @if (showPricesWithVat == "false" && !neverShowVat) 1273 { 1274 <span class="text-price fw-bold">@relProduct.Price.PriceWithoutVatFormatted</span> 1275 } 1276 else 1277 { 1278 <span class="text-price fw-bold">@relProduct.Price.PriceFormatted</span> 1279 } 1280 </p> 1281 @if (shippingFeeAmount != null) 1282 { 1283 <div class="fs-8 text-black-50 text-start"> 1284 <p>@deliveryCost</p> 1285 </div> 1286 } 1287 1288 1289 @if (showPricesWithVat == "false" && !neverShowVat) 1290 { 1291 <small class="opacity-85 fst-normal">@relProduct.Price.PriceWithVatFormatted @Translate("Incl. VAT")</small> 1292 } 1293 1294 </div> 1295 } 1296 </div> 1297 1298 <div class="d-flex gap-4 p-3 pt-0"> 1299 @{ 1300 string stepQty = relProduct.PurchaseQuantityStep > 1 ? relProduct.PurchaseQuantityStep.ToString() : "1"; 1301 string valueQty = relProduct.PurchaseMinimumQuantity > relProduct.PurchaseQuantityStep ? relProduct.PurchaseMinimumQuantity.ToString() : stepQty; 1302 1303 } 1304 <form method="post" action="@url"> 1305 <input type="hidden" name="redirect" value="false"> 1306 <input type="hidden" name="MainProductId" value="@mainProductId"> 1307 <input type="hidden" name="ProductId" value="@relProduct.Id"> 1308 <input type="hidden" name="ProductName" value="@relProduct.Name"> 1309 <input type="hidden" name="ProductCurrency" value="@Dynamicweb.Ecommerce.Common.Context.Currency.Code"> 1310 <input type="hidden" name="ProductReferer" value="product_list_listview"> 1311 <input type="hidden" name="ProductPrice" value="@relProduct.Price.PriceFormatted"> 1312 <input type="hidden" name="cartcmd" value="add"> 1313 @* #38 EA: sbj *@ 1314 <input type="hidden" name="ProductPriceFormatted" value="@relProduct.Price.PriceFormatted"> 1315 <input type="hidden" name="ProductUnit" value="@Translate("stk.", "stk.")"> 1316 <input type="hidden" name="ProductTotalText" value="@Translate("I alt", "I alt")"> 1317 <input type="hidden" name="ProductImage" value="@imagePathHidden"> 1318 <input type="hidden" name="ProductDeliveryCost" value="@deliveryCost"> 1319 <input type="hidden" name="ProductDeliveryName" value="@deliveryName"> 1320 <input type="hidden" name="cartpage" value="@cartUrl"> 1321 1322 <input id="Quantity_@(relProduct.Id)_@relProduct.VariantId" name="Quantity" value="@valueQty" type="hidden" @disableAddToCart> 1323 <div class="d-flex gap-3 p-3 pt-0"> 1324 <a href="@link" class="btn btn-secondary flex-fill">@Translate("View more")</a> 1325 <a onclick="swift.Cart.Update(event)" class="btn btn-primary @(buttonSize) @flexFill flex-fill js-add-to-cart-button" style="white-space: nowrap" @disableAddToCart title="@Translate("Add to cart")" id="AddToCartButton@(relProduct.Id)_@Pageview.CurrentParagraph.ID"> 1326 @if (!Model.Item.GetBoolean("HideButtonText")) 1327 { 1328 <span class="text-nowrap d-flex align-items-center justify-content-center gap-2"> 1329 @addToCartLabel 1330 </span> 1331 } 1332 else 1333 { 1334 @addToCartLabel 1335 } 1336 </a> 1337 </div> 1338 </form> 1339 </div> 1340 1341 </div> 1342 </div> 1343 @*@RenderModalRelatedProducts(relProduct)*@ 1344 } 1345 1346 @helper RenderModal(ProductViewModel product) 1347 { 1348 <div id="cartNotificationModal_@(product.Id)" class="modal" tabindex="-1" aria-labelledby="cartNotificationModalTitel" aria-hidden="true"> 1349 @{ 1350 string modelId = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ModelID")) ? Dynamicweb.Context.Current.Request.Form.Get("ModelID") : product.Id; 1351 1352 string scrollBarForceMobile = Dynamicweb.Context.Current.Request.Form.Get("NavigationShowScrollbar") != string.Empty ? "--swiffy-slider-track-height:0.5rem !important;" : string.Empty; 1353 bool hideSliderNavigation = false; 1354 1355 int itemsShown = 3; 1356 1357 string imageTheme = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ImageTheme")) ? Dynamicweb.Context.Current.Request.Form.Get("ImageTheme") : ""; 1358 string imageOutlineStyle = imageTheme == string.Empty ? "border: 1px solid transparent;" : string.Empty; 1359 string imageThemePadding = imageTheme != string.Empty ? "p-3" : string.Empty; 1360 1361 string imagePath = product?.DefaultImage.Value.ToString() ?? ""; 1362 imagePath = "/Admin/Public/GetImage.ashx?image=" + imagePath + "&width=" + 350 + "&Format=WebP&Quality=70"; 1363 1364 string ratio = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ImageAspectRatio")) ? Dynamicweb.Context.Current.Request.Form.Get("ImageAspectRatio") : ""; 1365 string ratioCssClass = ratio != "" ? "ratio" : ""; 1366 string ratioVariable = ratio != "" ? "--bs-aspect-ratio: " + ratio : ""; 1367 1368 string saleBadgeType = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeType")) ? Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeType") : ""; 1369 string saleBadgeCssClassName = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeCssClassName")) ? Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeCssClassName") : ""; 1370 string newBadgeCssClassName = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("NewBadgeCssClassName")) ? Dynamicweb.Context.Current.Request.Form.Get("NewBadgeCssClassName") : ""; 1371 int newPublicationDays = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("NewPublicationDays")) ? Convert.ToInt32(Dynamicweb.Context.Current.Request.Form.Get("NewPublicationDays")) : 0; 1372 string campaignBadgesValues = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("CampaignBadgesValues")) ? Dynamicweb.Context.Current.Request.Form.Get("CampaignBadgesValues") : ""; 1373 1374 var badgeParms = new Dictionary<string, object>(); 1375 badgeParms.Add("saleBadgeType", saleBadgeType); 1376 badgeParms.Add("saleBadgeCssClassName", saleBadgeCssClassName); 1377 badgeParms.Add("newBadgeCssClassName", newBadgeCssClassName); 1378 badgeParms.Add("campaignBadgesValues", campaignBadgesValues); 1379 badgeParms.Add("newPublicationDays", newPublicationDays); 1380 1381 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(saleBadgeCssClassName) && saleBadgeCssClassName != "none" ? true : false; 1382 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(newBadgeCssClassName) && newBadgeCssClassName != "none" ? true : false; 1383 DateTime createdDate = product.Created.Value; 1384 1385 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false; 1386 showBadges = (newBadgeEnabled && newPublicationDays == 0) || (newBadgeEnabled && (createdDate.AddDays(newPublicationDays) > DateTime.Now)) ? true : showBadges; 1387 showBadges = !string.IsNullOrEmpty(campaignBadgesValues) ? true : showBadges; 1388 var productCounter = 1; 1389 1390 var shopPageId = GetPageIdByNavigationTag("Shop"); 1391 1392 bool relatedExist = false; 1393 string modalProductCon = ""; 1394 string modalProductSize = ""; 1395 foreach (var relatedGroup in product.RelatedGroups) 1396 { 1397 if (relatedGroup.Id == "Addon") 1398 { 1399 if (relatedGroup.Products.Count > 0) 1400 { 1401 relatedExist = true; 1402 modalProductCon = "col-xl-7"; 1403 } 1404 } 1405 } 1406 if (relatedExist) 1407 { 1408 modalProductSize = "xl"; 1409 } 1410 else 1411 { 1412 modalProductSize = "lg"; 1413 } 1414 1415 } 1416 @*<script type="module" src="/Files/Templates/Designs/Swift/Assets/js/swiffy-slider.js"></script> 1417 <script type="module"> 1418 swift.AssetLoader.Load('/Files/Templates/Designs/Swift/Assets/css/swiffy-slider.min.css', 'css'); 1419 </script>*@ 1420 <div class="modal-dialog theme plus-primary modal-@modalProductSize"> 1421 <div class="modal-content rounded-0 container"> 1422 <div class="row"> 1423 <div class="col-12 @modalProductCon p-0 border-bottom border-lg-none"> 1424 <div class="modal-header w-100 text-center"> 1425 <h3 class="modal-title w-100" id="cartNotificationModalTitel">@Translate("Følgende er tilføjet til indkøbslisten", "Følgende er tilføjet til indkøbslisten")</h3> 1426 <button type="button" class="btn-close position-absolute m-0 end-0 me-3 d-block d-xl-none" data-bs-dismiss="modal" aria-label="Close"></button> 1427 </div> 1428 <div id="cartNotificationModalBody" class="modal-body"> 1429 <div class="container-fluid"> 1430 <div class="row"> 1431 <div class="@(imageTheme) col-12" style="@imageOutlineStyle"> 1432 <div class="@(ratioCssClass) col-4 col-xl-6 m-auto position-relative" style="@ratioVariable"> 1433 @if (showBadges) 1434 { 1435 <div class="position-absolute top-0 left-0 p-1 p-lg-2" style="z-index: 2"> 1436 @{ 1437 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms) 1438 } 1439 </div> 1440 } 1441 <img loading="lazy" decoding="async" src="@imagePath" class="h-100 w-100 @(imageThemePadding)" style="object-fit: contain;" alt="@product.Name" id="cartNotificationModal_Image"> 1442 </div> 1443 </div> 1444 <div class="d-flex flex-wrap py-3 col-12 gap-md-3"> 1445 <div class="col-12 col-md-auto info"> 1446 <div class="fc-dark efa-mod"><strong id="cartNotificationModal_Name"></strong></div> 1447 <div class="fs-5 fw-bold fc-dark efa-mod" id="cartNotificationModal_Price"></div> 1448 <div class="fs-8 fc-grey efa-mod" id="cartNotificationModal_DeliveryCost"></div> 1449 <div class="fs-8 fc-grey efa-mod" id="cartNotificationModal_DeliveryName"></div> 1450 <div id="cartNotificationModal_Quantity"></div> 1451 </div> 1452 <div class="col-12 col-lg action align-self-end"> 1453 <div class="col-12 d-flex mt-3"> 1454 <a id="cartNotificationModal_Button" class="btn btn-primary flex-fill" href="">@Translate("Gå til indkøbslisten", "Gå til indkøbslisten")</a> 1455 </div> 1456 </div> 1457 </div> 1458 </div> 1459 </div> 1460 </div> 1461 </div> 1462 @if (relatedExist) 1463 { 1464 <div class="col-12 col-xl-5 p-0"> 1465 <div class="modal-header w-100 text-center"> 1466 <h3 class="modal-title text-center w-100">@Translate("Add-on products", "Tilbehør")</h3> 1467 <button type="button" class="btn-close position-absolute m-0 end-0 me-3 d-none d-xl-block" data-bs-dismiss="modal" aria-label="Close"></button> 1468 </div> 1469 <div class="modal-body"> 1470 <div id="addonsList_@product.Id" class="list row gy-3 overflow-auto"> 1471 @{ 1472 foreach (RelatedGroupViewModel relatedGroup in product.RelatedGroups.Where(x => x.Id == "Addon")) 1473 { 1474 foreach (ProductInfoViewModel relatedProduct in relatedGroup.Products) 1475 { 1476 <div class="elements col-6 col-xl-12 align-self-end"> 1477 @RenderAddons(relatedProduct, productCounter, product.Id) 1478 </div> 1479 productCounter += 1; 1480 } 1481 } 1482 1483 } 1484 </div> 1485 </div> 1486 </div> 1487 } 1488 </div> 1489 </div> 1490 </div> 1491 </div> 1492 } 1493 1494 @helper RenderAddons(ProductInfoViewModel relatedProduct, int productCounter, string productID) 1495 { 1496 var relProduct = relatedProduct.GetProduct(); 1497 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", ""); 1498 bool anonymousUser = Pageview.User == null; 1499 bool hidePrice = anonymousUsersLimitations.Contains("price") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHidePrices") && !Dynamicweb.Ecommerce.DynamicwebLiveIntegration.TemplatesHelper.IsWebServiceConnectionAvailable(); 1500 bool showFavoritesSelectorMasterProduct = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ShowFavoritesSelectorMasterProduct")) ? Convert.ToBoolean(Dynamicweb.Context.Current.Request.Form.Get("ShowFavoritesSelectorMasterProduct")) : false; 1501 1502 string ratio = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ImageAspectRatio")) ? Dynamicweb.Context.Current.Request.Form.Get("ImageAspectRatio") : ""; 1503 string ratioCssClass = ratio != "" ? "ratio" : ""; 1504 string ratioVariable = ratio != "" ? "--bs-aspect-ratio: " + ratio : ""; 1505 1506 string theme = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("Theme")) ? Dynamicweb.Context.Current.Request.Form.Get("Theme") : ""; 1507 string themePadding = theme != string.Empty ? "p-3" : string.Empty; 1508 string imageTheme = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ImageTheme")) ? Dynamicweb.Context.Current.Request.Form.Get("ImageTheme") : ""; 1509 string imageOutlineStyle = imageTheme == string.Empty ? "border: 1px solid transparent;" : string.Empty; 1510 string imageThemePadding = imageTheme != string.Empty ? "p-3" : string.Empty; 1511 string ContentPadding = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ContentPadding")) ? Dynamicweb.Context.Current.Request.Form.Get("ContentPadding") : ""; 1512 1513 string showPricesWithVat = Pageview.Area.EcomPricesWithVat.ToLower(); 1514 bool neverShowVat = string.IsNullOrEmpty(showPricesWithVat); 1515 1516 string variantIdForLink = !string.IsNullOrEmpty(relProduct.VariantId) ? $"&VariantID={relProduct.VariantId}" : ""; 1517 variantIdForLink = string.IsNullOrEmpty(variantIdForLink) && !string.IsNullOrEmpty(relProduct.DefaultVariantId) ? $"&VariantID={relProduct.DefaultVariantId}" : variantIdForLink; 1518 1519 string link = "Default.aspx?ID=" + GetPageIdByNavigationTag("Shop"); 1520 link += $"&GroupID={relProduct.PrimaryOrDefaultGroup.Id}"; 1521 link += $"&ProductID={relProduct.Id}"; 1522 link += variantIdForLink; 1523 link = Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl(link); 1524 1525 string imagePath = relProduct?.DefaultImage.Value.ToString() ?? ""; 1526 imagePath = "/Admin/Public/GetImage.ashx?image=" + imagePath + "&width=" + 350 + "&Format=WebP&Quality=70"; 1527 1528 string saleBadgeType = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeType")) ? Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeType") : ""; 1529 string saleBadgeCssClassName = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeCssClassName")) ? Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeCssClassName") : ""; 1530 string newBadgeCssClassName = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("NewBadgeCssClassName")) ? Dynamicweb.Context.Current.Request.Form.Get("NewBadgeCssClassName") : ""; 1531 int newPublicationDays = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("NewPublicationDays")) ? Convert.ToInt32(Dynamicweb.Context.Current.Request.Form.Get("NewPublicationDays")) : 0; 1532 string campaignBadgesValues = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("CampaignBadgesValues")) ? Dynamicweb.Context.Current.Request.Form.Get("CampaignBadgesValues") : ""; 1533 1534 var badgeParms = new Dictionary<string, object>(); 1535 badgeParms.Add("saleBadgeType", saleBadgeType); 1536 badgeParms.Add("saleBadgeCssClassName", saleBadgeCssClassName); 1537 badgeParms.Add("newBadgeCssClassName", newBadgeCssClassName); 1538 badgeParms.Add("campaignBadgesValues", campaignBadgesValues); 1539 badgeParms.Add("newPublicationDays", newPublicationDays); 1540 1541 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(saleBadgeCssClassName) && saleBadgeCssClassName != "none" ? true : false; 1542 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(newBadgeCssClassName) && newBadgeCssClassName != "none" ? true : false; 1543 DateTime createdDate = relProduct.Created.Value; 1544 bool showBadges = saleBadgeEnabled && relProduct.Discount.Price != 0 ? true : false; 1545 showBadges = (newBadgeEnabled && newPublicationDays == 0) || (newBadgeEnabled && (createdDate.AddDays(newPublicationDays) > DateTime.Now)) ? true : showBadges; 1546 showBadges = !string.IsNullOrEmpty(campaignBadgesValues) ? true : showBadges; 1547 1548 string disableAddToCart = (relProduct.StockLevel <= 0) ? "disabled" : ""; 1549 bool isNeverOutOfStock = relProduct.NeverOutOfstock; 1550 disableAddToCart = isNeverOutOfStock ? "" : disableAddToCart; 1551 1552 string iconPath = "/Files/icons/"; 1553 string addToCartIcon = Model.Item.GetRawValueString("Icon", iconPath + "shopping-cart.svg"); 1554 string addToCartLabel = !addToCartIcon.Contains("_none") ? "<span class=\"icon-2\">" + ReadFile(addToCartIcon) + "</span>" : ""; 1555 addToCartLabel += !addToCartIcon.Contains("_none") && !Model.Item.GetBoolean("HideButtonText") ? " " : ""; 1556 addToCartLabel += !Model.Item.GetBoolean("HideButtonText") ? Translate("Add to cart") : ""; 1557 string flexFill = Model.Item.GetRawValueString("HorizontalAlignment", "") == "full" ? "flex-fill" : ""; 1558 string buttonSize = Model.Item.GetRawValueString("ButtonSize", "regular"); 1559 string inputSize = string.Empty; 1560 string viewMoreIcon = Model.Item.GetRawValueString("ViewMoreIcon"); 1561 1562 string url = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("EasyFlowCart")); 1563 if (!url.Contains("LayoutTemplate")) 1564 { 1565 url += url.Contains("?") ? "&LayoutTemplate=Swift_MiniCart.cshtml" : "?LayoutTemplate=Swift_MiniCart.cshtml"; 1566 } 1567 string cartUrl = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("EasyFlowCart")); 1568 string imagePathHidden = relProduct?.DefaultImage.Value.ToString() ?? ""; 1569 imagePathHidden = "/Admin/Public/GetImage.ashx?image=" + imagePathHidden + "&width=" + 350 + "&Format=WebP&Quality=70"; 1570 1571 switch (buttonSize) 1572 { 1573 case "small": 1574 inputSize = " input-group-sm"; 1575 buttonSize = " btn-sm"; 1576 break; 1577 case "regular": 1578 buttonSize = string.Empty; 1579 break; 1580 case "large": 1581 inputSize = " input-group-lg"; 1582 buttonSize = " btn-lg"; 1583 break; 1584 } 1585 1586 string ecomCountryCode = !string.IsNullOrEmpty(Pageview.Area.EcomCountryCode) ? Pageview.Area.EcomCountryCode : ""; 1587 var countryVat = Services.Countries.GetCountry(ecomCountryCode).Vat > 0 ? Services.Countries.GetCountry(ecomCountryCode).Vat : 0; 1588 var productToShow = Services.Products.GetProductById(relProduct.Id, relProduct.VariantId, relProduct.LanguageId); 1589 var currency = Dynamicweb.Ecommerce.Common.Context.Currency.Code; 1590 string shipId = ecomCountryCode.IsNotNullOrEmpty() ? ShippingBasedOnProductSetting.GetShippingId(ecomCountryCode, productToShow) : ""; 1591 var shippingFeeAmount = string.Empty; 1592 1593 if (!string.IsNullOrEmpty(ecomCountryCode) && shipId != null) 1594 { 1595 shippingFeeAmount = ShippingBasedOnProductSetting.GetShippingAmount(shipId, countryVat, productToShow, currency); 1596 } 1597 1598 var deliveryCost = Translate("Levering fra") + " " + currency + " " + shippingFeeAmount; 1599 var deliveryName = ShippingBasedOnProductSetting.GetShippingName(shipId, relProduct.LanguageId); 1600 string stepQty = relProduct.PurchaseQuantityStep > 1 ? relProduct.PurchaseQuantityStep.ToString() : "1"; 1601 string valueQty = relProduct.PurchaseMinimumQuantity > relProduct.PurchaseQuantityStep ? relProduct.PurchaseMinimumQuantity.ToString() : stepQty; 1602 1603 relProduct.ProductFields.TryGetValue("ProductNextBackorderDate", out FieldValueViewModel backInStockDateValue); 1604 DateTime backInstockDate = backInStockDateValue.Value != null ? DateTime.Parse(backInStockDateValue.Value.ToString()) : DateTime.Today.AddDays(-1); 1605 bool hasBackInstockDate = backInStockDateValue != null && backInstockDate >= DateTime.Today; 1606 bool hasExpectedDelivery = relProduct.ExpectedDelivery != null && relProduct.ExpectedDelivery >= DateTime.Today; 1607 string expectedDeliveryDate = hasBackInstockDate ? backInstockDate.ToShortDateString() : relProduct.ExpectedDelivery?.ToShortDateString() ?? ""; 1608 1609 <div class="element w-100 d-flex"> 1610 1611 <div class="w-100 row m-0 align-items-end"> 1612 <div class="product-info col ps-lg-0"> 1613 <div class="col-6 col-xl-3 m-auto m-xl-0 image-container pe-2"> 1614 <img class="h-100 w-100" src="@imagePath" alt="" style="object-fit: cover;" /> 1615 </div> 1616 <div> 1617 <div><strong>@relProduct.Name</strong></div> 1618 @if (!hidePrice) 1619 { 1620 <div> 1621 <p class="h6 m-0"> 1622 @if (showPricesWithVat == "false" && !neverShowVat) 1623 { 1624 if (relProduct.Price.Price != relProduct.PriceBeforeDiscount.Price) 1625 { 1626 <span class="text-decoration-line-through opacity-75 me-1"> 1627 @relProduct.PriceBeforeDiscount.PriceWithoutVatFormatted 1628 </span> 1629 } 1630 } 1631 else 1632 { 1633 if (relProduct.Price.Price != relProduct.PriceBeforeDiscount.Price) 1634 { 1635 <span class="text-decoration-line-through opacity-75 me-1"> 1636 @relProduct.PriceBeforeDiscount.PriceFormatted 1637 </span> 1638 } 1639 } 1640 1641 @if (showPricesWithVat == "false" && !neverShowVat) 1642 { 1643 <span class="text-price fw-bold">@relProduct.Price.PriceWithoutVatFormatted</span> 1644 } 1645 else 1646 { 1647 <span class="text-price fw-bold">@relProduct.Price.PriceFormatted</span> 1648 } 1649 </p> 1650 1651 <div class="fs-8 mt-1 mb-2 text-start"> 1652 @if (!string.IsNullOrWhiteSpace(relProduct.StockStatus)) 1653 { 1654 <p class="mb-1">@relProduct.StockStatus</p> 1655 if (!string.IsNullOrWhiteSpace(relProduct.StockDeliveryText)) 1656 { 1657 <p class="mb-1">@relProduct.StockDeliveryText</p> 1658 } 1659 } 1660 @if (hasExpectedDelivery || hasBackInstockDate) 1661 { 1662 <div class="mb-1"> 1663 <span>@Translate("Expected in stock"): </span> 1664 <span>@expectedDeliveryDate</span> 1665 </div> 1666 } 1667 </div> 1668 1669 @*<div class="fs-8 text-black-50 text-start"> 1670 <p class="m-0">@Translate("Levering fra") @currency @shippingFeeAmount</p> 1671 </div>*@ 1672 @if (showPricesWithVat == "false" && !neverShowVat) 1673 { 1674 <small class="opacity-85 fst-normal">@relProduct.Price.PriceWithVatFormatted @Translate("Incl. VAT")</small> 1675 } 1676 1677 </div> 1678 } 1679 </div> 1680 </div> 1681 <div class="action col-auto px-3 ps-lg-0 w-100 w-lg-auto"> 1682 <form method="post" action="@url"> 1683 <input type="hidden" name="redirect" value="false"> 1684 <input type="hidden" name="ProductId" value="@relProduct.Id"> 1685 <input type="hidden" name="ProductName" value="@relProduct.Name"> 1686 <input type="hidden" name="ProductCurrency" value="@Dynamicweb.Ecommerce.Common.Context.Currency.Code"> 1687 <input type="hidden" name="ProductReferer" value="product_list_listview"> 1688 <input type="hidden" name="ProductPrice" value="@relProduct.Price.PriceFormatted"> 1689 <input type="hidden" name="cartcmd" value="add"> 1690 @* #38 EA: sbj *@ 1691 <input type="hidden" name="ProductPriceFormatted" value="@relProduct.Price.PriceFormatted"> 1692 <input type="hidden" name="ProductUnit" value="@Translate("stk.", "stk.")"> 1693 <input type="hidden" name="ProductTotalText" value="@Translate("I alt", "I alt")"> 1694 <input type="hidden" name="ProductDeliveryCost" value="@deliveryCost"> 1695 <input type="hidden" name="ProductDeliveryName" value="@deliveryName"> 1696 <input type="hidden" name="ProductImage" value="@imagePathHidden"> 1697 <input type="hidden" name="cartpage" value="@cartUrl"> 1698 @if (relProduct.StockLevel > 0) 1699 { 1700 <div class="d-flex flex-nowrap w-100 mb-2 bg-light efa-mod" style="height:40px;"> 1701 1702 <div class="d-flex align-items-center justify-content-center btn border-0" id="minus-btn" onclick="quantityBtn('Quantity_@(productID)_@(relProduct.Id)_@relProduct.VariantId', 'minus')" style="width:calc(100%/3);"> 1703 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-dash-lg" viewBox="0 0 16 16"> 1704 <path fill-rule="evenodd" d="M2 8a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11A.5.5 0 0 1 2 8Z" /> 1705 </svg> 1706 </div> 1707 <div class="d-flex align-items-center justify-content-center" style="width:calc(100%/3);"> 1708 <input id="Quantity_@(productID)_@(relProduct.Id)_@relProduct.VariantId" name="Quantity" value="1" step="1" min="1" class="form-control p-0 bg-light border-0 fc-green quantity_selector efa-mod" style="max-width:40px;" type="number" onkeydown="swift.Cart.UpdateOnEnterKey(event)" @disableAddToCart> 1709 </div> 1710 <div class="d-flex align-items-center justify-content-center btn border-0" id="plus-btn" onclick="quantityBtn('Quantity_@(productID)_@(relProduct.Id)_@relProduct.VariantId', 'plus')" style="width:calc(100%/3);"> 1711 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-plus-lg" viewBox="0 0 16 16"> 1712 <path fill-rule="evenodd" d="M8 2a.5.5 0 0 1 .5.5v5h5a.5.5 0 0 1 0 1h-5v5a.5.5 0 0 1-1 0v-5h-5a.5.5 0 0 1 0-1h5v-5A.5.5 0 0 1 8 2Z" /> 1713 </svg> 1714 </div> 1715 <script type="text/javascript"> 1716 function quantityBtn(id, symbol) { 1717 console.log(document.getElementById(id)); 1718 var idValue = document.getElementById(id).value; 1719 var value = parseInt(idValue, 10); 1720 var step = 1; 1721 value = isNaN(value) ? 0 : value; 1722 switch (symbol) { 1723 case "minus": 1724 value = value - step; 1725 break; 1726 case "plus": 1727 value = value + step; 1728 break; 1729 } 1730 console.log(value); 1731 if (value > 0) { 1732 document.getElementById(id).value = value; 1733 } 1734 //UpdateQuantity(); 1735 } 1736 </script> 1737 1738 </div> 1739 <a onclick="swift.Cart.Update(event)" data-container="productRel" class="btn btn-primary @(buttonSize) @flexFill w-100 w-lg-auto flex-fill js-add-to-cart-button addToCart" style="white-space: nowrap" @disableAddToCart title="@Translate("Add to cart")" id="AddToCartButton@(relProduct.Id)_@Pageview.CurrentParagraph.ID"> 1740 @if (!Model.Item.GetBoolean("HideButtonText")) 1741 { 1742 <span class="text-nowrap d-flex align-items-center justify-content-center gap-2"> 1743 @addToCartLabel 1744 </span> 1745 } 1746 else 1747 { 1748 @addToCartLabel 1749 } 1750 </a> 1751 } 1752 else 1753 { 1754 <div class="w-100 efa-mod" style="height:40px;"> 1755 @*<button name="notify-cookie" data-href="@link" type="button" class="btn btn-primary flex-fill h-100 js-add-to-cart-button" title="@Translate("Produkt notifikations label")" id="AddCookie@(relProduct.Id)"> 1756 <span class="icon-2 d-flex gap-2 align-items-center justify-content-center">@ReadFile(iconPath + "envelope.svg")</span> 1757 </button>*@ 1758 <a href="@link" class="btn btn-secondary flex-fill view-more-btn w-100"><div class="">@ReadFile(viewMoreIcon)<span>@Translate("View more")</span></div></a> 1759 </div> 1760 } 1761 </form> 1762 </div> 1763 </div> 1764 </div> 1765 } 1766 1767 @*@helper RenderModalRelatedProducts(ProductViewModel product) 1768 { 1769 <div id="cartNotificationModal_@(product.Id)" class="modal" tabindex="-1" aria-labelledby="cartNotificationModalTitel" aria-hidden="true"> 1770 @{ 1771 string modelId = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ModelID")) ? Dynamicweb.Context.Current.Request.Form.Get("ModelID") : product.Id; 1772 1773 string scrollBarForceMobile = Dynamicweb.Context.Current.Request.Form.Get("NavigationShowScrollbar") != string.Empty ? "--swiffy-slider-track-height:0.5rem !important;" : string.Empty; 1774 bool hideSliderNavigation = false; 1775 1776 int itemsShown = 3; 1777 1778 string imageTheme = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ImageTheme")) ? Dynamicweb.Context.Current.Request.Form.Get("ImageTheme") : ""; 1779 string imageOutlineStyle = imageTheme == string.Empty ? "border: 1px solid transparent;" : string.Empty; 1780 string imageThemePadding = imageTheme != string.Empty ? "p-3" : string.Empty; 1781 1782 string imagePath = product?.DefaultImage.Value.ToString() ?? ""; 1783 imagePath = "/Admin/Public/GetImage.ashx?image=" + imagePath + "&width=" + 350 + "&Format=WebP&Quality=70"; 1784 1785 string ratio = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ImageAspectRatio")) ? Dynamicweb.Context.Current.Request.Form.Get("ImageAspectRatio") : ""; 1786 string ratioCssClass = ratio != "" ? "ratio" : ""; 1787 string ratioVariable = ratio != "" ? "--bs-aspect-ratio: " + ratio : ""; 1788 1789 string saleBadgeType = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeType")) ? Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeType") : ""; 1790 string saleBadgeCssClassName = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeCssClassName")) ? Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeCssClassName") : ""; 1791 string newBadgeCssClassName = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("NewBadgeCssClassName")) ? Dynamicweb.Context.Current.Request.Form.Get("NewBadgeCssClassName") : ""; 1792 int newPublicationDays = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("NewPublicationDays")) ? Convert.ToInt32(Dynamicweb.Context.Current.Request.Form.Get("NewPublicationDays")) : 0; 1793 string campaignBadgesValues = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("CampaignBadgesValues")) ? Dynamicweb.Context.Current.Request.Form.Get("CampaignBadgesValues") : ""; 1794 1795 var badgeParms = new Dictionary<string, object>(); 1796 badgeParms.Add("saleBadgeType", saleBadgeType); 1797 badgeParms.Add("saleBadgeCssClassName", saleBadgeCssClassName); 1798 badgeParms.Add("newBadgeCssClassName", newBadgeCssClassName); 1799 badgeParms.Add("campaignBadgesValues", campaignBadgesValues); 1800 badgeParms.Add("newPublicationDays", newPublicationDays); 1801 1802 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(saleBadgeCssClassName) && saleBadgeCssClassName != "none" ? true : false; 1803 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(newBadgeCssClassName) && newBadgeCssClassName != "none" ? true : false; 1804 DateTime createdDate = product.Created.Value; 1805 1806 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false; 1807 showBadges = (newBadgeEnabled && newPublicationDays == 0) || (newBadgeEnabled && (createdDate.AddDays(newPublicationDays) > DateTime.Now)) ? true : showBadges; 1808 showBadges = !string.IsNullOrEmpty(campaignBadgesValues) ? true : showBadges; 1809 1810 var shopPageId = GetPageIdByNavigationTag("Shop"); 1811 1812 bool relatedExist = false; 1813 foreach (var relatedGroup in product.RelatedGroups) 1814 { 1815 if (relatedGroup.Id == "Addon") 1816 { 1817 if (relatedGroup.Products.Count > 0) 1818 { 1819 relatedExist = true; 1820 } 1821 } 1822 } 1823 1824 } 1825 <script type="module" src="/Files/Templates/Designs/Swift/Assets/js/swiffy-slider.js"></script> 1826 <script type="module"> 1827 swift.AssetLoader.Load('/Files/Templates/Designs/Swift/Assets/css/swiffy-slider.min.css', 'css'); 1828 </script> 1829 <div class="modal-dialog theme plus-primary modal-xl"> 1830 <div class="modal-content rounded-0"> 1831 <div class="modal-header w-100 text-center"> 1832 <h3 class="modal-title w-100" id="cartNotificationModalTitel">@Translate("Følgende er tilføjet til indkøbslisten", "Følgende er tilføjet til indkøbslisten")</h3> 1833 <button type="button" class="btn-close position-absolute m-0 end-0 me-3" data-bs-dismiss="modal" aria-label="Close"></button> 1834 </div> 1835 <div id="cartNotificationModalBody" class="modal-body"> 1836 <div class="container-fluid"> 1837 <div class="row"> 1838 <div class="col-12 col-md-6 col-lg-3 m-auto me-lg-0 @(imageTheme)" style="@imageOutlineStyle"> 1839 <div class="@(ratioCssClass) position-relative" style="@ratioVariable"> 1840 @if (showBadges) 1841 { 1842 <div class="position-absolute top-0 left-0 p-1 p-lg-2" style="z-index: 2"> 1843 @{ 1844 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms) 1845 } 1846 </div> 1847 } 1848 <img loading="lazy" decoding="async" src="" class="h-100 w-100 @(imageThemePadding)" style="object-fit: contain;" alt="" id="cartNotificationModal_Image"> 1849 </div> 1850 </div> 1851 <div class="col-12 col-md-6 col-lg-4 m-auto ms-lg-0 d-flex align-content-between flex-wrap"> 1852 <div class="col-12 ms-auto fc-dark efa-mod"><strong id="cartNotificationModal_Name"></strong></div> 1853 <div class="col-12 ms-auto"> 1854 <div class="col-12 ms-auto fs-5 fw-bold fc-dark efa-mod" id="cartNotificationModal_Price"></div> 1855 <div class="fs-8 fc-grey efa-mod text-start"> 1856 <p class="" id="cartNotificationModal_DeliveryCost"></p> 1857 </div> 1858 1859 <div class="col-12 d-flex flex-wrap mb-3 ms-auto" id="cartNotificationModal_Quantity"></div> 1860 <div class="col-12 d-flex mb-2"> 1861 <a id="cartNotificationModal_Button" class="btn btn-primary flex-fill" href="">@Translate("Gå til indkøbslisten", "Gå til indkøbslisten")</a> 1862 </div> 1863 <div class="col-12 d-flex"> 1864 <div class="btn btn-secondary col-12" data-bs-dismiss="modal">@Translate("Continue shopping")</div> 1865 </div> 1866 </div> 1867 </div> 1868 </div> 1869 </div> 1870 </div> 1871 1872 @if (relatedExist) 1873 { 1874 <div class="modal-footer d-none d-md-block d-lg-block d-xl-block" id="cartNotificationModal_Related"> 1875 <div class="modal-title w-100 text-center"> 1876 <h3>@Translate("Add-on products", "Tilbehør")</h3> 1877 </div> 1878 <div class="container-fluid"> 1879 <div class="row"> 1880 <div class="col-12 m-auto"> 1881 <div id="slider_@(modelId)" class="swiffy-slider slider-item-show@(itemsShown) slider-nav-visible" style="--swiffy-slider-nav-light:var(--swift-foreground-color); --swiffy-slider-nav-dark:var(--swift-background-color); @(scrollBarForceMobile)"> 1882 <ul class="slider-container"> 1883 @{ 1884 foreach (RelatedGroupViewModel relatedGroup in product.RelatedGroups.Where(x => x.Id == "Addon")) 1885 { 1886 foreach (ProductInfoViewModel relatedProduct in relatedGroup.Products) 1887 { 1888 <li> 1889 @RenderProduct(relatedProduct, product.Id) 1890 </li> 1891 } 1892 } 1893 } 1894 1895 </ul> 1896 <button type="button" title="@Translate("Previous slide")" class="slider-nav" style="z-index: 2;"> 1897 <span class="visually-hidden">@Translate("Previous slide")</span> 1898 </button> 1899 <button type="button" title="@Translate("Next slide")" class="slider-nav slider-nav-next" style="z-index: 2;"> 1900 <span class="visually-hidden">@Translate("Next slide")</span> 1901 </button> 1902 1903 </div> 1904 <script type="module"> 1905 swiffyslider.initSlider(document.querySelector('#slider_@(modelId)')); 1906 </script> 1907 </div> 1908 </div> 1909 </div> 1910 </div> 1911 } 1912 </div> 1913 </div> 1914 </div> 1915 }*@ 1916 1917 <script type="text/javascript"> 1918 1919 var notifyCookieBtn = document.getElementsByName("notify-cookie"); 1920 if (notifyCookieBtn) { 1921 for (let i = 0; i < notifyCookieBtn.length; i++) { 1922 notifyCookieBtn[i].addEventListener("click", function (event) { 1923 let link = notifyCookieBtn[i].getAttribute("data-href"), 1924 cName = "ProductNotifier", 1925 cValue = true, 1926 date = new Date(), 1927 expDays = 1; 1928 date.setTime(date.getTime() + (expDays * 24 * 60 * 60 * 1000)); 1929 const expires = "expires=" + date.toUTCString(); 1930 document.cookie = cName + "=" + cValue + "; " + expires + "; path=/"; 1931 1932 window.location.href = link; 1933 }); 1934 } 1935 } 1936 1937 document.addEventListener("update.swift.cart", function (event) { 1938 var data = Object.fromEntries(event.detail.formData.entries()); 1939 var mainProductId = data.MainProductId; 1940 1941 if (mainProductId != "None") { 1942 var openqs = '#cartNotificationModal_' + mainProductId; 1943 1944 var prodId = data.ProductId; 1945 1946 var price = data.ProductPrice; 1947 if (price.includes(",")) { 1948 price = price.replace(/,/g, "."); 1949 price = parseFloat(price).toFixed(2); 1950 } 1951 1952 var quantity = data.Quantity; 1953 var detailsPrice = data.ProductPrice; 1954 var totalprice = data.ProductPrice.includes(",") ? data.ProductPrice : detailsPrice.concat("", ",00");; 1955 if (quantity > 1) { 1956 var total = price * quantity; 1957 var totalpriceCalc = parseFloat(total).toFixed(2); 1958 totalpriceCalc = totalpriceCalc.toString(); 1959 if (totalpriceCalc.includes(".")) { 1960 totalprice = totalpriceCalc.replace(".", ","); 1961 } 1962 else { 1963 totalprice = totalpriceCalc.concat("", ",00"); 1964 } 1965 } 1966 1967 var details = data.ProductPrice.includes(",") ? data.ProductPrice : detailsPrice.concat("", ",00"); 1968 1969 document.querySelector(openqs).querySelector("#cartNotificationModal_Button").href = data.cartpage; 1970 document.querySelector(openqs).querySelector("#cartNotificationModal_Quantity").innerHTML = data.Quantity + " " + data.ProductUnit; 1971 document.querySelector(openqs).querySelector("#cartNotificationModal_Name").innerHTML = data.ProductName; 1972 document.querySelector(openqs).querySelector("#cartNotificationModal_Price").innerHTML = details; 1973 document.querySelector(openqs).querySelector("#cartNotificationModal_Image").src = data.ProductImage; 1974 document.querySelector(openqs).querySelector("#cartNotificationModal_Image").alt = data.ProductName; 1975 document.querySelector(openqs).querySelector("#cartNotificationModal_DeliveryCost").innerHTML = data.ProductDeliveryCost; 1976 document.querySelector(openqs).querySelector("#cartNotificationModal_DeliveryName").innerHTML = data.ProductDeliveryName; 1977 document.querySelector(openqs).querySelector("#cartNotificationModal_Related").classList.remove('d-md-block', 'd-lg-block', 'd-xl-block'); 1978 } 1979 else { 1980 var prodId = data.ProductId; 1981 mainProductId = prodId; 1982 1983 var price = data.ProductPrice; 1984 if (price.includes(",")) { 1985 price = price.replace(/,/g, "."); 1986 price = parseFloat(price).toFixed(2); 1987 } 1988 1989 var quantity = data.Quantity; 1990 var detailsPrice = data.ProductPrice; 1991 var totalprice = data.ProductPrice.includes(",") ? data.ProductPrice : detailsPrice.concat("", ",00");; 1992 if (quantity > 1) { 1993 var total = price * quantity; 1994 var totalpriceCalc = parseFloat(total).toFixed(2); 1995 totalpriceCalc = totalpriceCalc.toString(); 1996 if (totalpriceCalc.includes(".")) { 1997 totalprice = totalpriceCalc.replace(".", ","); 1998 } 1999 else { 2000 totalprice = totalpriceCalc.concat("", ",00"); 2001 } 2002 } 2003 2004 var qs = "#cartNotificationModal_" + prodId; 2005 const myModalAlternative = new bootstrap.Modal(qs) 2006 2007 var details = data.ProductPrice.includes(",") ? data.ProductPrice : detailsPrice.concat("", ",00"); 2008 2009 document.querySelector(qs).querySelector("#cartNotificationModal_Button").href = data.cartpage; 2010 document.querySelector(qs).querySelector("#cartNotificationModal_Quantity").innerHTML = data.Quantity + " " + data.ProductUnit; 2011 document.querySelector(qs).querySelector("#cartNotificationModal_Name").innerHTML = data.ProductName; 2012 document.querySelector(qs).querySelector("#cartNotificationModal_Price").innerHTML = details; 2013 document.querySelector(qs).querySelector("#cartNotificationModal_Image").src = data.ProductImage; 2014 document.querySelector(qs).querySelector("#cartNotificationModal_Image").alt = data.ProductName; 2015 document.querySelector(qs).querySelector("#cartNotificationModal_DeliveryCost").innerHTML = data.ProductDeliveryCost; 2016 document.querySelector(qs).querySelector("#cartNotificationModal_DeliveryName").innerHTML = data.ProductDeliveryName; 2017 if (document.querySelector(qs).querySelector("#cartNotificationModal_Related")) { 2018 if (!document.querySelector(qs).querySelector("#cartNotificationModal_Related").classList.contains('d-md-block', 'd-lg-block', 'd-xl-block')) { 2019 document.querySelector(qs).querySelector("#cartNotificationModal_Related").classList.add('d-md-block', 'd-lg-block', 'd-xl-block'); 2020 } 2021 } 2022 2023 myModalAlternative.show(); 2024 } 2025 let addonsListHeight = 0; 2026 console.log(mainProductId); 2027 let addonList = document.querySelector("#addonsList_" + mainProductId); 2028 console.log(addonList); 2029 let addonListElems = addonList.children; 2030 console.log(addonListElems); 2031 if (addonListElems.length > 3) { 2032 for (var i = 0; 3 > i; i++) { 2033 console.log(addonListElems[i].clientHeight); 2034 addonsListHeight = addonsListHeight + addonListElems[i].clientHeight + 16; 2035 } 2036 addonList.style.maxHeight = addonsListHeight + "px"; 2037 } 2038 2039 }); 2040 2041 </script> 2042 @* #38 EA: end *@ 2043 2044 2045 <script> 2046 dataLayer = window.dataLayer || []; 2047 dataLayer.push({ ecommerce: null }); 2048 dataLayer.push({ 2049 event: 'view_item_list', 2050 ecommerce: { 2051 currency: '@Dynamicweb.Ecommerce.Common.Context.Currency.Code', 2052 ecomm_pagetype: 'category', 2053 items: [ 2054 @foreach (ProductViewModel product in productList.Products) 2055 { 2056 <text>{ 2057 item_name: '@product.Name', 2058 item_id: '@product.Number', 2059 price: @product.Price.Price, 2060 quantity: 1, 2061 item_category: '@product.PrimaryOrDefaultGroup.Name', 2062 item_brand: 'PLUS', 2063 item_variant: '' 2064 },</text> 2065 } 2066 ] 2067 }, 2068 user_data: { 2069 first_name: '@(string.IsNullOrEmpty(CartId) ? "" : order?.DeliveryName)', 2070 last_name: '', 2071 email_address: '@(string.IsNullOrEmpty(CartId) ? "" : order?.CustomerEmail)', 2072 phone_number: '@(string.IsNullOrEmpty(CartId) ? "" : order?.CustomerPhone)', 2073 } 2074 }); 2075 </script> 2076
Error executing template "Designs/Swift/Paragraph/Swift_EA_ProductListLongDescription.cshtml"
System.Data.SqlClient.SqlException (0x80131904): A connection was successfully established with the server, but then an error occurred during the login process. (provider: SSL Provider, error: 0 - An existing connection was forcibly closed by the remote host.) ---> System.ComponentModel.Win32Exception (0x80004005): An existing connection was forcibly closed by the remote host
   ved System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)
   ved System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal& connection)
   ved System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection)
   ved System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
   ved System.Data.SqlClient.SqlConnection.TryOpenInner(TaskCompletionSource`1 retry)
   ved System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)
   ved System.Data.SqlClient.SqlConnection.Open()
   ved Dynamicweb.Data.DatabaseConnectionProvider.CreateConnection(Boolean open)
   ved Dynamicweb.Data.Database.CreateConnection()
   ved Dynamicweb.Data.Database.CreateDataReader(CommandBuilder commandBuilder, IDbConnection connection, IDbTransaction transaction, Int32 commandTimeout)
   ved Dynamicweb.Ecommerce.Products.ProductRepository.GetProductsBySql(CommandBuilder query, Boolean doRefactoring, Boolean bulkFill, Boolean useAssortments)
   ved Dynamicweb.Ecommerce.Products.ProductRepository.GetProductsByGroup(Group group, String productLanguageId, Boolean useAssortments)
   ved Dynamicweb.Ecommerce.ProductCatalog.ViewModelFactory.CreateView(ProductListViewModelSettings settings, String groupId)
   ved CompiledRazorTemplates.Dynamic.RazorEngine_3232352ecf244fed8d231c721efbde12.Execute() i C:\inetpub\wwwroot\plus-prod\Files\Templates\Designs\Swift\Paragraph\Swift_EA_ProductListLongDescription.cshtml:linje 40
   ved RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader)
   ved RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   ved RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer)
   ved RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   ved Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template)
   ved Dynamicweb.Rendering.TemplateRenderingService.Render(Template template)
   ved Dynamicweb.Rendering.Template.RenderRazorTemplate()
ClientConnectionId:0eb8b6c8-646f-4084-9ce2-c04286559bb1
Error Number:10054,State:0,Class:20

1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Frontend.Navigation 3 @using Dynamicweb.Ecommerce.ProductCatalog 4 @using Dynamicweb.Environment 5 6 @{ 7 ProductListViewModel productList = new ProductListViewModel(); 8 9 ProductListViewModelSettings productSetting = new ProductListViewModelSettings 10 { 11 LanguageId = Dynamicweb.Ecommerce.Common.Context.LanguageID, 12 CurrencyCode = Dynamicweb.Ecommerce.Common.Context.Currency.Code, 13 CountryCode = Dynamicweb.Ecommerce.Common.Context.Country.Code2, 14 ShopId = Pageview.Area.EcomShopId 15 }; 16 17 if (Dynamicweb.Context.Current.Items.Contains("ProductList")) 18 { 19 productList = (ProductListViewModel)Dynamicweb.Context.Current.Items["ProductList"]; 20 } 21 else if (Pageview.Item["DummyProductGroup"] != null) 22 { 23 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 24 ProductListViewModel groupList = pageViewModel.Item.GetValue("DummyProductGroup") != null ? pageViewModel.Item.GetValue("DummyProductGroup") as ProductListViewModel : new ProductListViewModel(); 25 26 if (groupList?.Group?.Id != null) 27 { 28 productList = ViewModelFactory.CreateView(productSetting, groupList.Group.Id); 29 Dynamicweb.Context.Current.Items.Add("ProductList", productList); 30 } 31 else 32 { 33 productList = ViewModelFactory.CreateView(productSetting, Dynamicweb.Ecommerce.Services.ProductGroups.GetGroups(Dynamicweb.Ecommerce.Common.Context.LanguageID).FirstOrDefault().Id); 34 35 Dynamicweb.Context.Current.Items.Add("ProductList", productList); 36 } 37 } 38 else if (Pageview.Item["DummyProductGroup"] == null) 39 { 40 productList = ViewModelFactory.CreateView(productSetting, Dynamicweb.Ecommerce.Services.ProductGroups.GetGroups(Dynamicweb.Ecommerce.Common.Context.LanguageID).FirstOrDefault().Id); 41 Dynamicweb.Context.Current.Items.Add("ProductList", productList); 42 } 43 44 string searchParameter = Dynamicweb.Context.Current.Request.GetString("q"); 45 46 bool hideTitle = Model.Item.GetBoolean("HideGroupTitle"); 47 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "display-4"); 48 49 var navigationSettings = new NavigationSettings(); 50 navigationSettings.StartLevel = 2; 51 navigationSettings.ExpandMode = ExpandMode.Path; 52 navigationSettings.Parameters.Add("HideBreadcrumb", true); 53 navigationSettings.Parameters.Add("TitleFontSize", titleFontSize); 54 55 //string breadcrumbContent = Navigation.RenderNavigation("Navigation/ProductGroupHeader.cshtml", navigationSettings); 56 //breadcrumbContent = searchParameter != "" ? "<h1 class=\"" + titleFontSize + "\">" + Translate("Search results for") + ": " + searchParameter + "</h1>" : breadcrumbContent; 57 58 bool hideDescription = Model.Item.GetBoolean("HideGroupDescription"); 59 //string groupDescription = productList?.Group?.Description != null ? productList.Group.Description : ""; 60 string groupLongDescription = ""; 61 string groupDescriptionHeaderColumn = ""; 62 63 var groupFields = productList.Group?.GroupFields; 64 if (groupFields != null) 65 { 66 foreach (var field in groupFields) 67 { 68 if (field.SystemName == "groupLongDescription" && !string.IsNullOrEmpty(field.Value.ToString())) 69 { 70 groupLongDescription = field.Value.ToString(); 71 } 72 if (field.SystemName == "groupHeader" && !string.IsNullOrEmpty(field.Value.ToString())) 73 { 74 groupDescriptionHeaderColumn = field.Value.ToString(); 75 } 76 } 77 } 78 79 //string groupDescription = productList?.Group?.Description != null ? productList.Group.Description : ""; 80 string descriptionFontSize = Model.Item.GetRawValueString("DescriptionFontSize", "fs-6"); 81 82 string contentPadding = Model.Item.GetRawValueString("ContentPadding", ""); 83 contentPadding = contentPadding == "none" ? "p-0" : contentPadding; 84 contentPadding = contentPadding == "small" ? "p-2 p-md-3" : contentPadding; 85 contentPadding = contentPadding == "large" ? "p-4 p-md-5" : contentPadding; 86 87 string maxWidth = Model.Item.GetRawValueString("TextReadability", "max-width-on"); 88 maxWidth = maxWidth == "max-width-on" ? " mw-75ch" : maxWidth; 89 maxWidth = maxWidth == "max-width-off" ? "" : maxWidth; 90 91 string layout = Model.Item.GetRawValueString("Layout", "text-start"); 92 string autoMargin = layout == "text-start" ? "my-auto" : "m-auto"; 93 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 94 } 95 96 @if (!hideTitle || !string.IsNullOrEmpty(groupLongDescription)) { 97 <div class="h-100 @(contentPadding)@(theme) item_@Model.Item.SystemName.ToLower()"> 98 <div class="@(layout) @(maxWidth) w-100 @autoMargin"> 99 100 @if (!hideDescription && !string.IsNullOrEmpty(groupDescriptionHeaderColumn)) { 101 <div> 102 <h2 class="@descriptionFontSize group-list-descr p-md-3"> 103 @groupDescriptionHeaderColumn 104 </h2> 105 </div> 106 } 107 @if (!hideDescription && !string.IsNullOrEmpty(groupLongDescription)) { 108 <div class="@descriptionFontSize group-list-descr p-md-3"> 109 @groupLongDescription 110 </div> 111 } 112 </div> 113 </div> 114 } else if (Pageview.IsVisualEditorMode) { 115 <div class="alert alert-dark m-0" role="alert"> 116 <span>@Translate("Product list info: The info paragraph will be shown here, if any")</span> 117 </div> 118 } 119