Error executing template "Designs/Swift/eCom/ProductCatalog/ProductViewDetail.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_d9d3d34f34144af1b5ba0e96b2f1cd8f.ExecuteAsync()
   at RazorEngine.Templating.TemplateBase.Run(ExecuteContext context, TextWriter reader)
   at RazorEngine.Templating.RazorEngineCore.RunTemplate(ICompiledTemplate template, TextWriter writer, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.DynamicWrapperService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass23_0.<Run>b__0(TextWriter writer)
   at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, Type modelType, Object model, DynamicViewBag viewBag)
   at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template)
   at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template)
   at Dynamicweb.Rendering.Template.RenderRazorTemplate()

1 @inherits ViewModelTemplate<ProductViewModel> 2 @using Dynamicweb.Rendering 3 @using Dynamicweb.Ecommerce.ProductCatalog 4 @using Dynamicweb.Core 5 6 @{ 7 string metaDescription = string.IsNullOrEmpty(Model.MetaDescription) ? Model.Name : Model.MetaDescription; 8 9 Pageview.Meta.AddTag($"<meta property=\"og:image\" content=\"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{Model.DefaultImage.Value}\">"); 10 Pageview.Meta.AddTag($"<meta property=\"og:image:alt\" content=\"{Model.Name}\">"); 11 Pageview.Meta.AddTag($"<meta property=\"og:description\" content=\"{metaDescription}\">"); 12 13 Pageview.Meta.AddTag("twitter:image", Model.DefaultImage.Value); 14 Pageview.Meta.AddTag("twitter:image:alt", Model.Name); 15 Pageview.Meta.AddTag("twitter:description", metaDescription); 16 } 17 18 @{ 19 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 20 { 21 Dynamicweb.Context.Current.Items["ProductDetails"] = Model; 22 } 23 else 24 { 25 Dynamicweb.Context.Current.Items.Add("ProductDetails", Model); 26 } 27 28 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]); 29 if (isLazyLoadingForProductInfoEnabled) 30 { 31 string showPricesWithVat = Pageview.Area.EcomPricesWithVat.ToLower(); 32 bool neverShowVat = string.IsNullOrEmpty(showPricesWithVat); 33 bool hasVariantId = !string.IsNullOrEmpty(Model.VariantId); 34 string variantIdParam = hasVariantId ? $"/{Model.VariantId}" : ""; 35 string priceFilledProperties = $"Price,PriceFormatted{(showPricesWithVat == "false" && !neverShowVat ? ",PriceWithVat,PriceWithVatFormatted" : "")}"; 36 string productInfoFeed = $@"/dwapi/ecommerce/products/{Model.Id}{variantIdParam} 37 ?UserId={Converter.ToString(Pageview.User?.ID)} 38 &LanguageId={Pageview.Area.EcomLanguageId}&CurrencyCode={Pageview.Area.EcomCurrencyId}&CountryCode={Pageview.Area.EcomCountryCode}&ShopId={Pageview.Area.EcomShopId} 39 &FilledProperties=Id,Price,PriceBeforeDiscount,StockLevel,VariantInfo,NeverOutOfstock,Prices 40 &PriceSettings.ShowPricesWithVat={Pageview.Area.EcomPricesWithVat} 41 &PriceSettings.FilledProperties={priceFilledProperties} 42 &getproductinfo=true"; 43 Dynamicweb.Context.Current.Items["ProductInfoFeed"] = productInfoFeed; 44 45 <script type="module"> 46 swift.LiveProductInfo.init(); 47 </script> 48 } 49 } 50 51 <script> 52 gtag("event", "view_item", { 53 currency: "@Model.Price.CurrencyCode", 54 value: @PriceViewModelExtensions.ToStringInvariant(Model.Price), 55 items: [ 56 { 57 item_id: "@Model.Number", 58 item_name: "@Dynamicweb.Core.Encoders.HtmlEncoder.JavaScriptStringEncode(Model.Name)", 59 currency: "@Model.Price.CurrencyCode", 60 price: @PriceViewModelExtensions.ToStringInvariant(Model.Price) 61 } 62 ] 63 }); 64 </script> 65 66 <script> 67 window.addEventListener('load', function (event) { 68 swift.Video.init(); 69 }); 70 </script> 71
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_3ac0081da37a4a56ad08c8bd8f7a3b91.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 = includeImagePatternImages ? assetsList.Union(product.ImagePatternImages) : assetsList; 143 assetsList = assetsList.Union(assetsImages); 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 // Custom 150 bool showDownloadIcons = Model.Item.GetBoolean("CustomShowDownloadIcons"); 151 152 int totalAssets = 0; 153 if (showOnlyPrimaryImage == false) 154 { 155 foreach (MediaViewModel asset in assetsList) 156 { 157 var assetValue = asset.Value; 158 foreach (string format in allSupportedFormats) 159 { 160 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 161 { 162 totalAssets++; 163 } 164 } 165 } 166 } 167 @* 168 <span>AE: @totalAssets</span> 169 *@ 170 if ((totalAssets == 0 && product.DefaultImage != null && selectedAssetCategories.Count() == 0) || (showOnlyPrimaryImage == true && product.DefaultImage != null) || totalAssets == 0 && defaultImageFallback) 171 { 172 assetsList = new List<MediaViewModel>() { product.DefaultImage }; 173 totalAssets = 1; 174 } 175 176 @* Theme settings *@ 177 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 178 179 var badgeParms = new Dictionary<string, object>(); 180 badgeParms.Add("size", "h5"); 181 badgeParms.Add("saleBadgeType", Model.Item.GetRawValue("SaleBadgeType")); 182 badgeParms.Add("saleBadgeCssClassName", Model.Item.GetRawValue("SaleBadgeDesign")); 183 badgeParms.Add("newBadgeCssClassName", Model.Item.GetRawValue("NewBadgeDesign")); 184 badgeParms.Add("newPublicationDays", Model.Item.GetInt32("NewPublicationDays")); 185 badgeParms.Add("campaignBadgesValues", Model.Item.GetList("CampaignBadges")?.GetRawValue().OfType<string>().ToList()); 186 187 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("SaleBadgeDesign")) && Model.Item.GetRawValueString("SaleBadgeDesign") != "none" ? true : false; 188 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("NewBadgeDesign")) && Model.Item.GetRawValueString("NewBadgeDesign") != "none" ? true : false; 189 DateTime createdDate = product.Created.Value; 190 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false; 191 showBadges = (newBadgeEnabled && Model.Item.GetInt32("NewPublicationDays") == 0) || (newBadgeEnabled && (createdDate.AddDays(Model.Item.GetInt32("NewPublicationDays")) > DateTime.Now)) ? true : showBadges; 192 showBadges = !string.IsNullOrEmpty(Model.Item.GetRawValueString("CampaignBadges")) ? true : showBadges; 193 194 @* Get assets from selected categories or get all assets *@ 195 if (totalAssets != 0) 196 { 197 int assetNumber = 0; 198 int thumbnailNumber = 0; 199 int modalAssetNumber = 0; 200 string thumbnailAxisCss = GetThumbnailPlacement() == "bottom" ? "flex-column" : string.Empty; 201 202 <div class="d-flex gap-3 h-100 @(thumbnailAxisCss) @(theme) item_@Model.Item.SystemName.ToLower()"> 203 <div id="SmallScreenImages_@Model.ID" class="carousel@(GetArrowsColor()) col position-relative" data-bs-ride="carousel" @(showDownloadIcons ? "style=\"border: 1px solid #d9d9d9 !important;\"" : "")> 204 <div class="carousel-inner h-100"> 205 @foreach (MediaViewModel asset in assetsList) 206 { 207 var assetValue = asset.Value; 208 foreach (string format in allSupportedFormats) 209 { 210 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 211 { 212 string activeSlide = assetNumber == 0 ? "active" : ""; 213 214 <div class="carousel-item @activeSlide" data-bs-interval="99999"> 215 @{ 216 string size = "mobile"; 217 218 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 219 220 221 <div class="h-100 @(imageTheme)"> 222 @foreach (string imageFormat in supportedImageFormats) 223 { //Images 224 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 225 { 226 if (product is object) 227 { 228 string productName = product.Name; 229 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 230 string imageLinkPath = Dynamicweb.Context.Current.Server.UrlEncode(imagePath); 231 232 RatioSettings ratioSettings = GetRatioSettings(size); 233 234 var parms = new Dictionary<string, object>(); 235 parms.Add("alt", productName + asset.Keywords); 236 parms.Add("itemprop", "image"); 237 parms.Add("columns", Model.GridRowColumnCount); 238 parms.Add("eagerLoadNewImages", Model.Item.GetBoolean("DisableLazyLoading")); 239 parms.Add("doNotUseGetimage", Model.Item.GetBoolean("DisableGetImage")); 240 if (!string.IsNullOrEmpty(asset.DisplayName)) 241 { 242 parms.Add("title", asset.DisplayName); 243 } 244 245 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") 246 { 247 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 248 } 249 else 250 { 251 parms.Add("cssClass", "mw-100 mh-100"); 252 } 253 254 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 255 <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"> 256 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 257 </div> 258 </a> 259 @if (showDownloadIcons) 260 { 261 <a href="@imagePath" download class="position-absolute bottom-0 end-0 m-2 text-dark" title="Download billede"> 262 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" 263 stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" 264 class="feather feather-download"> 265 <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path> 266 <polyline points="7 10 12 15 17 10"></polyline> 267 <line x1="12" y1="15" x2="12" y2="3"></line> 268 </svg> 269 </a> 270 } 271 } 272 } 273 } 274 @foreach (string videoFormat in supportedVideoFormats) 275 { //Videos 276 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 277 { 278 if (Model.Item.GetString("OpenVideoInModal") == "true") 279 { 280 if (product is object) 281 { 282 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 283 284 string productName = product.Name; 285 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 286 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 287 288 RatioSettings ratioSettings = GetRatioSettings(size); 289 290 string type = GetVideoType(asset.Value); 291 292 string videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value, "maxresdefault") : string.Empty; 293 videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeUriString(asset.Value) : videoScreendumpPath; 294 string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : ""; 295 296 297 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable); cursor: pointer" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 298 <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"> 299 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 300 @if (type != "selfhosted") 301 { 302 <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;"> 303 } 304 else 305 { 306 string videoType = Path.GetExtension(asset.Value).ToLower(); 307 308 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 309 <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> 310 </video> 311 } 312 </div> 313 </div> 314 315 } 316 } 317 else 318 { 319 if (product is object) 320 { 321 var videoParams = GetVideoParams(asset, size); 322 @RenderPartial("Components/VideoPlayer.cshtml", new FileViewModel { Path = asset.Value}, videoParams); 323 } 324 } 325 } 326 } 327 @foreach (string documentFormat in supportedDocumentFormats) 328 { //Documents 329 if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) 330 { 331 if (product is object) 332 { 333 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 334 335 string productName = product.Name; 336 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 337 string imageLinkPath = imagePath; 338 339 RatioSettings ratioSettings = GetRatioSettings(size); 340 341 var parms = new Dictionary<string, object>(); 342 parms.Add("alt", productName + asset.Keywords); 343 parms.Add("itemprop", "image"); 344 parms.Add("fullwidth", true); 345 parms.Add("columns", Model.GridRowColumnCount); 346 if (!string.IsNullOrEmpty(asset.DisplayName)) 347 { 348 parms.Add("title", asset.DisplayName); 349 } 350 351 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") 352 { 353 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 354 } 355 else 356 { 357 parms.Add("cssClass", "mw-100 mh-100"); 358 } 359 360 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download alt="@Translate("Download")"> 361 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 362 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 363 @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) 364 { 365 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 366 } 367 </div> 368 </a> 369 } 370 371 } 372 } 373 </div> 374 } 375 376 377 </div> 378 assetNumber++; 379 } 380 } 381 } 382 </div> 383 @if (showBadges) 384 { 385 <div class="position-absolute top-0 left-0 p-2 p-lg-3"> 386 @{@RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms)} 387 </div> 388 } 389 390 </div> 391 392 @if (totalAssets > 1) 393 { 394 <div class="@(GetThumbnailRowSettingCss()) gap-3" id="SmallScreenImagesThumbnails_@Model.ID"> 395 @foreach (MediaViewModel asset in assetsList) 396 { 397 var assetValue = asset.Value; 398 string assetName = asset.Name; 399 assetName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 400 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : null; 401 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 402 403 string imagePath = Dynamicweb.Context.Current.Server.UrlEncode(assetValue); 404 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; 405 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; 406 407 RatioSettings ratioSettings = GetRatioSettings("desktop"); 408 409 bool isImageThumb = supportedImageFormats.Any(f => assetValue.IndexOf(f, StringComparison.OrdinalIgnoreCase) >= 0); 410 411 <div class="position-relative d-inline-block" style="min-width: 7rem; max-width: 8rem;"> 412 <div class="border outline-none position-relative @(ratioSettings.CssClass)" style="@(ratioSettings.CssVariable); cursor: pointer; min-width: 7rem; max-width: 8rem;"> 413 <button type="button" class="stretched-link p-0 m-0 border-0 bg-transparent" data-bs-target="#SmallScreenImages_@Model.ID" data-bs-slide-to="@thumbnailNumber" aria-label="@Translate("Show image")" style="z-index:1;"></button> 414 @foreach (string imageFormat in supportedImageFormats) 415 { //Images 416 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 417 { 418 <img src="@imagePathThumb" alt="@assetName" @assetTitle class="p-0 p-lg-1 w-100 h-100" style="object-fit: contain;"> 419 thumbnailNumber++; 420 } 421 } 422 423 @foreach (string videoFormat in supportedVideoFormats) 424 { //Videos 425 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 426 { 427 428 string type = GetVideoType(asset.Value); 429 430 string videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value, "mqdefault") : ""; 431 videoScreendumpPath = type == "vimeo" ? string.Empty : videoScreendumpPath; 432 videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeUriString(asset.Value) : videoScreendumpPath; 433 string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : string.Empty; 434 435 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 436 437 if (type != "selfhosted") 438 { 439 <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;" /> 440 } 441 else 442 { 443 string videoType = Path.GetExtension(asset.Value).ToLower(); 444 445 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 446 <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> 447 </video> 448 } 449 450 thumbnailNumber++; 451 } 452 } 453 454 @foreach (string documentFormat in supportedDocumentFormats) 455 { //Documents 456 if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) 457 { 458 <a href="@assetValue" class="ratio ratio-4x3 border outline-none" style="cursor: pointer; min-width: 7rem; max-width: 8rem;" download title="@asset.Value"> 459 @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) 460 { 461 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 462 <div class="icon-3 position-absolute text-light" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 463 </div> 464 <img src="@imagePathThumb" alt="@assetName" @assetTitle class="p-0 p-lg-1 mw-100 mh-100" style="object-fit: cover;"> 465 } 466 else 467 { 468 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 469 <div class="icon-3 position-absolute" style="z-index: 1">@ReadFile(iconPath + "file-text.svg")</div> 470 </div> 471 } 472 </a> 473 474 thumbnailNumber++; 475 } 476 } 477 </div> 478 @if (isImageThumb && showDownloadIcons) 479 { 480 <a href="@assetValue" download title="@Translate("Download")" class="position-absolute bottom-0 end-0 m-1 text-decoration-none text-dark" style="line-height:0; z-index:2;"> 481 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather feather-download"> 482 <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path> 483 <polyline points="7 10 12 15 17 10"></polyline> 484 <line x1="12" y1="15" x2="12" y2="3"></line> 485 </svg> 486 </a> 487 } 488 </div> 489 } 490 </div> 491 } 492 </div> 493 494 @* Modal with slides *@ 495 <div class="modal fade swift_products-details-images-modal" id="modal_@Model.ID" tabindex="-1" aria-labelledby="productDetailsGalleryModalTitle_@Model.ID" aria-hidden="true"> 496 <div class="modal-dialog modal-dialog-centered modal-xl"> 497 <div class="modal-content"> 498 <div class="modal-header visually-hidden"> 499 <h5 class="modal-title" id="productDetailsGalleryModalTitle_@Model.ID">@product.Title</h5> 500 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 501 </div> 502 <div class="modal-body p-2 p-lg-3 h-100"> 503 <div id="ModalCarousel_@Model.ID" class="carousel@(GetArrowsColor()) h-100" data-bs-ride="carousel"> 504 <div class="carousel-inner h-100 @theme"> 505 @foreach (MediaViewModel asset in assetsList) 506 { 507 var assetValue = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 508 foreach (string supportedFormat in supportedImageFormats.Concat(supportedVideoFormats).ToArray()) 509 { 510 if (assetValue.IndexOf(supportedFormat, StringComparison.OrdinalIgnoreCase) >= 0) 511 { 512 string imagePath = assetValue; 513 string activeSlide = modalAssetNumber == 0 ? "active" : ""; 514 515 var parms = new Dictionary<string, object>(); 516 parms.Add("cssClass", "d-block mw-100 mh-100 m-auto"); 517 parms.Add("fullwidth", true); 518 parms.Add("columns", Model.GridRowColumnCount); 519 520 <div class="carousel-item @activeSlide h-100" data-bs-interval="99999"> 521 @foreach (string imageFormat in supportedImageFormats) 522 { //Images 523 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 524 { 525 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 526 } 527 } 528 529 @foreach (string videoFormat in supportedVideoFormats) 530 { //Videos 531 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 532 { 533 if (product is object) 534 { 535 var videoParams = GetVideoParams(asset, "modal"); 536 @RenderPartial("Components/VideoPlayer.cshtml", new FileViewModel { Path = asset.Value }, videoParams) 537 } 538 } 539 } 540 </div> 541 modalAssetNumber++; 542 } 543 } 544 } 545 <button class="carousel-control-prev carousel-control-area" type="button" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide="prev"> 546 <span class="carousel-control-prev-icon" aria-hidden="true"></span> 547 <span class="visually-hidden">@Translate("Previous")</span> 548 </button> 549 <button class="carousel-control-next carousel-control-area" type="button" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide="next"> 550 <span class="carousel-control-next-icon" aria-hidden="true"></span> 551 <span class="visually-hidden">@Translate("Next")</span> 552 </button> 553 </div> 554 </div> 555 </div> 556 </div> 557 </div> 558 </div> 559 } 560 else if (Pageview.IsVisualEditorMode) 561 { 562 RatioSettings ratioSettings = GetRatioSettings("desktop"); 563 564 <div class="h-100 @theme"> 565 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> 566 <img src="/Files/Images/missing_image.jpg" loading="lazy" decoding="async" class="mh-100 mw-100" style="object-fit: cover;"> 567 </div> 568 </div> 569 } 570 } 571 else if (Pageview.IsVisualEditorMode) 572 { 573 <div class="alert alert-dark m-0">@Translate("No products available")</div> 574 } 575 576 577 578

