Error executing template "Designs/Swift/eCom/ProductCatalog/ProductViewDetail.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 (10054): An existing connection was forcibly closed by the remote host. at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection) at System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection) at System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions) at System.Data.SqlClient.SqlConnection.Open() at Dynamicweb.Data.DatabaseConnectionProvider.CreateConnection(Boolean open) at Dynamicweb.Data.Database.CreateConnection() at Dynamicweb.Data.Database.CreateDataReader(CommandBuilder commandBuilder, IDbConnection connection, IDbTransaction transaction, Int32 commandTimeout) at Dynamicweb.Ecommerce.Products.DetailRepository.GetInheritedDetailsBulk(List`1 productIds, String detailType, Boolean onlyDefault) at Dynamicweb.Ecommerce.Products.DetailService.GetPrimaryDetailsBulk(IEnumerable`1 productKeys, String detailType) at Dynamicweb.Ecommerce.ProductCatalog.ViewEngine.<>c__DisplayClass11_0.<BulkCreateView>b__4() at System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode) at System.Lazy`1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor) at System.Lazy`1.CreateValue() at Dynamicweb.Ecommerce.ProductCatalog.ViewEngine.GetDefaultImage(MediaViewModelSettings settings, String productId, String languageId, String variantId, Lazy`1 productImages, Lazy`1 details) at Dynamicweb.Ecommerce.ProductCatalog.ViewEngine.GetDefaultImage(MediaViewModelSettings settings, Product product, Lazy`1 productImages, Lazy`1 details) at Dynamicweb.Ecommerce.ProductCatalog.ViewEngine.<>c__DisplayClass11_2.<BulkCreateView>b__63() at System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode) at System.Lazy`1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor) at System.Lazy`1.CreateValue() at CompiledRazorTemplates.Dynamic.RazorEngine_0c351a913a2e46c0ac1fdf35daa14fe7.ExecuteAsync() at RazorEngine.Templating.TemplateBase.Run(ExecuteContext context, TextWriter reader) at RazorEngine.Templating.RazorEngineCore.RunTemplate(ICompiledTemplate template, TextWriter writer, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.DynamicWrapperService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass23_0.<Run>b__0(TextWriter writer) at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter) at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, Type modelType, Object model, DynamicViewBag viewBag) at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template) at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template) at Dynamicweb.Rendering.Template.RenderRazorTemplate() ClientConnectionId:559b7ce6-e72c-4b3f-b69c-1df50836d828 Error Number:10054,State:0,Class:20
1 @inherits ViewModelTemplate<ProductViewModel> 2 @using Dynamicweb.Rendering 3 @using Dynamicweb.Ecommerce.ProductCatalog 4 @using Dynamicweb.Core 5 @using Dynamicweb.Environment 6 @using System.Web 7 8 @{ 9 string metaDescription = string.IsNullOrEmpty(Model.MetaDescription) ? Model.Name : Model.MetaDescription; 10 string klaviyoID = Pageview.AreaSettings.GetString("KlaviyoPublicKey"); 11 12 string baseUrl = Dynamicweb.Context.Current.Request.Url.Scheme + "://" + Dynamicweb.Context.Current.Request.Url.Host; 13 14 15 Pageview.Meta.AddTag($"<meta property=\"og:image\" content=\"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{Model.DefaultImage.Value}\">"); 16 Pageview.Meta.AddTag($"<meta property=\"og:image:alt\" content=\"{Model.Name}\">"); 17 Pageview.Meta.AddTag($"<meta property=\"og:description\" content=\"{metaDescription}\">"); 18 19 Pageview.Meta.AddTag("twitter:image", Model.DefaultImage.Value); 20 Pageview.Meta.AddTag("twitter:image:alt", Model.Name); 21 Pageview.Meta.AddTag("twitter:description", metaDescription); 22 } 23 24 @{ 25 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 26 { 27 Dynamicweb.Context.Current.Items["ProductDetails"] = Model; 28 } 29 else 30 { 31 Dynamicweb.Context.Current.Items.Add("ProductDetails", Model); 32 } 33 34 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]); 35 if (isLazyLoadingForProductInfoEnabled) 36 { 37 string showPricesWithVat = Pageview.Area.EcomPricesWithVat.ToLower(); 38 bool neverShowVat = string.IsNullOrEmpty(showPricesWithVat); 39 bool hasVariantId = !string.IsNullOrEmpty(Model.VariantId); 40 string variantIdParam = hasVariantId ? $"/{Model.VariantId}" : ""; 41 string priceFilledProperties = $"Price,PriceFormatted{(showPricesWithVat == "false" && !neverShowVat ? ",PriceWithVat,PriceWithVatFormatted" : "")}"; 42 string productInfoFeed = $@"/dwapi/ecommerce/products/{Model.Id}{variantIdParam} 43 ?UserId={Converter.ToString(Pageview.User?.ID)} 44 &LanguageId={Pageview.Area.EcomLanguageId}&CurrencyCode={Pageview.Area.EcomCurrencyId}&CountryCode={Pageview.Area.EcomCountryCode}&ShopId={Pageview.Area.EcomShopId} 45 &FilledProperties=Id,Price,PriceBeforeDiscount,StockLevel,VariantInfo,NeverOutOfstock,Prices 46 &PriceSettings.ShowPricesWithVat={Pageview.Area.EcomPricesWithVat} 47 &PriceSettings.FilledProperties={priceFilledProperties} 48 &getproductinfo=true"; 49 Dynamicweb.Context.Current.Items["ProductInfoFeed"] = productInfoFeed; 50 51 <script type="module"> 52 swift.LiveProductInfo.init(); 53 </script> 54 } 55 56 if ((!string.IsNullOrWhiteSpace(klaviyoID))) 57 { 58 59 ProductViewModelSettings productSetting = new ProductViewModelSettings 60 { 61 LanguageId = Dynamicweb.Ecommerce.Common.Context.LanguageID, 62 CurrencyCode = Dynamicweb.Ecommerce.Common.Context.Currency.Code, 63 CountryCode = Dynamicweb.Ecommerce.Common.Context.Country.Code2, 64 ShopId = Pageview.Area.EcomShopId 65 }; 66 67 var productViewModel = ViewModelFactory.CreateView(productSetting, Model.Id, Model.VariantId); 68 69 string productLink = baseUrl + "/" + productViewModel.GetProductLink(GetPageIdByNavigationTag("Shop"), false); 70 71 string defaultProductImage = productViewModel.DefaultImage.Value; 72 73 defaultProductImage = baseUrl + "/" + defaultProductImage; 74 75 string categories = ""; 76 77 var i=0; 78 var count = Model.ProductCategories.Count(); 79 80 foreach (var category in Model.ProductCategories){ 81 categories += category.Value.Name; 82 83 if (++i != count){ 84 categories += ", "; 85 } 86 87 88 } 89 90 91 92 var groups = Model.ProductCategories; 93 94 string brandName = ""; 95 96 foreach (var group in groups) { 97 foreach (var field in group.Value.Fields){ 98 if (field.Value.Name == "Brand"){ 99 if (field.Value.Value != null){ 100 brandName = field.Value.Value.ToString(); 101 } 102 } 103 } 104 } 105 106 107 108 if ((!string.IsNullOrWhiteSpace(Converter.ToString(Pageview.User?.Email)))) 109 { 110 <script> 111 112 klaviyo.identify({ 113 '$email' : '@(Converter.ToString(Pageview.User?.Email))', 114 '$first_name' : '@(Converter.ToString(Pageview.User?.Name))' 115 }); 116 117 </script> 118 } 119 120 <script type="text/plain" data-cookieconsent="marketing"> 121 var item = { 122 "ProductName": "@Dynamicweb.Core.Encoders.HtmlEncoder.JavaScriptStringEncode(Model.Name)", 123 "ProductID": "@Model.Number", 124 "Categories": "@(categories)", 125 "ImageURL": "@defaultProductImage", 126 "URL": "@(productLink)", 127 "Brand": "@(brandName)", 128 "Price": @PriceViewModelExtensions.ToStringInvariant(Model.Price) 129 }; 130 klaviyo.push(["track", "Viewed Product", item]); 131 </script> 132 133 134 <script type="text/plain" data-cookieconsent="marketing"> 135 136 137 klaviyo.push(["trackViewedItem", { 138 "Title": "@Dynamicweb.Core.Encoders.HtmlEncoder.JavaScriptStringEncode(Model.Name)", 139 "ItemId": "@Model.Number", 140 "Categories": "@(categories)", 141 "ImageUrl": "@defaultProductImage", 142 "Url": "@(productLink)", 143 "Metadata": { 144 "Price": @PriceViewModelExtensions.ToStringInvariant(Model.Price) 145 } 146 }]); 147 </script> 148 149 } 150 } 151 152 <script> 153 gtag("event", "view_item", { 154 currency: "@Model.Price.CurrencyCode", 155 value: @PriceViewModelExtensions.ToStringInvariant(Model.Price), 156 items: [ 157 { 158 item_id: "@Model.Number", 159 item_name: "@Dynamicweb.Core.Encoders.HtmlEncoder.JavaScriptStringEncode(Model.Name)", 160 currency: "@Model.Price.CurrencyCode", 161 price: @PriceViewModelExtensions.ToStringInvariant(Model.Price) 162 } 163 ] 164 }); 165 </script> 166 167 <script> 168 window.addEventListener('load', function (event) { 169 swift.Video.init(); 170 }); 171 </script> 172
Error executing template "Designs/Swift/Paragraph/Swift_ProductDetailsImage.cshtml" System.ArgumentNullException: Value cannot be null. (Parameter 'source') at System.Linq.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument) at System.Linq.Enumerable.Where[TSource](IEnumerable`1 source, Func`2 predicate) at CompiledRazorTemplates.Dynamic.RazorEngine_a24c896461a4461c8bfca04a7445fcfb.ExecuteAsync() at RazorEngine.Templating.TemplateBase.Run(ExecuteContext context, TextWriter reader) at RazorEngine.Templating.RazorEngineCore.RunTemplate(ICompiledTemplate template, TextWriter writer, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.DynamicWrapperService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass23_0.<Run>b__0(TextWriter writer) at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter) at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, Type modelType, Object model, DynamicViewBag viewBag) at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template) at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template) at Dynamicweb.Rendering.Template.RenderRazorTemplate()
1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 @using Dynamicweb.Frontend 4 @using System.IO 5 @using System.Text.RegularExpressions; 6 7 @functions { 8 public ProductViewModel product { get; set; } = new ProductViewModel(); 9 public string galleryLayout { get; set; } 10 public string[] supportedImageFormats { get; set; } 11 public string[] supportedVideoFormats { get; set; } 12 public string[] supportedDocumentFormats { get; set; } 13 public string[] allSupportedFormats { get; set; } 14 15 public class RatioSettings 16 { 17 public string Ratio { get; set; } 18 public string CssClass { get; set; } 19 public string CssVariable { get; set; } 20 public string Fill { get; set; } 21 } 22 23 public RatioSettings GetRatioSettings(string size = "desktop") 24 { 25 var ratioSettings = new RatioSettings(); 26 27 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 28 ratio = ratio != "0" ? ratio : ""; 29 string cssClass = ratio != "" && ratio != "fill" ? " ratio" : ""; 30 string cssVariable = ratio != "" && ratio != "fill" ? "--bs-aspect-ratio: " + ratio : ""; 31 cssClass = ratio == "fill" && size == "mobile" ? " ratio" : cssClass; 32 cssVariable = ratio == "fill" && size == "mobile" ? "--bs-aspect-ratio: 66%" : cssVariable; 33 34 ratioSettings.Ratio = ratio; 35 ratioSettings.CssClass = cssClass; 36 ratioSettings.CssVariable = cssVariable; 37 ratioSettings.Fill = ratio == "fill" ? " h-100" : ""; 38 39 return ratioSettings; 40 } 41 42 public string GetArrowsColor() 43 { 44 var invertColor = Model.Item.GetBoolean("InvertModalArrowsColor"); 45 var arrowsColor = invertColor ? " carousel-dark" : string.Empty; 46 return arrowsColor; 47 } 48 49 public string GetThumbnailPlacement() 50 { 51 return Model.Item.GetRawValueString("ThumbnailPlacement", "bottom"); 52 } 53 54 public string GetThumbnailRowSettingCss() 55 { 56 switch (GetThumbnailPlacement()) 57 { 58 case "bottom": 59 return "d-flex flex-wrap"; 60 case "left": 61 return "d-flex flex-column order-first"; 62 case "right": 63 return "d-flex flex-column order-last"; 64 default: 65 return "d-flex flex-wrap"; 66 } 67 } 68 69 public Dictionary<string, object> GetVideoParams(MediaViewModel asset, string size) 70 { 71 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Name; 72 string type = GetVideoType(asset.Value); 73 bool openInModal = Model.Item.GetString("OpenVideoInModal") == "true" ? true : false; 74 bool autoPlay = Model.Item.GetBoolean("VideoAutoPlay"); 75 76 var videoParams = new Dictionary<string, object>(); 77 videoParams.Add("AssetName", asset.Name); 78 videoParams.Add("AssetVideoType", type); 79 videoParams.Add("AssetDisplayName", asset.DisplayName); 80 videoParams.Add("OpenVideoInModal", openInModal); 81 videoParams.Add("VideoAutoPlay", autoPlay); 82 videoParams.Add("Size", size); 83 videoParams.Add("Id", Model.ID); 84 return videoParams; 85 86 } 87 88 public string GetVideoType(string assetValue) 89 { 90 string type = assetValue.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "youtube" : string.Empty; 91 type = assetValue.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "vimeo" : type; 92 type = string.IsNullOrEmpty(type) ? "selfhosted" : type; 93 94 return type; 95 } 96 97 public string GetYoutubeScreenDump(string assetValue, string quality) 98 { 99 var regex = new Regex(@"(?:youtube\.com\/.*[\?&]v=|youtu\.be\/)([\w-]+)"); 100 Match match = regex.Match(assetValue); 101 string videoId = match.Success ? match.Groups[1].Value : string.Empty; 102 string youtubeThumbnail = $"https://img.youtube.com/vi/{videoId}/{quality}.jpg"; 103 return youtubeThumbnail; 104 } 105 } 106 107 @{ 108 ProductViewModel product = null; 109 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 110 { 111 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 112 } 113 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 114 { 115 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 116 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 117 118 if (productList?.Products is object) 119 { 120 product = productList.Products[0]; 121 } 122 } 123 } 124 125 @if (product is object) 126 { 127 @* Supported formats *@ 128 supportedImageFormats = new string[] { ".jpg", ".jpeg", ".webp", ".png", ".gif", ".bmp", ".tiff" }; 129 supportedVideoFormats = new string[] { "youtu.be", "youtube", "vimeo", ".mp4", ".webm" }; 130 supportedDocumentFormats = new string[] { ".pdf", ".docx", ".xlsx", ".ppt", "pptx" }; 131 allSupportedFormats = supportedImageFormats.Concat(supportedVideoFormats).Concat(supportedDocumentFormats).ToArray(); 132 133 @* Collect the assets *@ 134 var selectedAssetCategories = Model.Item.GetList("ImageAssets")?.GetRawValue().OfType<string>(); 135 bool includeImagePatternImages = Model.Item.GetBoolean("ImagePatternImages"); 136 137 @* Needed image data collection to support both DefaultImage, ImagePatterns and Image Assets *@ 138 string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : ""; 139 IEnumerable<MediaViewModel> assetsImages = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets); 140 assetsImages = assetsImages.OrderByDescending(x => x.Value.Equals(defaultImage)); 141 IEnumerable<MediaViewModel> assetsList = new MediaViewModel[] { }; 142 assetsList = assetsList.Union(assetsImages); 143 assetsList = includeImagePatternImages ? assetsList.Union(product.ImagePatternImages) : assetsList; 144 assetsList = includeImagePatternImages && assetsList.Count() == 0 ? assetsList.Append(product.DefaultImage) : assetsList; 145 146 bool defaultImageFallback = Model.Item.GetBoolean("DefaultImageFallback"); 147 bool showOnlyPrimaryImage = Model.Item.GetBoolean("ShowOnlyPrimaryImage"); 148 149 int totalAssets = 0; 150 if (showOnlyPrimaryImage == false) 151 { 152 foreach (MediaViewModel asset in assetsList) 153 { 154 var assetValue = asset.Value; 155 foreach (string format in allSupportedFormats) 156 { 157 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 158 { 159 totalAssets++; 160 } 161 } 162 } 163 } 164 165 if ((totalAssets == 0 && product.DefaultImage != null && selectedAssetCategories.Count() == 0) || (showOnlyPrimaryImage == true && product.DefaultImage != null) || totalAssets == 0 && defaultImageFallback) 166 { 167 assetsList = new List<MediaViewModel>() { product.DefaultImage }; 168 totalAssets = 1; 169 } 170 171 @* Theme settings *@ 172 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 173 174 var badgeParms = new Dictionary<string, object>(); 175 badgeParms.Add("size", "h5"); 176 badgeParms.Add("saleBadgeType", Model.Item.GetRawValue("SaleBadgeType")); 177 badgeParms.Add("saleBadgeCssClassName", Model.Item.GetRawValue("SaleBadgeDesign")); 178 badgeParms.Add("newBadgeCssClassName", Model.Item.GetRawValue("NewBadgeDesign")); 179 badgeParms.Add("newPublicationDays", Model.Item.GetInt32("NewPublicationDays")); 180 badgeParms.Add("campaignBadgesValues", Model.Item.GetList("CampaignBadges")?.GetRawValue().OfType<string>().ToList()); 181 182 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("SaleBadgeDesign")) && Model.Item.GetRawValueString("SaleBadgeDesign") != "none" ? true : false; 183 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("NewBadgeDesign")) && Model.Item.GetRawValueString("NewBadgeDesign") != "none" ? true : false; 184 DateTime createdDate = product.Created.Value; 185 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false; 186 showBadges = (newBadgeEnabled && Model.Item.GetInt32("NewPublicationDays") == 0) || (newBadgeEnabled && (createdDate.AddDays(Model.Item.GetInt32("NewPublicationDays")) > DateTime.Now)) ? true : showBadges; 187 showBadges = !string.IsNullOrEmpty(Model.Item.GetRawValueString("CampaignBadges")) ? true : showBadges; 188 189 @* Get assets from selected categories or get all assets *@ 190 if (totalAssets != 0) 191 { 192 int assetNumber = 0; 193 int thumbnailNumber = 0; 194 int modalAssetNumber = 0; 195 string thumbnailAxisCss = GetThumbnailPlacement() == "bottom" ? "flex-column" : string.Empty; 196 197 <div class="d-flex gap-3 h-100 @(thumbnailAxisCss) @(theme) item_@Model.Item.SystemName.ToLower()"> 198 <div id="SmallScreenImages_@Model.ID" class="carousel@(GetArrowsColor()) col position-relative" data-bs-ride="carousel"> 199 <div class="carousel-inner h-100"> 200 @foreach (MediaViewModel asset in assetsList) 201 { 202 var assetValue = asset.Value; 203 foreach (string format in allSupportedFormats) 204 { 205 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 206 { 207 string activeSlide = assetNumber == 0 ? "active" : ""; 208 209 <div class="carousel-item @activeSlide" data-bs-interval="99999"> 210 @{ 211 string size = "mobile"; 212 213 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 214 215 216 <div class="h-100 @(imageTheme)"> 217 @foreach (string imageFormat in supportedImageFormats) 218 { //Images 219 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 220 { 221 if (product is object) 222 { 223 string productName = product.Name; 224 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 225 string imageLinkPath = Dynamicweb.Context.Current.Server.UrlEncode(imagePath); 226 227 RatioSettings ratioSettings = GetRatioSettings(size); 228 229 var parms = new Dictionary<string, object>(); 230 parms.Add("alt", productName + asset.Keywords); 231 parms.Add("itemprop", "image"); 232 parms.Add("columns", Model.GridRowColumnCount); 233 parms.Add("eagerLoadNewImages", Model.Item.GetBoolean("DisableLazyLoading")); 234 parms.Add("doNotUseGetimage", Model.Item.GetBoolean("DisableGetImage")); 235 if (!string.IsNullOrEmpty(asset.DisplayName)) 236 { 237 parms.Add("title", asset.DisplayName); 238 } 239 240 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") 241 { 242 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 243 } 244 else 245 { 246 parms.Add("cssClass", "mw-100 mh-100"); 247 } 248 249 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 250 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@assetNumber"> 251 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 252 </div> 253 </a> 254 } 255 } 256 } 257 @foreach (string videoFormat in supportedVideoFormats) 258 { //Videos 259 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 260 { 261 if (Model.Item.GetString("OpenVideoInModal") == "true") 262 { 263 if (product is object) 264 { 265 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 266 267 string productName = product.Name; 268 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 269 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 270 271 RatioSettings ratioSettings = GetRatioSettings(size); 272 273 string type = GetVideoType(asset.Value); 274 275 string videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value, "maxresdefault") : string.Empty; 276 videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeUriString(asset.Value) : videoScreendumpPath; 277 string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : ""; 278 279 280 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable); cursor: pointer" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 281 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@assetNumber"> 282 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 283 @if (type != "selfhosted") 284 { 285 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@productName" @assetTitle class="@videoJsClass mw-100 mh-100" data-asset-value="@asset.Value" style="object-fit: cover;"> 286 } 287 else 288 { 289 string videoType = Path.GetExtension(asset.Value).ToLower(); 290 291 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 292 <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> 293 </video> 294 } 295 </div> 296 </div> 297 298 } 299 } 300 else 301 { 302 if (product is object) 303 { 304 var videoParams = GetVideoParams(asset, size); 305 @RenderPartial("Components/VideoPlayer.cshtml", new FileViewModel { Path = asset.Value}, videoParams); 306 307 } 308 } 309 } 310 } 311 @foreach (string documentFormat in supportedDocumentFormats) 312 { //Documents 313 if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) 314 { 315 if (product is object) 316 { 317 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 318 319 string productName = product.Name; 320 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 321 string imageLinkPath = imagePath; 322 323 RatioSettings ratioSettings = GetRatioSettings(size); 324 325 var parms = new Dictionary<string, object>(); 326 parms.Add("alt", productName + asset.Keywords); 327 parms.Add("itemprop", "image"); 328 parms.Add("fullwidth", true); 329 parms.Add("columns", Model.GridRowColumnCount); 330 if (!string.IsNullOrEmpty(asset.DisplayName)) 331 { 332 parms.Add("title", asset.DisplayName); 333 } 334 335 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") 336 { 337 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 338 } 339 else 340 { 341 parms.Add("cssClass", "mw-100 mh-100"); 342 } 343 344 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download alt="@Translate("Download")"> 345 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 346 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 347 @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) 348 { 349 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 350 } 351 </div> 352 </a> 353 } 354 355 } 356 } 357 </div> 358 } 359 360 361 </div> 362 assetNumber++; 363 } 364 } 365 } 366 </div> 367 @if (showBadges) 368 { 369 <div class="position-absolute top-0 left-0 p-2 p-lg-3"> 370 @{@RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms)} 371 </div> 372 } 373 374 </div> 375 376 @if (totalAssets > 1) 377 { 378 <div class="@(GetThumbnailRowSettingCss()) gap-3" id="SmallScreenImagesThumbnails_@Model.ID"> 379 @foreach (MediaViewModel asset in assetsList) 380 { 381 var assetValue = asset.Value; 382 string assetName = asset.Name; 383 assetName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 384 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : null; 385 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 386 387 string imagePath = Dynamicweb.Context.Current.Server.UrlEncode(assetValue); 388 imagePath = assetValue.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "https://img.youtube.com/vi/" + assetValue.Substring(assetValue.LastIndexOf('/') + 1) + "/mqdefault.jpg" : imagePath; 389 string imagePathThumb = assetValue.StartsWith("/Files/", StringComparison.OrdinalIgnoreCase) ? imagePath.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) < 0 && imagePath.IndexOf(".mp4", StringComparison.OrdinalIgnoreCase) < 0 ? $"/Admin/Public/GetImage.ashx?image={imagePath}&width=180&format=webp" : imagePath : assetValue; 390 391 RatioSettings ratioSettings = GetRatioSettings("desktop"); 392 393 <div class="border outline-none @(ratioSettings.CssClass)" style="@(ratioSettings.CssVariable); cursor: pointer; min-width: 7rem; max-width: 8rem;" data-bs-target="#SmallScreenImages_@Model.ID" data-bs-slide-to="@thumbnailNumber"> 394 @foreach (string imageFormat in supportedImageFormats) 395 { //Images 396 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 397 { 398 <img src="@imagePathThumb" alt="@assetName" @assetTitle class="p-0 p-lg-1 w-100 h-100" style="object-fit: contain;"> 399 400 thumbnailNumber++; 401 } 402 } 403 404 @foreach (string videoFormat in supportedVideoFormats) 405 { //Videos 406 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 407 { 408 409 string type = GetVideoType(asset.Value); 410 411 string videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value, "mqdefault") : ""; 412 videoScreendumpPath = type == "vimeo" ? string.Empty : videoScreendumpPath; 413 videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeUriString(asset.Value) : videoScreendumpPath; 414 string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : string.Empty; 415 416 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 417 418 if (type != "selfhosted") 419 { 420 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@assetTitle" @assetTitle class="@videoJsClass mw-100 mh-100" data-asset-value="@asset.Value" style="object-fit: cover;" /> 421 } 422 else 423 { 424 string videoType = Path.GetExtension(asset.Value).ToLower(); 425 426 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 427 <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> 428 </video> 429 } 430 431 thumbnailNumber++; 432 } 433 } 434 435 @foreach (string documentFormat in supportedDocumentFormats) 436 { //Documents 437 if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) 438 { 439 <a href="@assetValue" class="ratio ratio-4x3 border outline-none" style="cursor: pointer; min-width: 7rem; max-width: 8rem;" download title="@asset.Value"> 440 @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) 441 { 442 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 443 <div class="icon-3 position-absolute text-light" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 444 </div> 445 <img src="@imagePathThumb" alt="@assetName" @assetTitle class="p-0 p-lg-1 mw-100 mh-100" style="object-fit: cover;"> 446 } 447 else 448 { 449 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 450 <div class="icon-3 position-absolute" style="z-index: 1">@ReadFile(iconPath + "file-text.svg")</div> 451 </div> 452 } 453 </a> 454 455 thumbnailNumber++; 456 } 457 } 458 </div> 459 } 460 </div> 461 } 462 </div> 463 464 @* Modal with slides *@ 465 <div class="modal fade swift_products-details-images-modal" id="modal_@Model.ID" tabindex="-1" aria-labelledby="productDetailsGalleryModalTitle_@Model.ID" aria-hidden="true"> 466 <div class="modal-dialog modal-dialog-centered modal-xl"> 467 <div class="modal-content"> 468 <div class="modal-header visually-hidden"> 469 <h5 class="modal-title" id="productDetailsGalleryModalTitle_@Model.ID">@product.Title</h5> 470 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 471 </div> 472 <div class="modal-body p-2 p-lg-3 h-100"> 473 <div id="ModalCarousel_@Model.ID" class="carousel@(GetArrowsColor()) h-100" data-bs-ride="carousel"> 474 <div class="carousel-inner h-100 @theme"> 475 @foreach (MediaViewModel asset in assetsList) 476 { 477 var assetValue = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 478 foreach (string supportedFormat in supportedImageFormats.Concat(supportedVideoFormats).ToArray()) 479 { 480 if (assetValue.IndexOf(supportedFormat, StringComparison.OrdinalIgnoreCase) >= 0) 481 { 482 string imagePath = assetValue; 483 string activeSlide = modalAssetNumber == 0 ? "active" : ""; 484 485 var parms = new Dictionary<string, object>(); 486 parms.Add("cssClass", "d-block mw-100 mh-100 m-auto"); 487 parms.Add("fullwidth", true); 488 parms.Add("columns", Model.GridRowColumnCount); 489 490 <div class="carousel-item @activeSlide h-100" data-bs-interval="99999"> 491 @foreach (string imageFormat in supportedImageFormats) 492 { //Images 493 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 494 { 495 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 496 } 497 } 498 499 @foreach (string videoFormat in supportedVideoFormats) 500 { //Videos 501 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 502 { 503 if (product is object) 504 { 505 var videoParams = GetVideoParams(asset, "modal"); 506 @RenderPartial("Components/VideoPlayer.cshtml", new FileViewModel { Path = asset.Value }, videoParams) 507 } 508 } 509 } 510 </div> 511 modalAssetNumber++; 512 } 513 } 514 } 515 <button class="carousel-control-prev carousel-control-area" type="button" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide="prev"> 516 <span class="carousel-control-prev-icon" aria-hidden="true"></span> 517 <span class="visually-hidden">@Translate("Previous")</span> 518 </button> 519 <button class="carousel-control-next carousel-control-area" type="button" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide="next"> 520 <span class="carousel-control-next-icon" aria-hidden="true"></span> 521 <span class="visually-hidden">@Translate("Next")</span> 522 </button> 523 </div> 524 </div> 525 </div> 526 </div> 527 </div> 528 </div> 529 } 530 else if (Pageview.IsVisualEditorMode) 531 { 532 RatioSettings ratioSettings = GetRatioSettings("desktop"); 533 534 <div class="h-100 @theme"> 535 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> 536 <img src="/Files/Images/missing_image.jpg" loading="lazy" decoding="async" class="mh-100 mw-100" style="object-fit: cover;"> 537 </div> 538 </div> 539 } 540 } 541 else if (Pageview.IsVisualEditorMode) 542 { 543 <div class="alert alert-dark m-0">@Translate("No products available")</div> 544 } 545 546 547 548
Error executing template "Designs/Swift/Paragraph/Swift_ProductBadges.cshtml" System.NullReferenceException: Object reference not set to an instance of an object. at CompiledRazorTemplates.Dynamic.RazorEngine_e87ca39d69f3488ca934646f392f311b.ExecuteAsync() at RazorEngine.Templating.TemplateBase.Run(ExecuteContext context, TextWriter reader) at RazorEngine.Templating.RazorEngineCore.RunTemplate(ICompiledTemplate template, TextWriter writer, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.DynamicWrapperService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass23_0.<Run>b__0(TextWriter writer) at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter) at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, Type modelType, Object model, DynamicViewBag viewBag) at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template) at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template) at Dynamicweb.Rendering.Template.RenderRazorTemplate()
1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Frontend 3 @using Dynamicweb.Content 4 @using Dynamicweb.Ecommerce.ProductCatalog 5 6 @{ 7 ProductViewModel product = null; 8 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 9 { 10 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 11 } 12 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 13 { 14 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 15 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 16 17 if (productList?.Products is object) 18 { 19 product = productList.Products[0]; 20 } 21 } 22 } 23 24 @if (product is object) { 25 var badgeParms = new Dictionary<string, object>(); 26 badgeParms.Add("size", "h7"); 27 badgeParms.Add("saleBadgeType", Model.Item.GetRawValue("SaleBadgeType")); 28 badgeParms.Add("saleBadgeCssClassName", Model.Item.GetRawValue("SaleBadgeDesign")); 29 badgeParms.Add("newBadgeCssClassName", Model.Item.GetRawValue("NewBadgeDesign")); 30 badgeParms.Add("newPublicationDays", Model.Item.GetInt32("NewPublicationDays")); 31 badgeParms.Add("campaignBadgesValues", Model.Item.GetList("CampaignBadges")?.GetRawValue().OfType<string>().ToList()); 32 33 string badgeSize = Model.Item.GetRawValueString("BadgeSize", "fs-2"); 34 string horizontalAlign = Model.Item.GetRawValueString("HorizontalAlignment", ""); 35 horizontalAlign = horizontalAlign == "center" ? "text-center" : horizontalAlign; 36 horizontalAlign = horizontalAlign == "end" ? "text-end" : horizontalAlign; 37 38 Dictionary<string, ParagraphInfoViewModel> badgeConfigurations; 39 List<string> campaignBadgesValues = Model.Item.GetRawValueString("CampaignBadges") != null ? Model.Item.GetList("CampaignBadges")?.GetRawValue().OfType<string>().ToList() : new List<string>(); 40 41 var productObject = Dynamicweb.Ecommerce.Services.Products.GetProductById(product.Id, "", Pageview.Area.EcomLanguageId); 42 43 double defaultPrice = productObject.DefaultPrice; 44 45 defaultPrice = defaultPrice + (defaultPrice * product.Price.VATPercent / 100); 46 47 //double beforePriceCustomDouble = 0; 48 //var ProductDefaultPriceField = productObject.ProductFieldValues.GetProductFieldValue("ProductDefaultPrice"); 49 50 //if (ProductDefaultPriceField != null){ 51 // beforePriceCustomDouble = Math.Round(Convert.ToDouble(ProductDefaultPriceField.Value), 2); 52 //} 53 54 55 56 if (Dynamicweb.Context.Current.Items.Contains("badgeConfigurations")) 57 { 58 badgeConfigurations = (Dictionary<string, ParagraphInfoViewModel>)Dynamicweb.Context.Current.Items["badgeConfigurations"]; 59 } 60 else 61 { 62 var badgesPage = Pageview.AreaSettings.GetLink("EcommerceBadgesPage") != null ? Pageview.AreaSettings.GetLink("EcommerceBadgesPage").PageId : 0; 63 var allBadges = badgesPage != 0 ? Dynamicweb.Content.Services.Paragraphs.GetParagraphsByPageId(badgesPage) : null; 64 65 badgeConfigurations = new Dictionary<string, ParagraphInfoViewModel>(); 66 foreach (Paragraph badge in allBadges) 67 { 68 var paragraphviewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreateParagraphInfoViewModel(badge); 69 string cssClassName = paragraphviewModel.Item.GetString("CssClassName").Trim().ToLower(); 70 if (!badgeConfigurations.ContainsKey(cssClassName)) 71 { 72 badgeConfigurations.Add(cssClassName, paragraphviewModel); 73 } 74 } 75 Dynamicweb.Context.Current.Items.Add("badgeConfigurations", badgeConfigurations); 76 } 77 78 int badgesCount = 0; 79 if (badgeConfigurations.Any()) 80 { 81 foreach (string campaign in campaignBadgesValues) 82 { 83 if (!string.IsNullOrEmpty(campaign)) 84 { 85 86 FieldValueViewModel availableCampaignsObject; 87 product.ProductFields.TryGetValue("Campaign", out availableCampaignsObject); 88 89 if (availableCampaignsObject != null) 90 { 91 string campaignType = string.Empty; 92 93 if (badgeConfigurations.ContainsKey(campaign)) 94 { 95 ParagraphInfoViewModel paragraphviewModel; 96 if (badgeConfigurations.TryGetValue(campaign, out paragraphviewModel)) 97 { 98 campaignType = paragraphviewModel.Item.GetRawValueString("CampaignType"); 99 } 100 } 101 102 List<FieldOptionValueViewModel> availableCampaigns = (List<FieldOptionValueViewModel>)availableCampaignsObject.Value; 103 104 foreach (FieldOptionValueViewModel availableOption in availableCampaigns) 105 { 106 if (campaignType == availableOption.Value) 107 { 108 badgesCount++; 109 break; 110 } 111 } 112 } 113 } 114 } 115 } 116 117 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("SaleBadgeDesign")) && Model.Item.GetRawValueString("SaleBadgeDesign") != "none" ? true : false; 118 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("NewBadgeDesign")) && Model.Item.GetRawValueString("NewBadgeDesign") != "none" ? true : false; 119 DateTime createdDate = product.Created.Value; 120 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false; 121 122 showBadges = saleBadgeEnabled && defaultPrice != 0 ? true : showBadges; 123 124 showBadges = (newBadgeEnabled && Model.Item.GetInt32("NewPublicationDays") == 0) || (newBadgeEnabled && (createdDate.AddDays(Model.Item.GetInt32("NewPublicationDays")) > DateTime.Now)) ? true : showBadges; 125 showBadges = (!string.IsNullOrEmpty(Model.Item.GetRawValueString("CampaignBadges")) && badgesCount != 0) ? true : showBadges; 126 127 128 if (showBadges) 129 { 130 <div class="@badgeSize @horizontalAlign item_@Model.Item.SystemName.ToLower()"> 131 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms) 132 </div> 133 } 134 else if (Pageview.IsVisualEditorMode) 135 { 136 <span class="badge bg-success text-light rounded-0">@Translate("Badge example")</span> 137 } 138 } 139 else if (Pageview.IsVisualEditorMode) 140 { 141 <span class="badge bg-success text-light rounded-0">@Translate("Badge example")</span> 142 } 143 144
Error executing template "Designs/Swift/Paragraph/Swift_ProductPrice.cshtml" System.NullReferenceException: Object reference not set to an instance of an object. at CompiledRazorTemplates.Dynamic.RazorEngine_0398cc17cd9d4596bf0a2dddf60b726e.ExecuteAsync() at RazorEngine.Templating.TemplateBase.Run(ExecuteContext context, TextWriter reader) at RazorEngine.Templating.RazorEngineCore.RunTemplate(ICompiledTemplate template, TextWriter writer, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.DynamicWrapperService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass23_0.<Run>b__0(TextWriter writer) at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter) at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, Type modelType, Object model, DynamicViewBag viewBag) at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template) at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template) at Dynamicweb.Rendering.Template.RenderRazorTemplate()
1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 4 @{ 5 ProductViewModel product = null; 6 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 7 { 8 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 9 } 10 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 11 { 12 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 13 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 14 15 if (productList?.Products is object) 16 { 17 product = productList.Products[0]; 18 } 19 } 20 21 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", ""); 22 bool anonymousUser = Pageview.User == null; 23 bool isErpConnectionDown = !Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsWebServiceConnectionAvailable"]); 24 bool hidePrice = anonymousUsersLimitations.Contains("price") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHidePrices") && isErpConnectionDown; 25 26 bool productIsDiscontinued = product is object && product.Discontinued; 27 bool doNotShowPriceIfProductIsDiscontinued = false; 28 if (productIsDiscontinued){doNotShowPriceIfProductIsDiscontinued = Model.Item.GetBoolean("DoNotShowPriceIfProductIsDiscontinued");} 29 var isDiscontinued = productIsDiscontinued && doNotShowPriceIfProductIsDiscontinued; 30 31 // bool doNotShowPriceIfProductIsDiscontinued = Model.Item.GetBoolean("DoNotShowPriceIfProductIsDiscontinued"); 32 33 //var ProductDefaultPriceField = productObject.ProductFieldValues.GetProductFieldValue("ProductDefaultPrice"); 34 35 //double beforePriceCustomDouble = 0; 36 37 //if (ProductDefaultPriceField != null){ 38 // beforePriceCustomDouble = Math.Round(Convert.ToDouble(ProductDefaultPriceField.Value), 2); 39 //} 40 41 42 43 44 45 } 46 47 @if (product is object && !hidePrice && !isDiscontinued) { 48 var productObject = Dynamicweb.Ecommerce.Services.Products.GetProductById(product.Id, "", Pageview.Area.EcomLanguageId); 49 double defaultPrice = productObject.DefaultPrice; 50 defaultPrice = defaultPrice + (defaultPrice * product.Price.VATPercent / 100); 51 52 bool showInformativePrice = Model.Item.GetBoolean("ShowInformativePrice"); 53 string unitId = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("UnitId")) ? Dynamicweb.Context.Current.Request.Form.Get("UnitId") : string.Empty; 54 55 string priceFontSize = Model.Item.GetRawValueString("PriceSize", "fs-2"); 56 string horizontalAlign = Model.Item.GetRawValueString("HorizontalAlignment", ""); 57 string layout = Model.Item.GetRawValueString("Layout", "horizontal"); 58 string textAlign = horizontalAlign == "center" ? "text-center" : string.Empty; 59 textAlign = horizontalAlign == "end" ? "text-end" : textAlign; 60 61 horizontalAlign = horizontalAlign == "center" && layout == "horizontal" ? "justify-content-center" : horizontalAlign; 62 horizontalAlign = horizontalAlign == "end" && layout == "horizontal" ? "justify-content-end" : horizontalAlign; 63 horizontalAlign = horizontalAlign == "center" && layout == "vertical" ? "align-items-center" : horizontalAlign; 64 horizontalAlign = horizontalAlign == "end" && layout == "vertical" ? "align-items-end" : horizontalAlign; 65 66 string flexDirection = layout == "horizontal" ? string.Empty : "flex-column"; 67 string flexGap = layout == "horizontal" ? "gap-3" : string.Empty; 68 string order = layout == "horizontal" ? string.Empty : "order-2"; 69 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? "theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 70 theme = GetViewParameter("theme") != null ? GetViewParameterString("theme") : theme; 71 72 string contentPadding = Model.Item.GetRawValueString("ContentPadding", ""); 73 contentPadding = contentPadding == "none" ? "p-0" : contentPadding; 74 contentPadding = contentPadding == "small" ? "p-1 px-md-2 py-md-1" : contentPadding; 75 contentPadding = contentPadding == "large" ? "p-2 px-md-3 py-md-2" : contentPadding; 76 77 string showPricesWithVat = Pageview.Area.EcomPricesWithVat.ToLower(); 78 bool neverShowVat = string.IsNullOrEmpty(showPricesWithVat); 79 80 string priceMin = ""; 81 string priceMax = ""; 82 83 string liveInfoClass = ""; 84 string productInfoFeed = ""; 85 86 var priceMatrix = Dynamicweb.Ecommerce.Services.Prices.GetByProductId(product.Id); 87 var beautyClubPrice = priceMatrix.FirstOrDefault(p => p.UserGroupId == "1165"); 88 var campaignPrice = priceMatrix.FirstOrDefault(p => p.ProductId == product.Id); 89 if (beautyClubPrice != null) 90 { 91 if (product.Price.Price < beautyClubPrice.Amount) 92 { 93 beautyClubPrice = null; 94 } 95 } 96 97 string currency = product.Price.CurrencyCode; 98 var dwCurrency = Dynamicweb.Ecommerce.Services.Currencies.GetCurrency(currency); 99 if (dwCurrency != null) 100 { 101 if (dwCurrency.UseCurrencyCodeForFormat) 102 { 103 currency = dwCurrency.Code; 104 } 105 else 106 { 107 currency = dwCurrency.Symbol; 108 } 109 } 110 111 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]); 112 if (isLazyLoadingForProductInfoEnabled) 113 { 114 if (Dynamicweb.Context.Current.Items.Contains("ProductInfoFeed")) 115 { 116 productInfoFeed = Dynamicweb.Context.Current.Items["ProductInfoFeed"]?.ToString(); 117 if (!string.IsNullOrEmpty(productInfoFeed)) 118 { 119 productInfoFeed = $"data-product-info-feed=\"{productInfoFeed}\""; 120 } 121 } 122 liveInfoClass = "js-live-info"; 123 } 124 125 <div class="@textAlign @liveInfoClass item_@Model.Item.SystemName.ToLower()" data-product-id="@product.Id" data-variant-id="@product.VariantId" @productInfoFeed> 126 @if (showInformativePrice && product.PriceInformative.Price != 0) 127 { 128 <div class="opacity-50"> 129 <span>@Translate("RRP") </span> 130 <span class="text-decoration-line-through text-price">@product.PriceInformative.PriceFormatted</span> 131 </div> 132 } 133 <div class="@priceFontSize m-0 d-flex flex-wrap @flexDirection @flexGap @horizontalAlign" style="row-gap: 0 !important" itemprop="offers" itemscope itemtype="https://schema.org/Offer"> 134 <span itemprop="priceCurrency" content="@product.Price.CurrencyCode" class="d-none"></span> 135 136 137 @if (showPricesWithVat == "false" && !neverShowVat) 138 { 139 if (isLazyLoadingForProductInfoEnabled && !Pageview.IsVisualEditorMode) 140 { 141 <span itemprop="price" content="" class="d-none"></span> 142 <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> 143 } 144 else 145 { 146 string beforePrice = !string.IsNullOrEmpty(unitId) ? product.GetPrice(unitId).PriceBeforeDiscount.PriceWithoutVatFormatted : product.PriceBeforeDiscount.PriceWithoutVatFormatted; 147 148 <span itemprop="price" content="@product.Price.PriceWithoutVat" class="d-none"></span> 149 if (product.Price.Price != product.PriceBeforeDiscount.Price) 150 { 151 <span class="text-decoration-line-through opacity-75 @order">@beforePrice</span> 152 } 153 } 154 } 155 else 156 { 157 if (isLazyLoadingForProductInfoEnabled && !Pageview.IsVisualEditorMode) 158 { 159 <span itemprop="price" content="" class="d-none"></span> 160 <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> 161 } 162 else 163 { 164 string beforePrice = !string.IsNullOrEmpty(unitId) ? product.GetPrice(unitId).PriceBeforeDiscount.PriceFormatted : product.PriceBeforeDiscount.PriceFormatted; 165 166 <span itemprop="price" content="@product.Price.Price" class="d-none"></span> 167 168 //if (product.Price.Price != beforePriceCustomDouble && product.Price.Price < beforePriceCustomDouble){ 169 170 if (product.Price.Price < defaultPrice && beautyClubPrice != null) 171 { 172 if (campaignPrice != null && campaignPrice.Amount == beautyClubPrice.Amount) 173 { 174 <span class="@order"> 175 <div class="text-price text-decoration-line-through opacity-75">@defaultPrice.ToString("F2") @Translate(product.Price.CurrencyCode + "-shortname")</div> 176 </span> 177 } 178 else if (product.Price.Price == beautyClubPrice.Amount) 179 { 180 181 <span class="@order"> 182 <div class="fs-6">@Translate("Not a member")</div> 183 <div class="text-price text-decoration-line-through opacity-75">@defaultPrice.ToString("F2") @Translate(product.Price.CurrencyCode + "-shortname")</div> 184 </span> 185 186 } 187 else 188 { 189 <span class="@order"> 190 <div class="text-price text-decoration-line-through opacity-75">@defaultPrice.ToString("F2") @Translate(product.Price.CurrencyCode + "-shortname")</div> 191 </span> 192 } 193 } 194 195 if (product.Price.Price != defaultPrice && product.Price.Price < defaultPrice && beautyClubPrice == null) 196 { 197 <span class="@order"> 198 <div class="text-price text-decoration-line-through opacity-75">@defaultPrice.ToString("F2") @Translate(product.Price.CurrencyCode + "-shortname")</div> 199 </span> 200 } 201 202 203 else if (product.Price.Price != product.PriceBeforeDiscount.Price) 204 { 205 <span class="text-decoration-line-through opacity-75 @order"> 206 <span class="text-price">@beforePrice</span> 207 </span> 208 } 209 } 210 } 211 212 @if (showPricesWithVat == "false" && !neverShowVat) 213 { 214 if (isLazyLoadingForProductInfoEnabled && !Pageview.IsVisualEditorMode) 215 { 216 <span class="text-price js-text-price"> 217 <span class="spinner-border" role="status"></span> 218 </span> 219 } 220 else 221 { 222 string price = !string.IsNullOrEmpty(unitId) ? product.GetPrice(unitId).Price.PriceWithoutVatFormatted : product.Price.PriceWithoutVatFormatted; 223 224 if (product?.VariantInfo?.VariantInfo != null) 225 { 226 priceMin = product?.VariantInfo?.PriceMin?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithoutVatFormatted : ""; 227 priceMax = product?.VariantInfo?.PriceMax?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithoutVatFormatted : ""; 228 } 229 if (priceMin != priceMax) 230 { 231 price = priceMin + " - " + priceMax; 232 } 233 <span class="@theme @contentPadding"> 234 <span class="text-price">@price</span> 235 </span> 236 } 237 } 238 else 239 { 240 if (isLazyLoadingForProductInfoEnabled && !Pageview.IsVisualEditorMode) 241 { 242 <span class="text-price js-text-price"> 243 <span class="spinner-border" role="status"></span> 244 </span> 245 } 246 else 247 { 248 string price = !string.IsNullOrEmpty(unitId) ? product.GetPrice(unitId).Price.PriceFormatted : product.Price.PriceFormatted; 249 250 if (product?.VariantInfo?.VariantInfo != null) 251 { 252 priceMin = product?.VariantInfo?.PriceMin?.PriceFormatted != null ? product.VariantInfo.PriceMin.PriceFormatted : ""; 253 priceMax = product?.VariantInfo?.PriceMax?.PriceFormatted != null ? product.VariantInfo.PriceMax.PriceFormatted : ""; 254 } 255 if (priceMin != priceMax) 256 { 257 price = priceMin + " - " + priceMax; 258 } 259 260 if (priceMatrix.All(pm => pm?.Amount == product?.Price?.Price) && product?.Price?.Price == beautyClubPrice?.Amount) 261 { 262 <span class="@theme @contentPadding"> 263 <span class="text-price">@price</span> 264 </span> 265 } 266 else if (beautyClubPrice != null && product.Price.Price == beautyClubPrice?.Amount) 267 { 268 <span class="@theme @contentPadding"> 269 <div class="fs-6 text-beautyclub">@Translate("Beauty club member")</div> 270 <div class="@theme @contentPadding text-beautyclub"> 271 <span class="text-price">@price</span> 272 </div> 273 </span> 274 } 275 else 276 { 277 <span class="@theme @contentPadding"> 278 @if (beautyClubPrice != null) 279 { 280 if (product.Price.Price == defaultPrice) 281 { 282 <div class="fs-6">@Translate("Not a member")</div> 283 } 284 } 285 <div class="text-price">@price</div> 286 </span> 287 } 288 } 289 } 290 @if (beautyClubPrice != null && product.Price.Price > beautyClubPrice.Amount && product.Price.Price == defaultPrice) 291 { 292 <span class="@theme @contentPadding"> 293 <div class="fs-6 text-beautyclub">@Translate("Beauty club member")</div> 294 <div class="@theme @contentPadding text-beautyclub"> 295 <span class="text-price">@beautyClubPrice.Amount.ToString("F2") @currency</span> 296 </div> 297 </span> 298 } 299 300 301 @* Stock state for Schema.org, start *@ 302 @{ 303 Uri url = Dynamicweb.Context.Current.Request.Url; 304 } 305 306 <link itemprop="url" href="@url"> 307 308 @{ 309 bool IsNeverOutOfStock = product.NeverOutOfstock; 310 } 311 312 @if (IsNeverOutOfStock) 313 { 314 <span itemprop="availability" class="d-none">@Translate("Available in stock")</span> 315 } 316 else 317 { 318 if (product.StockLevel > 0) 319 { 320 <span itemprop="availability" class="d-none">InStock</span> 321 } 322 else 323 { 324 <span itemprop="availability" class="d-none">OutOfStock</span> 325 } 326 } 327 @* Stock state for Schema.org, stop *@ 328 329 </div> 330 331 @if (showPricesWithVat == "false" && !neverShowVat) 332 { 333 if (isLazyLoadingForProductInfoEnabled && !Pageview.IsVisualEditorMode) 334 { 335 <small class="opacity-85 fst-normal js-text-price-with-vat d-none" data-suffix="@Translate("Incl. VAT")"></small> 336 } 337 else 338 { 339 string price = !string.IsNullOrEmpty(unitId) ? product.GetPrice(unitId).Price.PriceWithVatFormatted : product.Price.PriceWithVatFormatted; 340 341 if (product?.VariantInfo?.VariantInfo != null) 342 { 343 priceMin = product?.VariantInfo?.PriceMin?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithVatFormatted : ""; 344 priceMax = product?.VariantInfo?.PriceMax?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithVatFormatted : ""; 345 } 346 if (priceMin != priceMax) 347 { 348 price = priceMin + " - " + priceMax; 349 } 350 <small class="opacity-85 fst-normal">@price @Translate("Incl. VAT")</small> 351 } 352 } 353 </div> 354 } 355 else if (Pageview.IsVisualEditorMode) 356 { 357 <div class="alert alert-dark m-0" role="alert"> 358 <span>@Translate("No products available")</span> 359 </div> 360 } 361
Error executing template "Designs/Swift/Paragraph/Swift_ProductAddToCart.cshtml" System.NullReferenceException: Object reference not set to an instance of an object. at CompiledRazorTemplates.Dynamic.RazorEngine_ecda8dd8e05849bb91a9192d59f87ed6.ExecuteAsync() at RazorEngine.Templating.TemplateBase.Run(ExecuteContext context, TextWriter reader) at RazorEngine.Templating.RazorEngineCore.RunTemplate(ICompiledTemplate template, TextWriter writer, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.DynamicWrapperService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass23_0.<Run>b__0(TextWriter writer) at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter) at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, Type modelType, Object model, DynamicViewBag viewBag) at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template) at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template) at Dynamicweb.Rendering.Template.RenderRazorTemplate()
1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 @using Dynamicweb.Core.Encoders 4 5 6 @{ 7 ProductViewModel product = null; 8 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 9 { 10 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 11 } 12 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 13 { 14 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 15 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 16 17 if (productList?.Products is object) 18 { 19 product = productList.Products[0]; 20 } 21 } 22 23 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", ""); 24 bool anonymousUser = Pageview.User == null; 25 bool isErpConnectionDown = !Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsWebServiceConnectionAvailable"]); 26 bool hideAddToCart = anonymousUsersLimitations.Contains("cart") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHideAddToCart") && isErpConnectionDown; 27 hideAddToCart = Pageview.IsVisualEditorMode ? false : hideAddToCart; 28 } 29 30 @if (product is object && !hideAddToCart) { 31 string horizontalAlign = Model.Item.GetRawValueString("HorizontalAlignment", ""); 32 horizontalAlign = horizontalAlign == "center" ? "justify-content-center" : horizontalAlign; 33 horizontalAlign = horizontalAlign == "end" ? "justify-content-end" : horizontalAlign; 34 horizontalAlign = horizontalAlign == "full" ? "" : horizontalAlign; 35 36 bool favoritesSelector = !string.IsNullOrEmpty(Model.Item.GetString("ShowAddToFavorites")) ? Model.Item.GetBoolean("ShowAddToFavorites") : false; 37 bool quantitySelector = !string.IsNullOrEmpty(Model.Item.GetString("ShowQuantitySelector")) ? Model.Item.GetBoolean("ShowQuantitySelector") : false; 38 bool unitsSelector = !string.IsNullOrEmpty(Model.Item.GetString("ShowUnitsSelector")) ? Model.Item.GetBoolean("ShowUnitsSelector") : false; 39 bool hideInventory = !string.IsNullOrEmpty(Model.Item.GetString("HideInventory")) ? Model.Item.GetBoolean("HideInventory") : false; 40 bool hideStockState = !string.IsNullOrEmpty(Model.Item.GetString("HideStockState")) ? Model.Item.GetBoolean("HideStockState") : false; 41 42 string buttonSize = Model.Item.GetRawValueString("ButtonSize", "regular"); 43 string inputSize = string.Empty; 44 45 switch (buttonSize) 46 { 47 case "small": 48 inputSize = " input-group-sm"; 49 buttonSize = " btn-sm"; 50 break; 51 case "regular": 52 buttonSize = string.Empty; 53 break; 54 case "large": 55 inputSize = " input-group-lg"; 56 buttonSize = " btn-lg"; 57 break; 58 } 59 60 string iconPath = "/Files/icons/"; 61 string url = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("CartService")); 62 if (!url.Contains("LayoutTemplate")) 63 { 64 url += url.Contains("?") ? "&LayoutTemplate=Swift_MiniCart.cshtml" : "?LayoutTemplate=Swift_MiniCart.cshtml"; 65 } 66 67 string whenVariantsExist = Model.Item.GetRawValueString("WhenVariantsExist", "hide"); 68 string flexFill = Model.Item.GetRawValueString("HorizontalAlignment", "") == "full" ? "flex-fill" : ""; 69 string fullWidth = Model.Item.GetRawValueString("HorizontalAlignment", "") == "full" ? "w-100" : ""; 70 string addToCartIcon = Model.Item.GetRawValueString("Icon", iconPath + "shopping-cart.svg"); 71 string addToCartLabel = !addToCartIcon.Contains("_none") ? $"<span class=\"icon-2\">{ReadFile(addToCartIcon)}</span>" : ""; 72 addToCartLabel += !addToCartIcon.Contains("_none") && !Model.Item.GetBoolean("HideButtonText") ? " " : ""; 73 addToCartLabel += !Model.Item.GetBoolean("HideButtonText") ? $"<span class=\"d-none d-md-inline\">{Translate("Add to cart")}</span><span class=\"d-inline d-md-none\">{Translate("Add")}</span>" : ""; 74 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]); 75 76 bool userHasPendingQuote = Dynamicweb.Ecommerce.Common.Context.Cart != null && Dynamicweb.Ecommerce.Common.Context.Cart.IsQuote; 77 78 if (product.VariantInfo.VariantInfo == null || whenVariantsExist == "disable") { 79 string unitId = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("UnitId")) ? Dynamicweb.Context.Current.Request.Form.Get("UnitId") : product.DefaultUnitId; 80 if (string.IsNullOrEmpty(unitId) && product?.UnitOptions != null) { 81 if (product.UnitOptions.FirstOrDefault<UnitOptionViewModel>() != null) { 82 unitId = product.UnitOptions.FirstOrDefault<UnitOptionViewModel>().Id; 83 } 84 } 85 86 double? stepQty = product.PurchaseQuantityStep > 0 ? product.PurchaseQuantityStep : 1; 87 double? minQty = product.PurchaseMinimumQuantity > 0 ? product.PurchaseMinimumQuantity : 1; 88 double? valueQty = minQty > stepQty ? minQty : stepQty; 89 string disableAddToCart = null; 90 double? maxQty = null; 91 92 if (product.ProductType == Dynamicweb.Ecommerce.Products.ProductType.Stock && !product.NeverOutOfstock) 93 { 94 disableAddToCart = (product.StockLevel <= 0) || (!product.NeverOutOfstock && isLazyLoadingForProductInfoEnabled) ? "disabled" : disableAddToCart; 95 maxQty = product.StockLevel; 96 } 97 98 disableAddToCart = whenVariantsExist == "disable" && product.VariantInfo.VariantInfo != null && string.IsNullOrEmpty(product.VariantId) ? "disabled" : disableAddToCart; 99 100 if (unitsSelector && product.UnitOptions.Count > 0) { 101 <form method="post" action="/Default.aspx?ID=@(Pageview.Page.ID)&ProductId=@product.Id" id="UnitSelectorForm_@(product.Id)_@(product.VariantId.Replace(".", "_"))_@Model.ID"> 102 <input type="hidden" name="redirect" value="false"> 103 <input type="hidden" name="VariantID" value="@product.VariantId"> 104 <input type="hidden" name="UnitID" class="js-unit-id" value="@unitId"> 105 </form> 106 } 107 108 <div class="d-flex @horizontalAlign @fullWidth js-input-group item_@Model.Item.SystemName.ToLower()"> 109 <form method="post" action="@url" class="@fullWidth" style="z-index: 1"> 110 <input type="hidden" name="redirect" value="false"> 111 <input type="hidden" name="ProductId" value="@product.Id"> 112 <input type="hidden" name="ProductName" value="@HtmlEncoder.HtmlEncode(product.Name)"> 113 <input type="hidden" name="ProductVariantName" value="@product.VariantName"> 114 <input type="hidden" name="ProductCurrency" value="@Dynamicweb.Ecommerce.Common.Context.Currency.Code"> 115 <input type="hidden" name="ProductPrice" value="@PriceViewModelExtensions.ToStringInvariant(product.Price)"> 116 <input type="hidden" name="ProductReferer" value="component_ProductAddToCart"> 117 <input type="hidden" name="cartcmd" value="add"> 118 <input type="submit" class="d-none" onclick="event.preventDefault(); swift.Cart.Update(event)"> @* Fix for enterKey should not redirect to minicart page *@ 119 120 @if (!string.IsNullOrEmpty(product.VariantId)) 121 { 122 <input type="hidden" name="VariantId" value="@product.VariantId"> 123 } 124 125 <template class="js-step-quantity-warning"> 126 <div class="modal-header"> 127 <h1 class="modal-title fs-5">@Translate("The quantity is not valid")</h1> 128 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 129 </div> 130 <div class="modal-body"> 131 @Translate("Please select a quantity that is dividable by") @stepQty 132 </div> 133 </template> 134 135 136 <template class="js-min-quantity-warning"> 137 <div class="modal-header"> 138 <h1 class="modal-title fs-5">@Translate("The product could not be added to the cart")</h1> 139 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 140 </div> 141 <div class="modal-body"> 142 @Translate("The quantity is not valid. You must buy at least") @product.PurchaseMinimumQuantity 143 </div> 144 </template> 145 146 147 @if (userHasPendingQuote) 148 { 149 <input type="hidden" name="PendingQuote" value="true"> 150 151 <template class="js-pending-quote-notice"> 152 <div class="modal-header"> 153 <h1 class="modal-title fs-5">@Translate("Pending Quote")</h1> 154 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="@Translate("Close")"></button> 155 </div> 156 <div class="modal-body"> 157 @Translate("You need to complete your current quote or empty the cart before adding this product to cart.") 158 </div> 159 </template> 160 } 161 162 @if (quantitySelector || (!anonymousUser && product.VariantInfo.VariantInfo != null) || (!anonymousUser && favoritesSelector)) 163 { 164 <input type="hidden" id="Unit_@(product.Id)_@product.VariantId.Replace(".", "_")" name="UnitID" value="@unitId" /> 165 } 166 167 <div class="d-flex flex-row w-100"> 168 @if (!quantitySelector) 169 { 170 <input id="Quantity_@(product.Id)_@product.VariantId.Replace(".", "_")" class="swift_quantity_field" name="Quantity" value="@valueQty" type="hidden" @disableAddToCart> 171 } 172 173 @if (unitsSelector && product.UnitOptions.Count > 0) 174 { 175 string selectedUnitName = !string.IsNullOrEmpty(unitId) && product?.UnitOptions != null ? unitId : product.UnitOptions.FirstOrDefault<UnitOptionViewModel>().Name; 176 177 foreach (var unitOption in product.UnitOptions) 178 { 179 if (unitOption.Id == unitId) 180 { 181 selectedUnitName = unitOption.Name; 182 } 183 } 184 185 <div class="d-flex flex-column gap-2 w-100"> 186 <div class="input-group input-primary-button-group flex-nowrap@(inputSize)"> 187 @if (!anonymousUser && favoritesSelector) 188 { 189 @RenderPartial("Components/ToggleFavorite.cshtml", product) 190 } 191 192 @if (quantitySelector) 193 { 194 <input id="Quantity_@(product.Id)_@product.VariantId.Replace(".", "_")" name="Quantity" value="@valueQty" step="@stepQty" min="@minQty" max="@maxQty" class="form-control swift_quantity-field" style="min-width: 80px; max-width: 100px; z-index: 1" type="number" @disableAddToCart> 195 } 196 197 <button class="btn btn-secondary @flexFill dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false"> 198 @selectedUnitName 199 </button> 200 201 <ul class="dropdown-menu swift_unit-field"> 202 @foreach (var unitOption in product.UnitOptions) 203 { 204 var selectedUnit = unitOption.Id == unitId ? "selected" : ""; 205 206 <li> 207 <button type="button" class="btn dropdown-item" data-value="@unitOption.Id" onclick="document.querySelector('#UnitSelectorForm_@(product.Id)_@(product.VariantId.Replace(".", "_"))_@Model.ID').querySelector('.js-unit-id').value = this.getAttribute('data-value'); 208 document.querySelector('#Unit_@(product.Id)_@product.VariantId.Replace(".", "_")').value = this.getAttribute('data-value'); 209 swift.PageUpdater.Update(document.querySelector('#UnitSelectorForm_@(product.Id)_@(product.VariantId.Replace(".", "_"))_@Model.ID'))"> 210 <span>@unitOption.Name</span> 211 <span> 212 @if (unitOption.StockLevel > 0 || unitOption.NeverOutOfStock) 213 { 214 if (!Model.Item.GetBoolean("HideInventory") && !unitOption.NeverOutOfStock) 215 { 216 <span class="small text-success">@unitOption.StockLevel @Translate("In stock")</span> 217 } 218 else 219 { 220 <span class="small text-success">@Translate("In stock")</span> 221 } 222 } 223 else 224 { 225 <span class="small text-danger">@Translate("Out of Stock")</span> 226 } 227 </span> 228 </button> 229 </li> 230 } 231 </ul> 232 </div> 233 <button type="button" onclick="swift.Cart.Update(event)" class="btn btn-primary @(buttonSize) js-add-to-cart-button" style="white-space: nowrap" @disableAddToCart title="@Translate("Add to cart")" id="AddToCartButton@(product.Id)_@Pageview.CurrentParagraph.ID"> 234 @if (!Model.Item.GetBoolean("HideButtonText")) 235 { 236 <span class="text-nowrap d-flex align-items-center justify-content-center gap-2"> 237 @addToCartLabel 238 </span> 239 } 240 else 241 { 242 @addToCartLabel 243 } 244 </button> 245 </div> 246 } 247 else 248 { 249 if (!anonymousUser && favoritesSelector) 250 { 251 @RenderPartial("Components/ToggleFavorite.cshtml", product) 252 } 253 254 <div class="input-group input-primary-button-group flex-nowrap@(inputSize)"> 255 @if (quantitySelector) 256 { 257 <input id="Quantity_@(product.Id)_@product.VariantId.Replace(".", "_")" name="Quantity" value="@valueQty" step="@stepQty" min="@minQty" max="@maxQty" class="form-control swift_quantity-field" style="min-width: 60px; max-width: 100px; z-index: 1" type="number" @disableAddToCart> 258 } 259 260 <button type="button" onclick="swift.Cart.Update(event)" class="btn btn-primary @(buttonSize) @flexFill js-add-to-cart-button" style="white-space: nowrap" @disableAddToCart title="@Translate("Add to cart")" id="AddToCartButton@(product.Id)_@Pageview.CurrentParagraph.ID"> 261 @if (!Model.Item.GetBoolean("HideButtonText")) 262 { 263 <span class="text-nowrap d-flex align-items-center justify-content-center gap-2"> 264 @addToCartLabel 265 </span> 266 } 267 else 268 { 269 @addToCartLabel 270 } 271 </button> 272 </div> 273 } 274 </div> 275 </form> 276 </div> 277 } else if (whenVariantsExist == "modal") { 278 string ButtonShape = Model.Item.GetRawValueString("VariantButtonShape", "square"); 279 string buttonAspectRatio = Model.Item.GetRawValueString("VariantImageAspectRatio", "56%"); 280 281 string buttonText = Translate("Select"); 282 string variantId = !string.IsNullOrWhiteSpace(product.VariantId) ? product.VariantId : product.DefaultVariantId; 283 284 string variantSelectorServicePageId = !string.IsNullOrEmpty(Model.Item.GetString("VariantSelectorServicePageId")) ? Model.Item.GetLink("VariantSelectorServicePageId").PageId.ToString() : ""; 285 variantSelectorServicePageId = variantSelectorServicePageId != "" ? variantSelectorServicePageId : GetPageIdByNavigationTag("VariantSelectorService").ToString(); 286 287 <div class="d-flex @horizontalAlign w-100 item_@Model.Item.SystemName.ToLower()"> 288 @if (!anonymousUser && favoritesSelector) 289 { 290 @RenderPartial("Components/ToggleFavorite.cshtml", product) 291 } 292 <form action="/Default.aspx?ID=@variantSelectorServicePageId" data-response-target-element="DynamicModalContent" data-preloader="inline" style="z-index: 1" class="@fullWidth"> 293 <input type="hidden" name="ProductID" value="@product.Id"> 294 <input type="hidden" name="VariantID" value="@variantId"> 295 <input type="hidden" name="QuantitySelector" value="@quantitySelector.ToString()"> 296 <input type="hidden" name="HideInventory" value="@hideInventory.ToString()"> 297 <input type="hidden" name="HideStockState" value="@hideStockState.ToString()"> 298 <input type="hidden" name="ButtonLayout" value="@ButtonShape"> 299 <input type="hidden" name="ButtonAspectRatio" value="@buttonAspectRatio"> 300 <input type="hidden" name="VariantSelectorServicePage" value="@variantSelectorServicePageId"> 301 <input type="hidden" name="ViewType" value="ModalContent"> 302 @if (isLazyLoadingForProductInfoEnabled) 303 { 304 @* If lazy loading is enabled, bypass it because we're loading a modal window, so render everything as if it was server-side *@ 305 <input type="hidden" name="getproductinfo" value="true"> 306 } 307 <button type="button" onclick="swift.PageUpdater.Update(event)" class="btn btn-primary@(buttonSize) @fullWidth" title="@Translate("Select")" data-bs-toggle="modal" data-bs-target="#DynamicModal" id="OpenVariantSelectorModal@(product.Id)_@Pageview.CurrentParagraph.ID">@buttonText</button> 308 </form> 309 </div> 310 } 311 } else if (Pageview.IsVisualEditorMode) { 312 <div class="alert alert-dark m-0">@Translate("No products available")</div> 313 } 314
Ikke på lager
Error executing template "Designs/Swift/Paragraph/Swift_BackInStockNotification.cshtml" System.NullReferenceException: Object reference not set to an instance of an object. at CompiledRazorTemplates.Dynamic.RazorEngine_1f9117486aad46c0844016eed9bdcf47.ExecuteAsync() at RazorEngine.Templating.TemplateBase.Run(ExecuteContext context, TextWriter reader) at RazorEngine.Templating.RazorEngineCore.RunTemplate(ICompiledTemplate template, TextWriter writer, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.DynamicWrapperService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass23_0.<Run>b__0(TextWriter writer) at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter) at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, Type modelType, Object model, DynamicViewBag viewBag) at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template) at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template) at Dynamicweb.Rendering.Template.RenderRazorTemplate()
1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using System.Linq 3 @using Dynamicweb.Ecommerce 4 @using Dynamicweb.Ecommerce.ProductCatalog 5 @using Dynamicweb.Ecommerce.Products 6 @using Dynamicweb.Ecommerce.Stocks 7 8 @functions 9 { 10 private static bool BackInStockRegisteredForUser(ProductViewModel product, string unitId = "", long stocklocationId = 0) 11 { 12 if (!Dynamicweb.Security.UserManagement.User.IsExtranetUserLoggedIn()) return false; 13 14 Product productObject = Services.Products.GetProductById(product.Id, product.VariantId, product.LanguageId); 15 StockLocation stockLocation = Services.StockService.GetStockLocation(stocklocationId); 16 double unitStock = productObject.GetUnitStock(stockLocation, unitId); 17 return unitStock <= 0d && ProductBackInStockNotification.BackInStockNotificationExists(productObject, unitId); 18 } 19 } 20 21 @{ 22 ProductViewModel product = null; 23 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 24 { 25 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 26 } 27 else if (Pageview.Page.Item["DummyProduct"] != null) 28 { 29 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 30 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 31 32 if (productList?.Products is object) 33 { 34 product = productList.Products[0]; 35 } 36 } 37 } 38 39 @if ((product is object && product.ProductType == Dynamicweb.Ecommerce.Products.ProductType.Stock)) 40 { 41 bool productInStock = product.StockLevel > 0 || product.NeverOutOfstock; 42 43 if (!productInStock) 44 { 45 string notifiedMessage = Model.Item.GetString("NotifiedMessage"); 46 string notifiedMessageId = $"NotifiedMessage_item_{Model.Item.SystemName.ToLower()}_{product.Id}_{product.VariantId.Replace(".", "_")}"; 47 48 string horizontalAlign = Model.Item.GetRawValueString("HorizontalAlignment", ""); 49 horizontalAlign = horizontalAlign == "center" ? "justify-content-center" : horizontalAlign; 50 horizontalAlign = horizontalAlign == "end" ? "justify-content-end" : horizontalAlign; 51 horizontalAlign = horizontalAlign == "full" ? "" : horizontalAlign; 52 53 string flexFill = Model.Item.GetRawValueString("HorizontalAlignment", "") == "full" ? "flex-fill" : ""; 54 string fullWidth = Model.Item.GetRawValueString("HorizontalAlignment", "") == "full" ? "w-100" : ""; 55 56 bool notificationRegisteredForUser = BackInStockRegisteredForUser(product); 57 58 <div class="d-flex flex-row w-100 @horizontalAlign @fullWidth item_@Model.Item.SystemName.ToLower()"> 59 <div class="@horizontalAlign @fullWidth @(notificationRegisteredForUser ? "" : "d-none")" id="@notifiedMessageId"> 60 @notifiedMessage 61 </div> 62 63 @if (!notificationRegisteredForUser) 64 { 65 66 string notifyIcon = Model.Item.GetString("Icon"); 67 string notifyLabel = !notifyIcon.Contains("_none") ? "<span class=\"icon-2\">" + ReadFile(notifyIcon) + "</span>" : ""; 68 notifyLabel += " " + Translate("Notify me when available"); 69 70 string buttonSize = Model.Item.GetRawValueString("ButtonSize", "regular"); 71 string inputSize = string.Empty; 72 73 switch (buttonSize) 74 { 75 case "small": 76 inputSize = " input-group-sm"; 77 buttonSize = " btn-sm"; 78 break; 79 case "regular": 80 buttonSize = string.Empty; 81 break; 82 case "large": 83 inputSize = " input-group-lg"; 84 buttonSize = " btn-lg"; 85 break; 86 } 87 88 string modalId = $"modal_item_{Model.Item.SystemName.ToLower()}_{product.Id}_{product.VariantId.Replace(".", "_")}"; 89 string formId = $"NotificationForm_item_{Model.Item.SystemName.ToLower()}_{product.Id}_{product.VariantId.Replace(".", "_")}"; 90 string notificationButtonId = $"NotificationButton_item_{Model.Item.SystemName.ToLower()}_{product.Id}_{product.VariantId.Replace(".", "_")}"; 91 string notificationOnClick = !Pageview.IsVisualEditorMode ? $"swift.BackInStockNotification.SubmitNotification('{formId}', '{modalId}', '{notifiedMessageId}', '{notificationButtonId}')" : ""; 92 93 string productPage = $"Default.aspx?ID={GetPageIdByNavigationTag("ProductDetailPage")}"; 94 95 <div class="d-flex @horizontalAlign @fullWidth js-input-group"> 96 <div class="input-group input-primary-button-group flex-nowrap@(inputSize)"> 97 <button type="button" class="btn btn-primary @(buttonSize) @flexFill" id="@notificationButtonId" data-bs-toggle="modal" data-bs-target="#@modalId"> 98 <span class="text-nowrap d-flex align-items-center justify-content-center gap-2"> 99 @notifyLabel 100 </span> 101 </button> 102 </div> 103 </div> 104 105 <div class="modal fade" id="@modalId" tabindex="-1" aria-labelledby="label_@modalId" aria-hidden="true"> 106 <div class="modal-dialog modal-dialog-centered"> 107 <div class="modal-content"> 108 <div class="modal-header"> 109 <h5 class="modal-title" id="label_@modalId">@Translate("Back in Stock")</h5> 110 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="@Translate("Close")"></button> 111 </div> 112 <div class="modal-body"> 113 <form name="@product.Id" id="@formId" method="post" action="@productPage"> 114 <input type="hidden" name="ProductID" value="@product.Id" /> 115 <input type="hidden" name="VariantID" value="@product.VariantId" /> 116 <input type="hidden" name="LanguageID" value="@product.LanguageId" /> 117 <input type="hidden" name="CartCmd" value="createnotificationforthisproduct" /> 118 <div class="m-2"> 119 @Translate("Send me an email when the product is in stock.") 120 </div> 121 <div class="form-floating m-2"> 122 <input class="form-control" type="text" id="NotificationEmail" value="@(Pageview.User != null ? Pageview.User.Email : "")" name="NotificationEmail" placeholder="@Translate("Email")" required="required" aria-required="aria-required" /> 123 <label for="NotificationEmail" class="form-label">@Translate("Email")</label> 124 </div> 125 </form> 126 </div> 127 <div class="modal-footer"> 128 <button type="button" class="btn btn-primary" onclick="@notificationOnClick">@Translate("Submit")</button> 129 </div> 130 </div> 131 </div> 132 </div> 133 } 134 </div> 135 } 136 } 137 else if (Pageview.IsVisualEditorMode) 138 { 139 <div class="alert alert-dark m-0">@Translate("Back in stock notification will be shown here")</div> 140 } 141
Bestil før kl. 13 - afsendes i dag!
Forventet levering: 1-2 dage
Fri fragt over 599 kr. - ellers fra 39 kr.
30 dages returret
Error executing template "Designs/Swift/Paragraph/Swift_ProductDetailsGallery.cshtml" System.ArgumentNullException: Value cannot be null. (Parameter 'source') at System.Linq.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument) at System.Linq.Enumerable.Where[TSource](IEnumerable`1 source, Func`2 predicate) at CompiledRazorTemplates.Dynamic.RazorEngine_9413a9f8ec044709bb9237d1e7975eb4.ExecuteAsync() at RazorEngine.Templating.TemplateBase.Run(ExecuteContext context, TextWriter reader) at RazorEngine.Templating.RazorEngineCore.RunTemplate(ICompiledTemplate template, TextWriter writer, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.DynamicWrapperService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass23_0.<Run>b__0(TextWriter writer) at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter) at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, Type modelType, Object model, DynamicViewBag viewBag) at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template) at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template) at Dynamicweb.Rendering.Template.RenderRazorTemplate()
1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 @using Dynamicweb.Frontend 4 @using System.IO 5 @using System.Text.RegularExpressions; 6 7 @functions { 8 public ProductViewModel product { get; set; } = new ProductViewModel(); 9 public string galleryLayout { get; set; } 10 public string[] supportedImageFormats { get; set; } 11 public string[] supportedVideoFormats { get; set; } 12 public string[] supportedDocumentFormats { get; set; } 13 public string[] allSupportedFormats { get; set; } 14 15 public class RatioSettings { 16 public string Ratio { get; set; } 17 public string CssClass { get; set; } 18 public string CssVariable { get; set; } 19 public string Fill { get; set; } 20 } 21 22 public RatioSettings GetRatioSettings(string size = "desktop") { 23 var ratioSettings = new RatioSettings(); 24 25 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 26 ratio = ratio != "0" ? ratio : ""; 27 string cssClass = ratio != "" && ratio != "fill" ? " ratio" : ""; 28 string cssVariable = ratio != "" && ratio != "fill" ? "--bs-aspect-ratio: " + ratio : ""; 29 cssClass = ratio != "" && ratio == "fill" && size == "mobile" ? " ratio" : cssClass; 30 cssVariable = ratio != "" && ratio == "fill" && size == "mobile" ? "--bs-aspect-ratio: 66%" : cssVariable; 31 32 ratioSettings.Ratio = ratio; 33 ratioSettings.CssClass = cssClass; 34 ratioSettings.CssVariable = cssVariable; 35 ratioSettings.Fill = ratio == "fill" ? " h-100" : ""; 36 37 return ratioSettings; 38 } 39 40 public string GetColumnClass(int total, int assetNumber) { 41 string colClass = total > 1 ? "g-col-lg-6" : "g-col-12"; 42 colClass = galleryLayout == "full-first" && assetNumber == 0 ? "g-col-12" : colClass; 43 colClass = galleryLayout == "full-last" && assetNumber == (total - 1) ? "g-col-12" : colClass; 44 colClass = galleryLayout == "advanced-grid" && assetNumber > 1 ? "g-col-4" : colClass; 45 46 colClass = galleryLayout == "advanced-grid" && total == 1 ? "g-col-12" : colClass; 47 colClass = galleryLayout == "advanced-grid" && total == 3 && assetNumber == 2 ? "g-col-12" : colClass; 48 colClass = galleryLayout == "advanced-grid" && total == 4 && assetNumber == 2 ? "g-col-6" : colClass; 49 colClass = galleryLayout == "advanced-grid" && total == 4 && assetNumber == 3 ? "g-col-6" : colClass; 50 colClass = galleryLayout == "advanced-grid" && total == 6 && assetNumber == 5 ? "g-col-12" : colClass; 51 colClass = galleryLayout == "advanced-grid" && total == 7 && assetNumber == 5 ? "g-col-6" : colClass; 52 colClass = galleryLayout == "advanced-grid" && total == 7 && assetNumber == 6 ? "g-col-6" : colClass; 53 colClass = galleryLayout == "advanced-grid" && total == 9 && assetNumber == 8 ? "g-col-12" : colClass; 54 55 return colClass; 56 } 57 58 public string GetArrowsColor() 59 { 60 var invertColor = Model.Item.GetBoolean("InvertModalArrowsColor"); 61 var arrowsColor = invertColor ? " carousel-dark" : string.Empty; 62 return arrowsColor; 63 } 64 65 public Dictionary<string, object> GetVideoParams(MediaViewModel asset, string size) 66 { 67 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Name; 68 string type = GetVideoType(asset.Value); 69 bool openInModal = Model.Item.GetString("OpenVideoInModal") == "true" ? true : false; 70 bool autoPlay = Model.Item.GetBoolean("VideoAutoPlay"); 71 72 var videoParams = new Dictionary<string, object>(); 73 videoParams.Add("AssetName", asset.Name); 74 videoParams.Add("AssetVideoType", type); 75 videoParams.Add("AssetDisplayName", asset.DisplayName); 76 videoParams.Add("OpenVideoInModal", openInModal); 77 videoParams.Add("VideoAutoPlay", autoPlay); 78 videoParams.Add("Size", size); 79 videoParams.Add("Id", Model.ID); 80 return videoParams; 81 } 82 83 public string GetVideoType(string assetValue) 84 { 85 string type = assetValue.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "youtube" : string.Empty; 86 type = assetValue.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "vimeo" : type; 87 type = string.IsNullOrEmpty(type) ? "selfhosted" : type; 88 return type; 89 } 90 91 public string GetYoutubeScreenDump(string assetValue, string quality) 92 { 93 var regex = new Regex(@"(?:youtube\.com\/.*[\?&]v=|youtu\.be\/)([\w-]+)"); 94 Match match = regex.Match(assetValue); 95 string videoId = match.Success ? match.Groups[1].Value : string.Empty; 96 string youtubeThumbnail = $"https://img.youtube.com/vi/{videoId}/{quality}.jpg"; 97 return youtubeThumbnail; 98 } 99 } 100 101 @{ 102 @* Get the product data *@ 103 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 104 { 105 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 106 } 107 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 108 { 109 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 110 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 111 112 if (productList?.Products is object) 113 { 114 product = productList.Products[0]; 115 } 116 } 117 } 118 119 @if (product is object) 120 { 121 @* Supported formats *@ 122 supportedImageFormats = new string[] { ".jpg", ".jpeg", ".webp", ".png", ".gif", ".bmp", ".tiff" }; 123 supportedVideoFormats = new string[] { "youtu.be", "youtube", "vimeo", ".mp4", ".webm" }; 124 supportedDocumentFormats = new string[] { ".pdf", ".docx", ".xlsx", ".ppt", "pptx" }; 125 allSupportedFormats = supportedImageFormats.Concat(supportedVideoFormats).Concat(supportedDocumentFormats).ToArray(); 126 127 @* Collect the assets *@ 128 var selectedAssetCategories = Model.Item.GetList("ImageAssets")?.GetRawValue().OfType<string>(); 129 bool includeImagePatternImages = Model.Item.GetBoolean("ImagePatternImages"); 130 131 @* Needed image data collection to support both DefaultImage, ImagePatterns and Image Assets *@ 132 string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : ""; 133 IEnumerable<MediaViewModel> assetsImages = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets); 134 assetsImages = assetsImages.OrderByDescending(x => x.Value.Equals(defaultImage)); 135 IEnumerable<MediaViewModel> assetsList = new MediaViewModel[]{}; 136 assetsList = assetsList.Union(assetsImages); 137 assetsList = includeImagePatternImages ? assetsList.Union(product.ImagePatternImages) : assetsList; 138 assetsList = includeImagePatternImages && assetsList.Count() == 0 ? assetsList.Append(product.DefaultImage) : assetsList; 139 140 bool defaultImageFallback = Model.Item.GetBoolean("DefaultImageFallback"); 141 142 int totalAssets = 0; 143 foreach (MediaViewModel asset in assetsList) { 144 var assetValue = asset.Value; 145 foreach (string format in allSupportedFormats) { 146 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 147 totalAssets++; 148 } 149 } 150 } 151 152 if (totalAssets == 0) 153 { 154 if (defaultImageFallback) { 155 assetsList = new List<MediaViewModel>(){ product.DefaultImage }; 156 totalAssets = 1; 157 } else { 158 assetsList = new List<MediaViewModel>(){ }; 159 totalAssets = 0; 160 } 161 } 162 163 @* Layout settings *@ 164 string spacing = Model.Item.GetRawValueString("Spacing", "4"); 165 spacing = spacing == "none" ? "gap-0" : spacing; 166 spacing = spacing == "small" ? "gap-3" : spacing; 167 spacing = spacing == "large" ? "gap-4" : spacing; 168 169 galleryLayout = Model.Item.GetRawValueString("Layout", "grid"); 170 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 171 172 var badgeParms = new Dictionary<string, object>(); 173 badgeParms.Add("size", "h5"); 174 badgeParms.Add("saleBadgeType", Model.Item.GetRawValue("SaleBadgeType")); 175 badgeParms.Add("saleBadgeCssClassName", Model.Item.GetRawValue("SaleBadgeDesign")); 176 badgeParms.Add("newBadgeCssClassName", Model.Item.GetRawValue("NewBadgeDesign")); 177 badgeParms.Add("newPublicationDays", Model.Item.GetInt32("NewPublicationDays")); 178 badgeParms.Add("campaignBadgesValues", Model.Item.GetList("CampaignBadges")?.GetRawValue().OfType<string>().ToList()); 179 180 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("SaleBadgeDesign")) && Model.Item.GetRawValueString("SaleBadgeDesign") != "none" ? true : false; 181 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("NewBadgeDesign")) && Model.Item.GetRawValueString("NewBadgeDesign") != "none" ? true : false; 182 DateTime createdDate = product.Created.Value; 183 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false; 184 showBadges = (newBadgeEnabled && Model.Item.GetInt32("NewPublicationDays") == 0) || (newBadgeEnabled && (createdDate.AddDays(Model.Item.GetInt32("NewPublicationDays")) > DateTime.Now)) ? true : showBadges; 185 showBadges = !string.IsNullOrEmpty(Model.Item.GetRawValueString("CampaignBadges")) ? true : showBadges; 186 187 188 @* Get assets from selected categories or get all assets *@ 189 if (totalAssets != 0 && assetsList.Count() != 0) { 190 int desktopAssetNumber = 0; 191 int mobileAssetNumber = 0; 192 int mobileAssetThumbnailNumber = 0; 193 int modalAssetNumber = 0; 194 195 @* Desktop: Show the gallery on large screens *@ 196 <div class="d-none d-lg-block h-100 position-relative @theme item_@Model.Item.SystemName.ToLower() desktop"> 197 <div class="grid @spacing"> 198 @foreach (MediaViewModel asset in assetsList) { 199 var assetName = asset.Value; 200 foreach (string format in allSupportedFormats) { 201 if (assetName.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 202 string size = "desktop"; 203 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 204 string assetValue = asset.Value; 205 206 <div class="@GetColumnClass(totalAssets, desktopAssetNumber)"> 207 <div class="h-100 @(imageTheme)"> 208 @foreach (string imageFormat in supportedImageFormats) 209 { //Images 210 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 211 { 212 string productName = product.Name; 213 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 214 string imageLinkPath = Dynamicweb.Context.Current.Server.UrlEncode(imagePath); 215 216 RatioSettings ratioSettings = GetRatioSettings(size); 217 218 var parms = new Dictionary<string, object>(); 219 parms.Add("alt", productName); 220 parms.Add("itemprop", "image"); 221 if (totalAssets > 1) 222 { 223 parms.Add("columns", 2); 224 } 225 else 226 { 227 parms.Add("columns", 1); 228 } 229 parms.Add("eagerLoadNewImages", Model.Item.GetBoolean("DisableLazyLoading")); 230 parms.Add("doNotUseGetimage", Model.Item.GetBoolean("DisableGetImage")); 231 232 if (!string.IsNullOrEmpty(asset.DisplayName)) 233 { 234 parms.Add("title", asset.DisplayName); 235 } 236 237 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") 238 { 239 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 240 } 241 else 242 { 243 parms.Add("cssClass", "mw-100 mh-100"); 244 } 245 246 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 247 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@desktopAssetNumber"> 248 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 249 </div> 250 </a> 251 } 252 } 253 @foreach (string videoFormat in supportedVideoFormats) 254 { //Videos 255 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 256 { 257 if (Model.Item.GetString("OpenVideoInModal") == "true") 258 { 259 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 260 261 string type = GetVideoType(asset.Value); 262 string videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeUriString(asset.Value) : string.Empty; 263 videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value, "maxresdefault") : videoScreendumpPath; 264 string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : string.Empty; 265 266 string productName = product.Name; 267 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 268 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 269 270 RatioSettings ratioSettings = GetRatioSettings(size); 271 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable); cursor: pointer" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 272 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@desktopAssetNumber"> 273 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 274 @if (type != "selfhosted") 275 { 276 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@productName" @assetTitle class="@videoJsClass mw-100 mh-100" data-asset-value="@asset.Value" style="object-fit: cover;" /> 277 } 278 else 279 { 280 string videoType = Path.GetExtension(asset.Value).ToLower(); 281 282 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 283 <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> 284 </video> 285 } 286 </div> 287 </div> 288 289 } 290 else 291 { 292 var videoParams = GetVideoParams(asset, size); 293 @RenderPartial("Components/VideoPlayer.cshtml", new FileViewModel { Path = asset.Value }, videoParams); 294 } 295 } 296 } 297 @foreach (string documentFormat in supportedDocumentFormats) 298 { //Documents 299 if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) 300 { 301 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 302 303 string productName = product.Name; 304 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 305 string imageLinkPath = imagePath; 306 307 RatioSettings ratioSettings = GetRatioSettings(size); 308 309 var parms = new Dictionary<string, object>(); 310 parms.Add("alt", productName); 311 parms.Add("itemprop", "image"); 312 parms.Add("fullwidth", true); 313 parms.Add("columns", Model.GridRowColumnCount); 314 if (!string.IsNullOrEmpty(asset.DisplayName)) 315 { 316 parms.Add("title", asset.DisplayName); 317 } 318 319 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") 320 { 321 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 322 } 323 else 324 { 325 parms.Add("cssClass", "mw-100 mh-100"); 326 } 327 328 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download title="@Translate("Download")"> 329 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 330 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 331 @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) 332 { 333 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 334 } 335 else 336 { 337 338 } 339 </div> 340 </a> 341 } 342 } 343 </div> 344 </div> 345 desktopAssetNumber++; 346 } 347 } 348 } 349 </div> 350 351 @if (showBadges) { 352 <div class="position-absolute top-0 left-0 p-2 p-lg-3 w-100"> 353 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms) 354 </div> 355 } 356 </div> 357 358 @* Mobile: Show the thumbs on small screens *@ 359 <div class="d-block d-lg-none mx-lg-0 position-relative @theme item_@Model.Item.SystemName.ToLower() mobile"> 360 <div id="SmallScreenImages_@Model.ID" class="carousel@(GetArrowsColor())" data-bs-ride="carousel"> 361 <div class="carousel-inner h-100"> 362 @foreach (MediaViewModel asset in assetsList) { 363 var assetValue = asset.Value; 364 foreach (string format in allSupportedFormats) { 365 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 366 string activeSlide = mobileAssetNumber == 0 ? "active" : ""; 367 string size = "mobile"; 368 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 369 370 <div class="carousel-item @activeSlide" data-bs-interval="99999"> 371 <div class="h-100 @(imageTheme)"> 372 @foreach (string imageFormat in supportedImageFormats) 373 { //Images 374 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 375 { 376 string productName = product.Name; 377 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 378 string imageLinkPath = Dynamicweb.Context.Current.Server.UrlEncode(imagePath); 379 380 RatioSettings ratioSettings = GetRatioSettings(size); 381 382 var parms = new Dictionary<string, object>(); 383 parms.Add("alt", productName); 384 parms.Add("itemprop", "image"); 385 if (totalAssets > 1) 386 { 387 parms.Add("columns", 2); 388 } 389 else 390 { 391 parms.Add("columns", 1); 392 } 393 parms.Add("eagerLoadNewImages", Model.Item.GetBoolean("DisableLazyLoading")); 394 parms.Add("doNotUseGetimage", Model.Item.GetBoolean("DisableGetImage")); 395 396 if (!string.IsNullOrEmpty(asset.DisplayName)) 397 { 398 parms.Add("title", asset.DisplayName); 399 } 400 401 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") 402 { 403 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 404 } 405 else 406 { 407 parms.Add("cssClass", "mw-100 mh-100"); 408 } 409 410 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 411 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@desktopAssetNumber"> 412 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 413 </div> 414 </a> 415 } 416 } 417 @foreach (string videoFormat in supportedVideoFormats) 418 { //Videos 419 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 420 { 421 if (Model.Item.GetString("OpenVideoInModal") == "true") 422 { 423 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 424 425 string type = GetVideoType(asset.Value); 426 427 string videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeUriString(asset.Value) : string.Empty; 428 videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value, "maxresdefault") : videoScreendumpPath; 429 string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : ""; 430 431 string productName = product.Name; 432 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 433 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 434 435 RatioSettings ratioSettings = GetRatioSettings(size); 436 437 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable); cursor: pointer" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 438 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@desktopAssetNumber"> 439 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 440 @if (type != "selfhosted") 441 { 442 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@productName" @assetTitle class="@videoJsClass mw-100 mh-100" data-asset-value="@asset.Value" style="object-fit: cover;" > 443 } 444 else 445 { 446 string videoType = Path.GetExtension(asset.Value).ToLower(); 447 448 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 449 <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> 450 </video> 451 } 452 </div> 453 </div> 454 } 455 else 456 { 457 Dictionary<string, object> videoParams = GetVideoParams(asset, size); 458 @RenderPartial("Components/VideoPlayer.cshtml", new FileViewModel { Path = asset.Value }, videoParams); 459 } 460 } 461 } 462 </div> 463 </div> 464 mobileAssetNumber++; 465 } 466 } 467 } 468 </div> 469 </div> 470 471 @if (totalAssets > 1) { 472 <div id="SmallScreenImagesThumbnails_@Model.ID" class="d-flex flex-nowrap gap-2 overflow-x-auto my-3"> 473 @foreach (MediaViewModel asset in assetsList) { 474 var assetValue = asset.Value; 475 foreach (string format in allSupportedFormats) { 476 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 477 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 478 string type = GetVideoType(asset.Value); 479 480 string videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value, "maxresdefault") : string.Empty; 481 videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeUriString(asset.Value) : videoScreendumpPath; 482 string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : string.Empty; 483 484 string productName = product.Name; 485 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 486 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 487 488 <div class="ratio ratio-4x3 border outline-none" style="flex:0 0 80px" data-bs-target="#SmallScreenImages_@Model.ID" data-bs-slide-to="@mobileAssetThumbnailNumber"> 489 @foreach (string imageFormat in supportedImageFormats) 490 { //Images 491 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 492 { 493 string imagePath = !string.IsNullOrEmpty(asset.Value) ? $"/Admin/Public/GetImage.ashx?image={asset.Value}&width=180&format=webp" : string.Empty; 494 <img src="@imagePath" class="p-1 mw-100 mh-100" style="object-fit: cover;" alt="@productName" @assetTitle > 495 } 496 } 497 @foreach (string videoFormat in supportedVideoFormats) 498 { //Videos 499 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 500 { 501 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 502 <div class="icon-3 position-absolute text-light" style="z-index: 1">@ReadFile(iconPath+"play-circle.svg")</div> 503 </div> 504 if (type != "selfhosted") 505 { 506 507 <img src="@(videoScreendumpPath)" class="p-1 @videoJsClass mw-100 mh-100" data-asset-value="@asset.Value" style="object-fit: cover;" alt="@productName" @assetTitle> 508 509 } 510 else 511 { 512 string videoType = Path.GetExtension(asset.Value).ToLower(); 513 514 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 515 <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> 516 </video> 517 } 518 } 519 } 520 @foreach (string documentFormat in supportedDocumentFormats) 521 { //Documents 522 if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) 523 { 524 string imagePath = !string.IsNullOrEmpty(asset.Value) ? $"/Admin/Public/GetImage.ashx?image={asset.Value}&width=180&format=webp" : string.Empty; 525 526 <a href="@assetValue" style="cursor: pointer; min-width: 7rem; max-width: 8rem;" download title="@asset.Value"> 527 @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) 528 { 529 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 530 <div class="icon-3 position-absolute text-light" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 531 </div> 532 <img src="@imagePath" alt="@productName" @assetTitle class="p-0 p-lg-1 mw-100 mh-100" style="object-fit: cover;"> 533 } 534 else 535 { 536 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 537 <div class="icon-3 position-absolute" style="z-index: 1">@ReadFile(iconPath + "file-text.svg")</div> 538 </div> 539 } 540 </a> 541 } 542 } 543 </div> 544 545 mobileAssetThumbnailNumber++; 546 } 547 } 548 } 549 </div> 550 } 551 552 @if (showBadges) { 553 <div class="position-absolute top-0 left-0 p-2 p-lg-3"> 554 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms) 555 </div> 556 } 557 </div> 558 559 @* Modal with slides *@ 560 <div class="modal fade swift_products-details-images-modal" id="modal_@Model.ID" tabindex="-1" aria-labelledby="productDetailsGalleryModalTitle_@Model.ID" aria-hidden="true"> 561 <div class="modal-dialog modal-dialog-centered modal-xl"> 562 <div class="modal-content"> 563 <div class="modal-header visually-hidden"> 564 <h5 class="modal-title" id="productDetailsGalleryModalTitle_@Model.ID">@product.Title</h5> 565 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 566 </div> 567 <div class="modal-body p-2 p-lg-3 h-100"> 568 <div id="ModalCarousel_@Model.ID" class="carousel@(GetArrowsColor()) h-100" data-bs-ride="carousel"> 569 <div class="carousel-inner h-100 @theme"> 570 @foreach (MediaViewModel asset in assetsList) { 571 var assetValue = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 572 foreach (string format in allSupportedFormats) { 573 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) { 574 string imagePath = assetValue; 575 string activeSlide = modalAssetNumber == 0 ? "active" : ""; 576 577 var parms = new Dictionary<string, object>(); 578 parms.Add("cssClass", "d-block mw-100 mh-100 m-auto"); 579 parms.Add("columns", Model.GridRowColumnCount); 580 parms.Add("eagerLoadNewImages", Model.Item.GetBoolean("DisableLazyLoading")); 581 parms.Add("doNotUseGetimage", Model.Item.GetBoolean("DisableGetImage")); 582 583 <div class="carousel-item @activeSlide h-100" data-bs-interval="99999"> 584 @foreach (string imageFormat in supportedImageFormats) { 585 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) { 586 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 587 } 588 } 589 590 @foreach (string videoFormat in supportedVideoFormats) { 591 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) { 592 593 Dictionary<string, object> videoParams = GetVideoParams(asset, "modal"); 594 @RenderPartial("Components/VideoPlayer.cshtml", new FileViewModel { Path = asset.Value }, videoParams); 595 596 } 597 } 598 </div> 599 600 modalAssetNumber++; 601 } 602 } 603 } 604 <button class="carousel-control-prev carousel-control-area" type="button" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide="prev"> 605 <span class="carousel-control-prev-icon" aria-hidden="true"></span> 606 <span class="visually-hidden">@Translate("Previous")</span> 607 </button> 608 <button class="carousel-control-next carousel-control-area" type="button" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide="next"> 609 <span class="carousel-control-next-icon" aria-hidden="true"></span> 610 <span class="visually-hidden">@Translate("Next")</span> 611 </button> 612 </div> 613 </div> 614 </div> 615 </div> 616 </div> 617 </div> 618 } else if (Pageview.IsVisualEditorMode) { 619 RatioSettings ratioSettings = GetRatioSettings("desktop"); 620 621 <div class="h-100 @theme"> 622 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> 623 <img src="/Files/Images/missing_image.jpg" loading="lazy" decoding="async" class="mh-100 mw-100" style="object-fit: cover;" alt="@Translate("Missing image")"> 624 </div> 625 </div> 626 } 627 } 628
Beklager. Der er ikke noget at se her