Favorite list updated
Error executing template "Designs/Swift/Paragraph/Swift_ProductDetailsInfo_Custom.cshtml" System.NullReferenceException: Object reference not set to an instance of an object. at CompiledRazorTemplates.Dynamic.RazorEngine_f4fa5840804d45df8b3388b79991949a.Execute() in D:\dynamicweb.net\Solutions\twodayco3\OttoSch.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_ProductDetailsInfo_Custom.cshtml:line 307 at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader) at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer) at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter) at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template) at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template) at Dynamicweb.Rendering.Template.RenderRazorTemplate()
1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 @using Dynamicweb.Ecommerce.CustomerExperienceCenter.Favorites 4 @using Dynamicweb.Ecommerce.Products 5 @using Dynamicweb.Core 6 @using System.Web 7 @using Dynamicweb.Environment 8 @using Dynamicweb.Frontend 9 10 @{ 11 bool isVisualEditor = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("VisualEdit")) ? Convert.ToBoolean(Dynamicweb.Context.Current.Request.QueryString.Get("VisualEdit")) : false; 12 13 ProductViewModel product = new ProductViewModel(); 14 15 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 16 { 17 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 18 } 19 20 bool anonymousUser = Pageview.User == null; 21 22 string[] variantId = product.VariantId.Split('.'); 23 string disableAddToCart = string.IsNullOrEmpty(product.VariantId) && product.VariantInfo.VariantInfo != null ? "disabled" : ""; 24 string hideStockState = string.IsNullOrEmpty(product.VariantId) && product.VariantInfo.VariantInfo != null ? "d-none" : ""; 25 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", ""); 26 bool hideAddToCart = anonymousUsersLimitations.Contains("cart") && anonymousUser; 27 hideAddToCart = product.VariantInfo.VariantInfo != null && Model.Item.GetBoolean("HideVariantSelector") ? true : hideAddToCart; 28 bool hidePrice = anonymousUsersLimitations.Contains("price") && anonymousUser; 29 bool hideFavoritesSelector = !string.IsNullOrEmpty(Model.Item.GetString("HideFavoritesSelector")) ? Model.Item.GetBoolean("HideFavoritesSelector") : false; 30 31 ProductService productService = new ProductService(); 32 Product ecomProduct = productService.GetProductById(product.Id, product.VariantId, true); 33 34 double purchaseQuantityStep = Converter.ToDouble(productService.GetPropertyValue(ecomProduct, "PurchaseQuantityStep")); 35 purchaseQuantityStep = purchaseQuantityStep > 0 ? purchaseQuantityStep : 1; 36 string url = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("CartService")); 37 if (!url.Contains("LayoutTemplate")) 38 { 39 url += url.Contains("?") ? "&LayoutTemplate=Swift_MiniCart.cshtml" : "?LayoutTemplate=Swift_MiniCart.cshtml"; 40 } 41 42 IEnumerable<string> selectedDisplayGroups = Model.Item.GetList("MainFeatures").SelectedValues; 43 List<CategoryFieldViewModel> mainFeatures = new List<CategoryFieldViewModel>(); 44 45 foreach (var selection in selectedDisplayGroups) 46 { 47 foreach (CategoryFieldViewModel group in product.FieldDisplayGroups.Values) 48 { 49 if (selection == group.Id) 50 { 51 mainFeatures.Add(group); 52 } 53 } 54 } 55 56 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 57 58 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "display-6"); 59 60 string contentPadding = Model.Item.GetRawValueString("ContentPadding", ""); 61 contentPadding = contentPadding == "small" ? "p-2 p-md-3" : contentPadding; 62 contentPadding = contentPadding == "large" ? "p-4 p-md-5" : contentPadding; 63 64 string minQty = product.PurchaseMinimumQuantity != 1 ? "min=\"" + product.PurchaseMinimumQuantity.ToString() + "\"" : "min=\"1\""; 65 string stepQty = product.PurchaseQuantityStep > 1 ? product.PurchaseQuantityStep.ToString() : "1"; 66 string valueQty = product.PurchaseMinimumQuantity > product.PurchaseQuantityStep ? product.PurchaseMinimumQuantity.ToString() : stepQty; 67 string qtyValidCheck = stepQty != "1" ? "onkeyup=\"swift.Cart.QuantityValidate(event)\"" : ""; 68 69 bool purchaseDeactivated = Converter.ToBoolean(productService.GetProductFieldValue(ecomProduct, "SpPurchaseDeactivated")); 70 string showPricesWithVat = Pageview.Area.EcomPricesWithVat.ToLower(); 71 bool neverShowVat = string.IsNullOrEmpty(showPricesWithVat); 72 73 string priceMin = ""; 74 string priceMax = ""; 75 76 var favoriteParameters = new Dictionary<string, object>(); 77 if (!anonymousUser && !hideFavoritesSelector) 78 { 79 int defaultFavoriteListId = 0; 80 81 IEnumerable<FavoriteList> favoriteLists = Pageview.User.GetFavoriteLists(); 82 if (favoriteLists.Count() == 1) 83 { 84 defaultFavoriteListId = favoriteLists.First().ListId; 85 } 86 87 favoriteParameters.Add("ListId", defaultFavoriteListId); 88 } 89 } 90 91 <div class="h-100 @(contentPadding) @(theme)"> 92 <div class="d-flex flex-column gap-2 js-product"> 93 <div> 94 @if (!Converter.ToBoolean(ecomProduct.GetProductFieldValue("SpManufacturerOverride"))) 95 { 96 if (!string.IsNullOrEmpty(ecomProduct.Manufacturer?.Logo)) 97 { 98 <img class="brand-logo mb-2" src="/Admin/Public/GetImage.ashx?image=@ecomProduct.Manufacturer.Logo&width=100&Format=WebP&Quality=99" alt="@HttpUtility.HtmlAttributeEncode(ecomProduct.Manufacturer.Name)" /> 99 } 100 } 101 else 102 { 103 string alternativeBrandLogo = product.ProductFields["SpAlternativeManufacturerBrandLogo"]?.Value.ToString(); 104 105 if (!string.IsNullOrEmpty(alternativeBrandLogo)) 106 { 107 <img class="brand-logo mb-2" src="/Admin/Public/GetImage.ashx?image=@alternativeBrandLogo&width=100&Format=WebP&Quality=99" alt="Brand Logo" /> 108 } 109 } 110 111 <h1 class="@titleFontSize" itemprop="name">@product.Name</h1> 112 @if (!Model.Item.GetBoolean("HideProductNumber")) 113 { 114 <div class="opacity-85">@product.Number</div> 115 } 116 </div> 117 118 @if (!ecomProduct.IsVariantMaster && !hidePrice) 119 { 120 <div> 121 <div class="h4" itemprop="offers" itemscope itemtype="https://schema.org/Offer"> 122 <span itemprop="priceCurrency" content="@product.Price.CurrencyCode" class="d-none"></span> 123 124 @if (showPricesWithVat == "false" && !neverShowVat) 125 { 126 string beforePrice = product.PriceBeforeDiscount.PriceWithoutVatFormatted; 127 128 <span itemprop="price" content="@product.Price.PriceWithoutVat" class="d-none"></span> 129 if (product.Price.Price != product.PriceBeforeDiscount.Price) 130 { 131 <span class="text-decoration-line-through opacity-75 me-3">@beforePrice</span> 132 } 133 } 134 else 135 { 136 string beforePrice = product.PriceBeforeDiscount.PriceFormatted; 137 138 <span itemprop="price" content="@product.Price.Price" class="d-none"></span> 139 if (product.Price.Price != product.PriceBeforeDiscount.Price) 140 { 141 <span class="text-decoration-line-through opacity-75 me-3">@beforePrice</span> 142 } 143 } 144 145 @if (showPricesWithVat == "false" && !neverShowVat) 146 { 147 string price = product.Price.PriceWithoutVatFormatted; 148 if (product?.VariantInfo?.VariantInfo != null) 149 { 150 priceMin = product?.VariantInfo?.PriceMin?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithoutVatFormatted : ""; 151 priceMax = product?.VariantInfo?.PriceMax?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithoutVatFormatted : ""; 152 } 153 if (priceMin != priceMax) 154 { 155 price = priceMin + " - " + priceMax; 156 } 157 <span class="text-price">@price</span> 158 } 159 else 160 { 161 string price = product.Price.PriceFormatted; 162 if (product?.VariantInfo?.VariantInfo != null) 163 { 164 priceMin = product?.VariantInfo?.PriceMin?.PriceFormatted != null ? product.VariantInfo.PriceMin.PriceFormatted : ""; 165 priceMax = product?.VariantInfo?.PriceMax?.PriceFormatted != null ? product.VariantInfo.PriceMax.PriceFormatted : ""; 166 } 167 if (priceMin != priceMax) 168 { 169 price = priceMin + " - " + priceMax; 170 } 171 <span class="text-price">@price</span> 172 } 173 </div> 174 @if (showPricesWithVat == "false" && !neverShowVat) 175 { 176 string price = product.Price.PriceWithVatFormatted; 177 if (product?.VariantInfo?.VariantInfo != null) 178 { 179 priceMin = product?.VariantInfo?.PriceMin?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithVatFormatted : ""; 180 priceMax = product?.VariantInfo?.PriceMax?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithVatFormatted : ""; 181 } 182 if (priceMin != priceMax) 183 { 184 price = priceMin + " - " + priceMax; 185 } 186 <small class="opacity-85 fst-normal">@price @Translate("Incl. VAT")</small> 187 } 188 </div> 189 } 190 else if (anonymousUser) 191 { 192 <p class="fw-bold">@Translate("Log ind for at se priser og købe produktet")</p> 193 } 194 195 @if (!string.IsNullOrEmpty(product.ShortDescription)) 196 { 197 <div class="mb-0-last-child" itemprop="disambiguatingDescription"> 198 @product.ShortDescription 199 </div> 200 } 201 202 @if (mainFeatures.Count > 0) 203 { 204 <div class="grid gap-0"> 205 @foreach (CategoryFieldViewModel mainFeatureGroup in mainFeatures) 206 { 207 foreach (var field in mainFeatureGroup.Fields) 208 { 209 @RenderField(field.Value, ecomProduct) 210 } 211 } 212 </div> 213 } 214 215 @if (product.VariantInfo.VariantInfo != null && !Model.Item.GetBoolean("HideVariantSelector")) 216 { 217 int groupNumber = 1; 218 string baseUrl = Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl($"Default.aspx?ID={GetPageIdByNavigationTag("Shop")}&GroupID={product.PrimaryOrDefaultGroup.Id}&ProductID={product.Id}"); 219 220 <form class="mb-3 js-variant-selector" data-combinations="@string.Join(",", product.VariantCombinations())" data-base-url="@baseUrl"> 221 <input type="hidden" name="variantid" /> 222 223 @foreach (var variantGroup in product.VariantGroups()) 224 { 225 VariantGroupViewModel group = variantGroup; 226 227 <h3 class="h6">@Translate($"Smartpage:VariantGroup.Name.{group.Name}", group.Name)</h3> 228 <div class="mb-3 js-variant-group" data-group-id="@groupNumber"> 229 @foreach (var option in group.Options) 230 { 231 string active = variantId.Contains(option.Id) ? "active" : ""; 232 233 <button type="button" class="btn btn-secondary d-inline-block mb-2 variant-option js-variant-option @active" onclick="swift.VariantSelector.OptionClick(event)" data-variant-id="@option.Id"> 234 @option.Name 235 </button> 236 } 237 </div> 238 groupNumber++; 239 } 240 </form> 241 } 242 <div class="d-flex flex-row justify-content-between align-items-center"> 243 @if (product.VariantInfo.VariantInfo != null && !string.IsNullOrWhiteSpace(product.VariantId) && !anonymousUser) 244 { 245 @RenderAddToCart(product, ecomProduct, productService, hideAddToCart, purchaseDeactivated, url, valueQty, stepQty, minQty, purchaseQuantityStep, hideAddToCart) 246 } 247 else if (product.VariantInfo.VariantInfo != null && string.IsNullOrWhiteSpace(product.VariantId) && !anonymousUser) 248 { 249 <p class="mb-0 fw-bold">@Translate("Du skal vælge variant før du kan tilføje til kurv")</p> 250 } 251 else if (product.VariantInfo.VariantInfo == null && !anonymousUser) 252 { 253 @RenderAddToCart(product, ecomProduct, productService, hideAddToCart, purchaseDeactivated, url, valueQty, stepQty, minQty, purchaseQuantityStep, hideAddToCart) 254 } 255 256 257 @if (!anonymousUser && !hideFavoritesSelector) 258 { 259 @RenderPartial("Components/ToggleFavorite.cshtml", product, favoriteParameters) 260 } 261 </div> 262 @if (!anonymousUser) 263 { 264 if (!purchaseDeactivated) 265 { 266 if (!Model.Item.GetBoolean("HideStockState")) 267 { 268 <div class="mb-2 js-stock-state @hideStockState"> 269 @if (product.StockLevel > 0) 270 { 271 if (!Model.Item.GetBoolean("HideInventory")) 272 { 273 <p class="small text-success m-0">@product.StockLevel @Translate("Products available in stock")</p> 274 } 275 else 276 { 277 <p class="small text-success m-0">@Translate("Available in stock")</p> 278 } 279 } 280 else 281 { 282 <p class="small text-danger m-0">@Translate("Out of Stock")</p> 283 } 284 </div> 285 } 286 } 287 } 288 <!--CUSTOM Pictograms start--> 289 <div class="pictogram__section"> 290 <div class="grid"> 291 @{ 292 foreach (var productCategory in product.ProductCategories) 293 { 294 productCategory.Value.Fields.TryGetValue("SpStandardLink", out var productSupportLink); 295 var disabled = productSupportLink != null ? "" : "custom-link-disabled"; 296 for (int i = 1; i <= 10; i++) 297 { 298 string searchTerm = "SpStandard" + i; 299 if (productCategory.Value.Fields.TryGetValue(searchTerm + "Icon", out var icon) && !string.IsNullOrEmpty((string)icon.Value)) 300 { 301 var filePath = icon.Value.ToString(); 302 var absolutePath = System.Web.HttpContext.Current.Server.MapPath("~/" + filePath); 303 if (System.IO.File.Exists(absolutePath)) 304 { 305 306 productCategory.Value.Fields.TryGetValue(searchTerm + "TextOver", out var textOver); 307 productCategory.Value.Fields.TryGetValue(searchTerm + "TextBelow", out var textBelow); 308 309 <div class="pictoimage-container"> 310 <div class="pictogram__image-container grid__cell"> 311 <a class="pictolink @disabled" href="@productSupportLink.Value"> 312 <p class="pictotext">@textOver.Value</p> 313 <img class="pictogram__image" src="@icon.Value"> 314 <p class="pictotext">@textBelow.Value</p> 315 </a> 316 </div> 317 </div> 318 319 } 320 } 321 } 322 } 323 } 324 </div> 325 </div> 326 <!--CUSTOM Pictograms end--> 327 </div> 328 </div> 329 330 @helper RenderAddToCart(ProductViewModel product, Product ecomProduct, ProductService productService, bool hideAddToCart, bool purchaseDeactivated, string url, string valueQty, string stepQty, string minQty, double purchaseQuantityStep, bool disableAddToCart) 331 { 332 if (!hideAddToCart) 333 { 334 if (!purchaseDeactivated) 335 { 336 <form method="post" action="@url" class="flex-fill"> 337 <input type="hidden" name="redirect" value="false" /> 338 <input type="hidden" name="ProductId" value="@product.Id" /> 339 <input type="hidden" name="cartcmd" value="add" /> 340 341 <div class="input-group input-primary-button-group js-input-group d-flex flex-row flex-nowrap w-50"> 342 @if (!string.IsNullOrEmpty(product.VariantId)) 343 { 344 <input type="hidden" name="VariantId" value="@product.VariantId" /> 345 } 346 @if (!Model.Item.GetBoolean("QuantitySelector")) 347 { 348 <input id="Quantity_@product.Id" name="Quantity" value="@valueQty" type="hidden"> 349 350 } 351 else 352 { 353 <input id="Quantity_@product.Id" name="Quantity" value="@valueQty" step="@stepQty" @minQty class="form-control js-quantity" style="max-width: 6rem; min-width:4rem;" type="number"> 354 if (stepQty != "1") 355 { 356 <div class="invalid-feedback d-none"> 357 @Translate("Please select a quantity that is dividable by") @stepQty 358 </div> 359 } 360 } 361 <button type="button" onclick="swift.Cart.Update(event)" class="btn btn-primary flex-fill js-add-to-cart-button @disableAddToCart" title="@Translate("Add to cart")" id="AddToCartButton@(product.Id)">@Translate("Add to cart")</button> 362 </div> 363 @{ 364 string salesUnit = Translate("Smartpage:SalesUnit", "stk"); 365 if (productService.GetProductFieldValue(ecomProduct, "SpSalesUnit") != null) 366 { 367 salesUnit = Converter.ToString(productService.GetProductFieldValue(ecomProduct, "SpSalesUnit")); 368 } 369 } 370 <div class="mt-1">@Translate("Smartpage:Product.QuantityStepText", "Min. antal: ") @purchaseQuantityStep @salesUnit</div> 371 372 </form> 373 } 374 else 375 { 376 <div class="d-flex align-items-center flex-fill">@Translate("Smartpage:Product.PurchaseDeactivated", "Produkt kan ikke bestilles, kontakt salgssupport") </div> 377 } 378 } 379 } 380 381 @helper RenderField(FieldValueViewModel field, Product ecomProduct) 382 { 383 string fieldValue = field?.Value != null ? field.Value.ToString() : ""; 384 bool noValues = false; 385 var ecomProductFieldValue = ecomProduct.ProductFieldValues.SingleOrDefault(fieldVal => fieldVal.ProductField.SystemName == field.SystemName)?.Value; 386 387 if (!string.IsNullOrEmpty(fieldValue)) 388 { 389 if (field.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 390 { 391 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 392 values = values.Where(x => !string.IsNullOrEmpty(x.Value)).ToList(); 393 noValues = values.Count > 0 ? false : true; 394 } 395 } 396 397 if (!string.IsNullOrEmpty(fieldValue) && noValues == false && ecomProductFieldValue == null) 398 { 399 <ul class="g-col-12 g-col-sm-8 g-col-lg-12 mb-1"> 400 <li>@RenderFieldValue(field, ecomProduct)</li> 401 </ul> 402 } 403 if (ecomProductFieldValue != null) 404 { 405 <dt class="g-col-12 g-col-sm-4 g-col-lg-12 fw-bold m-0">@field.Name</dt> 406 <dd class="g-col-12 g-col-sm-8 g-col-lg-12 mb-3"> 407 @RenderFieldValue(field, ecomProduct) 408 </dd> 409 } 410 } 411 412 @helper RenderFieldValue(FieldValueViewModel field, Product ecomProduct) 413 { 414 string fieldValue = field?.Value != null ? field.Value.ToString() : ""; 415 var ecomProductFieldValue = ecomProduct.ProductFieldValues.SingleOrDefault(fieldVal => fieldVal.ProductField.SystemName == field.SystemName)?.Value; 416 417 fieldValue = fieldValue == "False" ? Translate("No") : fieldValue; 418 fieldValue = fieldValue == "True" ? Translate("Yes") : fieldValue; 419 420 bool isColor = false; 421 422 if (field.Value.GetType() == typeof(System.Collections.Generic.List<Dynamicweb.Ecommerce.ProductCatalog.FieldOptionValueViewModel>)) 423 { 424 int valueCount = 0; 425 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 426 //Remove values with no content 427 values = values.Where(x => !string.IsNullOrEmpty(x.Value)).ToList(); 428 int totalValues = values.Count; 429 430 foreach (FieldOptionValueViewModel option in values) 431 { 432 if (option.Value.Substring(0, 1) == "#") 433 { 434 isColor = true; 435 } 436 437 if (!isColor) 438 { 439 @option.Name 440 } 441 else 442 { 443 <span class="colorbox-sm" style="background-color: @option.Value" title="@option.Value"></span> 444 } 445 446 if (valueCount != totalValues && valueCount < (totalValues - 1)) 447 { 448 if (isColor) 449 { 450 <text> </text> 451 } 452 else 453 { 454 <text>, </text> 455 } 456 } 457 valueCount++; 458 } 459 } 460 else if (!string.IsNullOrEmpty(fieldValue)) 461 { 462 if (fieldValue.Substring(0, 1) == "#") 463 { 464 isColor = true; 465 } 466 467 if (!isColor && ecomProductFieldValue != null) 468 { 469 @ecomProductFieldValue 470 } 471 else 472 { 473 @fieldValue 474 } 475 } 476 } 477 478 @if (product.VariantInfo.VariantInfo != null) 479 { 480 <script> 481 //Initialize the variant selector 482 document.addEventListener('load', function (event) { 483 swift.VariantSelector.init(); 484 }); 485 </script> 486 } 487