Error executing template "Designs/Swift/Paragraph/Swift_ProductAddToDownloadCart.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_77115af3880d47be93b46d36aa4f6cd7.ExecuteAsync()
   at RazorEngine.Templating.TemplateBase.Run(ExecuteContext context, TextWriter reader)
   at RazorEngine.Templating.RazorEngineCore.RunTemplate(ICompiledTemplate template, TextWriter writer, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.DynamicWrapperService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass23_0.<Run>b__0(TextWriter writer)
   at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, Type modelType, Object model, DynamicViewBag viewBag)
   at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template)
   at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template)
   at Dynamicweb.Rendering.Template.RenderRazorTemplate()

1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 @using Dynamicweb.Ecommerce.CustomerExperienceCenter.Favorites 4 5 @{ 6 ProductViewModel product = null; 7 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 8 { 9 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 10 } 11 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 12 { 13 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 14 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 15 16 if (productList?.Products is object) 17 { 18 product = productList.Products[0]; 19 } 20 } 21 22 bool isErpConnectionDown = !Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsWebServiceConnectionAvailable"]); 23 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", ""); 24 bool anonymousUser = Pageview.User == null; 25 bool hideAddToCart = anonymousUsersLimitations.Contains("cart") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHideAddToCart") && isErpConnectionDown; 26 hideAddToCart = Pageview.IsVisualEditorMode ? false : hideAddToCart; 27 } 28 29 @if (product is object && !hideAddToCart) 30 { 31 string contextCart = Model.Item.GetRawValueString("ContextCart", ""); 32 33 string formurl = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("CartService")); 34 if (!formurl.Contains("LayoutTemplate")) 35 { 36 formurl += formurl.Contains("?") ? "&LayoutTemplate=Swift_MiniCart.cshtml" : "?LayoutTemplate=Swift_MiniCart.cshtml"; 37 } 38 39 string horizontalAlign = Model.Item.GetRawValueString("HorizontalAlignment", ""); 40 horizontalAlign = horizontalAlign == "center" ? "justify-content-center" : horizontalAlign; 41 horizontalAlign = horizontalAlign == "end" ? "justify-content-end" : horizontalAlign; 42 horizontalAlign = horizontalAlign == "full" ? "" : horizontalAlign; 43 44 string buttonSize = Model.Item.GetRawValueString("ButtonSize", "regular"); 45 buttonSize = buttonSize == "small" ? " btn-sm" : buttonSize; 46 buttonSize = buttonSize == "regular" ? string.Empty : buttonSize; 47 buttonSize = buttonSize == "large" ? " btn-lg" : buttonSize; 48 49 string iconPath = "/Files/icons/"; 50 string url = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("CartService")); 51 if (!url.Contains("LayoutTemplate")) 52 { 53 url += url.Contains("?") ? "&LayoutTemplate=Swift_MiniCart.cshtml" : "?LayoutTemplate=Swift_MiniCart.cshtml"; 54 } 55 56 string cartCommand = "add"; 57 string buttonStyle = "btn-primary"; 58 bool inCart = false; 59 60 string fullWidth = Model.Item.GetRawValueString("HorizontalAlignment", "") == "full" ? "w-100" : ""; 61 string addToCartIcon = Model.Item.GetRawValueString("Icon", iconPath + "download.svg"); 62 string addToCartLabel = ""; 63 64 if (!string.IsNullOrEmpty(contextCart)) 65 { 66 addToCartLabel += !addToCartIcon.Contains("_none") ? "<span class=\"icon-2\">" + ReadFile(addToCartIcon) + "</span>" : ""; 67 addToCartLabel += !addToCartIcon.Contains("_none") && !Model.Item.GetBoolean("HideButtonText") ? " " : ""; 68 69 if (product.IsProductInCart(contextCart)) 70 { 71 addToCartLabel += !Model.Item.GetBoolean("HideButtonText") ? Translate("Remove from download cart") : ""; 72 cartCommand = "delorderline"; 73 buttonStyle = "btn-secondary"; 74 inCart = true; 75 } 76 else 77 { 78 addToCartLabel += !Model.Item.GetBoolean("HideButtonText") ? Translate("Add to download cart") : ""; 79 } 80 81 <div class="d-flex @horizontalAlign @fullWidth item_@Model.Item.SystemName.ToLower()"> 82 <form method="post" action="@formurl" class="@fullWidth" style="z-index: 1"> 83 <input type="hidden" name="OrderContext" value="@contextCart"> 84 <input type="hidden" name="minicartid" value="@contextCart"> 85 <input type="hidden" name="redirect" value="false"> 86 <input type="hidden" name="ProductId" value="@product.Id"> 87 88 @if (!string.IsNullOrEmpty(product.VariantId)) 89 { 90 <input type="hidden" name="VariantId" value="@product.VariantId"> 91 } 92 93 <input name="RenderOrderlineCountInsteadOfProductCount" value="true" type="hidden"> 94 <input name="Quantity" value="1" type="hidden"> 95 96 <input type="hidden" name="cartcmd" value="@cartCommand"> 97 <button type="button" class="btn @(buttonStyle)@(buttonSize) js-add-to-cart-button @fullWidth" onclick="swift.Cart.Update(event); this.addEventListener('updated.swift.cart', updateDownloadCartButton(this))" data-in-cart="@inCart" id="AddToDownloadCartButton@(product.Id)@(product.VariantId.Replace(".", "_"))"> 98 99 @if (!Model.Item.GetBoolean("HideButtonText")) 100 { 101 <span class="text-nowrap d-flex align-items-center justify-content-center gap-2"> 102 @addToCartLabel 103 </span> 104 } 105 else 106 { 107 @addToCartLabel 108 } 109 </button> 110 </form> 111 112 <script> 113 function debounce(func, wait, immediate) { 114 var timeout; 115 return function () { 116 var context = this, args = arguments; 117 var later = function () { 118 timeout = null; 119 if (!immediate) func.apply(context, args); 120 }; 121 var callNow = immediate && !timeout; 122 clearTimeout(timeout); 123 timeout = setTimeout(later, wait); 124 if (callNow) func.apply(context, args); 125 }; 126 }; 127 128 updateDownloadCartButton = debounce(function (clickedButton) { 129 var inCart = clickedButton.getAttribute('data-in-cart'); 130 131 let parser = new DOMParser(); 132 const icon = parser.parseFromString(clickedButton.innerHTML, 'text/html').querySelector(".icon-2"); 133 134 if (inCart == 'True') { 135 const label = icon != null ? icon.outerHTML + " @Translate("Add to download cart")" : "@Translate("Add to download cart")"; 136 137 clickedButton.parentNode.querySelector('input[name="cartcmd"]').value = 'add'; 138 clickedButton.innerHTML = '<span class="text-nowrap">' + label + '</span>'; 139 clickedButton.classList.add('btn-primary'); 140 clickedButton.classList.remove('btn-secondary'); 141 clickedButton.setAttribute('data-in-cart', 'False'); 142 } else { 143 const label = icon != null ? icon.outerHTML + " @Translate("Remove from download cart")" : "@Translate("Remove from download cart")"; 144 145 clickedButton.parentNode.querySelector('input[name="cartcmd"]').value = 'delorderline'; 146 clickedButton.innerHTML = '<span class="text-nowrap">' + label + '</span>'; 147 clickedButton.classList.remove('btn-primary'); 148 clickedButton.classList.add('btn-secondary'); 149 clickedButton.setAttribute('data-in-cart', 'True'); 150 } 151 }, 300); 152 </script> 153 </div> 154 } 155 else if (Pageview.IsVisualEditorMode) 156 { 157 <div class="alert alert-dark m-0">@Translate("Please create and select a context cart")</div> 158 } 159 } 160 else if (Pageview.IsVisualEditorMode) 161 { 162 <div class="alert alert-dark m-0">@Translate("No products available")</div> 163 } 164
Error executing template "Designs/Swift/Paragraph/Swift_ProductDownloadPublication.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_8034c4da673b41298669a3a518cfdf2e.ExecuteAsync()
   at RazorEngine.Templating.TemplateBase.Run(ExecuteContext context, TextWriter reader)
   at RazorEngine.Templating.RazorEngineCore.RunTemplate(ICompiledTemplate template, TextWriter writer, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.DynamicWrapperService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass23_0.<Run>b__0(TextWriter writer)
   at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, Type modelType, Object model, DynamicViewBag viewBag)
   at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template)
   at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template)
   at Dynamicweb.Rendering.Template.RenderRazorTemplate()

1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 @using Dynamicweb.Ecommerce.CustomerExperienceCenter.Favorites 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) 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 uniqueId = $"{product?.Id}_{product?.VariantId.Replace(".", "_")}_{Pageview.CurrentParagraph.ID}"; 24 25 var selectedCatalogs = Model.Item.GetList("SelectedCatalogs")?.GetRawValue()?.OfType<string>(); 26 int catalogCount = 0; 27 28 string horizontalAlign = Model.Item.GetRawValueString("HorizontalAlignment", string.Empty); 29 horizontalAlign = horizontalAlign == "center" ? "justify-content-center" : horizontalAlign; 30 horizontalAlign = horizontalAlign == "end" ? "justify-content-end" : horizontalAlign; 31 horizontalAlign = horizontalAlign == "full" ? string.Empty : horizontalAlign; 32 33 string buttonSize = Model.Item.GetRawValueString("ButtonSize", "regular"); 34 35 switch (buttonSize) 36 { 37 case "small": 38 buttonSize = " btn-sm"; 39 break; 40 case "regular": 41 buttonSize = string.Empty; 42 break; 43 case "large": 44 buttonSize = " btn-lg"; 45 break; 46 } 47 48 string iconPath = "/Files/icons/"; 49 50 string flexFill = Model.Item.GetRawValueString("HorizontalAlignment", "") == "full" ? "flex-fill" : string.Empty; 51 string fullWidth = Model.Item.GetRawValueString("HorizontalAlignment", "") == "full" ? "w-100" : string.Empty; 52 string icon = Model.Item.GetRawValueString("Icon", iconPath + "printer.svg"); 53 string label = !icon.Contains("_none") ? $"<span class=\"icon-2\">{ReadFile(icon)}</span>" : string.Empty; 54 label += !icon.Contains("_none") && !Model.Item.GetBoolean("HideButtonText") ? " " : string.Empty; 55 label += !Model.Item.GetBoolean("HideButtonText") && !string.IsNullOrEmpty(Model.Item.GetString("Title")) ? Model.Item.GetString("Title") : string.Empty; 56 } 57 58 @if (product is object && selectedCatalogs != null && selectedCatalogs.Any()) 59 { 60 var ecomLanguages = Dynamicweb.Ecommerce.Services.Languages.GetLanguages(); 61 var currencies = Dynamicweb.Ecommerce.Services.Currencies.GetAllCurrencies(); 62 var languageId = Dynamicweb.Ecommerce.Common.Context.LanguageID; 63 var areaEcomLanguageId = Pageview.Area.EcomLanguageId; 64 var areaEcomCurrencyId = Pageview.Area.EcomCurrencyId; 65 66 <div class="d-flex @horizontalAlign @fullWidth item_@Model.Item.SystemName.ToLower()"> 67 <button type="button" class="btn btn-primary @(buttonSize) @flexFill" data-bs-toggle="modal" data-bs-target="#DownloadPublicationModal@(uniqueId)" style="white-space: nowrap" title="@Model.Item.GetString("Title")" id="DownloadPublication@(uniqueId)"> 68 @if (!Model.Item.GetBoolean("HideButtonText")) 69 { 70 <span class="text-nowrap d-flex align-items-center justify-content-center gap-2"> 71 @label 72 </span> 73 } 74 else 75 { 76 @label 77 } 78 </button> 79 </div> 80 81 <div class="modal fade" id="DownloadPublicationModal@(uniqueId)" tabindex="-1" aria-labelledby="#DownloadPublicationModalLabel@(uniqueId)" aria-hidden="true"> 82 <div class="modal-dialog modal-xl modal-dialog-centered modal-dialog-scrollable"> 83 <form target="_blank" action="/Default.aspx?ID=@selectedCatalogs.FirstOrDefault()" method="post" class="modal-content" id="DownloadPublicationModalForm@(uniqueId)"> 84 <input type="hidden" name="mainproductid" value="@product.Id"> 85 <input type="hidden" name="print" value="true"> 86 87 @if (!string.IsNullOrEmpty(product.VariantId)) 88 { 89 <input type="hidden" name="VariantId" value="@product.VariantId"> 90 } 91 92 <div class="modal-header"> 93 <h1 class="modal-title fs-5" id="DownloadPublicationModalLabel@(uniqueId)">@Translate("Publication for print")</h1> 94 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 95 </div> 96 <div class="modal-body p-0"> 97 <div class="grid gap-0"> 98 <div class="g-col-12 g-col-lg-4 border-end p-3 p-lg-4"> 99 100 @if (Model.Item.GetBoolean("ShowLanguageSelector") && ecomLanguages.Count() > 1) 101 { 102 <div class="mb-4"> 103 <label class="form-label">@Translate("Language")</label> 104 <select name="RequestLanguageID" class="form-select" aria-label="@Translate("Language")"> 105 @foreach (var language in ecomLanguages) 106 { 107 var selected = string.Empty; 108 if (language.LanguageId.Equals(areaEcomLanguageId, StringComparison.OrdinalIgnoreCase)) 109 { 110 selected = "selected"; 111 } 112 <option @selected value="@language.LanguageId">@language.GetDisplayName()</option> 113 } 114 </select> 115 </div> 116 } 117 118 119 @if (Model.Item.GetBoolean("ShowCurrencySelector")) 120 { 121 <div class="mb-4" id="CurrencySelector@(uniqueId)"> 122 <label class="form-label">@Translate("Currency")</label> 123 <select name="RequestCurrencyCode" class="form-select" aria-label="@Translate("Currency")"> 124 @foreach (var currency in currencies) 125 { 126 var selected = string.Empty; 127 if (currency.Code.Equals(areaEcomCurrencyId, StringComparison.OrdinalIgnoreCase)) 128 { 129 selected = "selected"; 130 } 131 <option @selected value="@currency.Code">@currency.GetName(languageId)</option> 132 } 133 </select> 134 </div> 135 } 136 137 @if (Model.Item.GetBoolean("ShowPriceSelector")) 138 { 139 <div class="mb-4"> 140 <label class="form-label">@Translate("Show price")</label> 141 <div class="form-check"> 142 <input class="form-check-input" type="radio" name="hideprice" value="false" id="HidePriceFalse@(uniqueId)" checked onchange="document.querySelector('#CurrencySelector@(uniqueId)').classList.remove('d-none')"> 143 <label class="form-check-label" for="HidePriceFalse@(uniqueId)"> 144 @Translate("Yes") 145 </label> 146 </div> 147 <div class="form-check"> 148 <input class="form-check-input" type="radio" name="hideprice" value="true" id="HidePriceTrue@(uniqueId)" onchange="document.querySelector('#CurrencySelector@(uniqueId)').classList.add('d-none')"> 149 <label class="form-check-label" for="HidePriceTrue@(uniqueId)"> 150 @Translate("No") 151 </label> 152 </div> 153 </div> 154 } 155 156 @if (Model.Item.GetBoolean("ShowCommentField")) 157 { 158 <div class="mb-4"> 159 <label for="DealerComment@(uniqueId)">@Translate("Comment")</label> 160 <textarea name="DealerComment" class="form-control" rows="6" placeholder="@Translate("Leave a comment here")" id="DealerComment@(uniqueId)"></textarea> 161 </div> 162 } 163 </div> 164 <div class="g-col-12 g-col-lg-8 p-3 p-lg-4"> 165 <div class="grid grid-2 grid-lg-3 gap-lg-4"> 166 @foreach (var catalog in selectedCatalogs) 167 { 168 var pageObj = Dynamicweb.Content.Services.Pages.GetPage(Convert.ToInt32(catalog)); 169 170 var selected = catalogCount == 0 ? "checked" : string.Empty; 171 var radioId = $"CatalogCheck_{Model.ID}_{pageObj.ID}"; 172 string imagePath = pageObj.Item["Thumbnail"] != null ? $"/Admin/Public/GetImage.ashx?image={pageObj.Item["Thumbnail"].ToString()}&width=200&format=webp" : $"https://placehold.co/235x300?text={@Translate("Catalog+Image")}"; 173 174 <div class="form-thumb"> 175 <input class="form-thumb-input visually-hidden" type="radio" name="ID" value="@pageObj.ID" id="@radioId" onclick="document.querySelector('#DownloadPublicationModalForm@(uniqueId)').action='/Default.aspx?ID=@(pageObj.ID)'" @selected> 176 <label class="form-thumb-label d-flex flex-column" for="@radioId" role="button"> 177 <span class="d-block p-1"> 178 <img class="rounded-2 w-100" style="object-fit:cover" src="@imagePath" /> 179 </span> 180 <span class="d-grid p-3"> 181 <span class="d-block fw-bold">@pageObj.GetDisplayName()</span> 182 @*<span class="d-block opacity-75 fs-7 lh-sm">@catalog.Name</span> // Descriptive text goes here //*@ 183 </span> 184 </label> 185 </div> 186 187 catalogCount++; 188 } 189 </div> 190 </div> 191 </div> 192 </div> 193 <div class="modal-footer"> 194 <button type="button" onclick="GetPublication(event)" data-bs-dismiss="modal" class="btn btn-primary">@Translate("Print")</button> 195 </div> 196 </form> 197 </div> 198 </div> 199 200 <script> 201 const GetPublication = (e) => { 202 const clickedButton = e.currentTarget; 203 const form = clickedButton.closest('form'); 204 205 form.closest('form').submit(); 206 207 commentField = form.querySelector('textarea[name="DealerComment"]'); 208 commentField.value = ""; 209 }; 210 </script> 211 } 212 else if (Pageview.IsVisualEditorMode) 213 { 214 <div class="d-flex @horizontalAlign @fullWidth item_@Model.Item.SystemName.ToLower()"> 215 <button type="button" class="btn btn-primary @(buttonSize) @flexFill" data-bs-toggle="modal" data-bs-target="#DownloadPublicationModal@(uniqueId)" style="white-space: nowrap" title="@Model.Item.GetString("Title")" id="DownloadPublication@(uniqueId)"> 216 @if (!Model.Item.GetBoolean("HideButtonText")) 217 { 218 <span class="text-nowrap d-flex align-items-center justify-content-center gap-2"> 219 @label 220 </span> 221 } 222 else 223 { 224 @label 225 } 226 </button> 227 </div> 228 } 229
By clicking 'Accept All' you consent that we may collect information about you for various purposes, including: Statistics and Marketing