Error executing template "/Designs/Swift/Paragraph/Swift_ProductDetailsInfo_Custom.cshtml"
System.Exception: Product id is requred to find a product.
at Dynamicweb.Core.Ensure.That[TException](Boolean condition, String message)
at Dynamicweb.Ecommerce.ProductCatalog.ViewModelFactory.CreateView(ProductViewModelSettings settings, String productId, String variantId, String groupId)
at Dynamicweb.Ecommerce.ProductCatalog.ProductInfoViewModelExtensions.GetProduct(ProductInfoViewModel productInfo)
at CompiledRazorTemplates.Dynamic.RazorEngine_776b9197f12542d7ba54831bd9dcd930.Execute() in D:\dynamicweb.net\Solutions\Dynamicweb\Jkoffice.cloud.dynamicweb-cms.com\Files\Templates\\Designs\Swift\Paragraph\Swift_ProductDetailsInfo_Custom.cshtml:line 515
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 System.IO
5 @using Dynamicweb.Core
6 @using Dynamicweb.Environment
7
8 @{
9 var getProductViewModelHelper = new CustomCode.ProductSelector.GetProductViewModelHelper();
10 ProductViewModel product = getProductViewModelHelper.ProductViewModel;
11
12 string googleAnalyticsTrackingID = Pageview.AreaSettings.GetString("GoogleAnalyticsTrackingID");
13 string googleAnalyticsMeasurementID = Pageview.AreaSettings.GetString("GoogleAnalyticsMeasurementID");
14 var cookieOptInLevel = CookieManager.GetCookieOptInLevel();
15 bool allowTracking = cookieOptInLevel == CookieOptInLevel.All || (cookieOptInLevel == CookieOptInLevel.Functional && CookieManager.GetCookieOptInCategories().Contains("Statistical"));
16
17 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", "");
18 bool anonymousUser = Pageview.User == null;
19 bool hideAddToCart = anonymousUsersLimitations.Contains("cart") && anonymousUser;
20 hideAddToCart = product.VariantInfo.VariantInfo != null && Model.Item.GetBoolean("HideVariantSelector") ? true : hideAddToCart;
21 hideAddToCart = Model.Item.GetBoolean("HideAddToCartButton") ? true : hideAddToCart;
22 bool hidePrice = anonymousUsersLimitations.Contains("price") && anonymousUser;
23 if (Model.Item.GetBoolean("HidePrice"))
24 {
25 hidePrice = true;
26 }
27 bool hideFavoritesSelector = !string.IsNullOrEmpty(Model.Item.GetString("HideFavoritesSelector")) ? Model.Item.GetBoolean("HideFavoritesSelector") : false;
28 bool hideProductData = Model.Item.GetBoolean("Hide_Product_Data");
29
30 bool IsNeverOutOfStock = product.NeverOutOfstock;
31 bool isDiscontinued = product.Discontinued;
32
33 string[] variantId = product.VariantId.Split('.');
34 string disableAddToCart = (product.StockLevel <= 0) ? "disabled" : "";
35 if (IsNeverOutOfStock)
36 {
37 disableAddToCart = "";
38 }
39
40 bool isAnonymousB2BUser = System.Web.HttpContext.Current.Request.Cookies["userType"] != null ? System.Web.HttpContext.Current.Request.Cookies["userType"].Value == "B2B" : false;
41
42
43 // Does product has a expected delivery data
44 bool hasExpectedDelivery = product.ExpectedDelivery != null && product.ExpectedDelivery > DateTime.Now;
45 string expectedDeliveryDate = product.ExpectedDelivery?.ToShortDateString() ?? "";
46
47 string url = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("CartService"));
48 if (!url.Contains("LayoutTemplate"))
49 {
50 url += url.Contains("?") ? "&LayoutTemplate=Swift_MiniCart.cshtml" : "?LayoutTemplate=Swift_MiniCart.cshtml";
51 }
52
53 IEnumerable<string> selectedDisplayGroups = Model.Item.GetRawValueString("MainFeatures").Split(',').ToList();
54 List<CategoryFieldViewModel> mainFeatures = new List<CategoryFieldViewModel>();
55
56 foreach (var selection in selectedDisplayGroups)
57 {
58 foreach (CategoryFieldViewModel group in product.FieldDisplayGroups.Values)
59 {
60 if (selection == group.Id)
61 {
62 mainFeatures.Add(group);
63 }
64 }
65 }
66
67 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : "";
68
69 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "display-6");
70 string subtitleFontSize = Model.Item.GetRawValueString("SubtitleFontSize", "h6");
71
72 string contentPadding = Model.Item.GetRawValueString("ContentPadding", "");
73 contentPadding = contentPadding == "small" ? "p-2 p-md-3" : contentPadding;
74 contentPadding = contentPadding == "large" ? "p-4 p-md-5" : contentPadding;
75
76 string quantityPricesLayout = Model.Item.GetRawValueString("QuantityPricesLayout", "list");
77
78 string minQty = product.PurchaseMinimumQuantity != 1 ? "min=\"" + product.PurchaseMinimumQuantity.ToString() + "\"" : "min=\"1\"";
79 string stepQty = product.PurchaseQuantityStep > 1 ? product.PurchaseQuantityStep.ToString() : "1";
80 string valueQty = product.PurchaseMinimumQuantity > product.PurchaseQuantityStep ? product.PurchaseMinimumQuantity.ToString() : stepQty;
81 string qtyValidCheck = stepQty != "1" ? "onkeyup=\"swift.Cart.QuantityValidate(event)\"" : "";
82
83 string showPricesWithVat = Pageview.Area.EcomPricesWithVat.ToLower();
84 bool neverShowVat = string.IsNullOrEmpty(showPricesWithVat);
85
86 string priceMin = "";
87 string priceMax = "";
88
89 var favoriteParameters = new Dictionary<string, object>();
90 if (!anonymousUser && !hideFavoritesSelector)
91 {
92 IEnumerable<FavoriteList> favoreiteLists = Pageview.User.GetFavoriteLists();
93 int defaultFavoriteListId = 0;
94
95 if (favoreiteLists.Count() == 1)
96 {
97 foreach (FavoriteList list in favoreiteLists)
98 {
99 defaultFavoriteListId = list.ListId;
100 }
101 }
102
103 favoriteParameters.Add("ListId", defaultFavoriteListId);
104 }
105
106 var badgeParms = new Dictionary<string, object>();
107 badgeParms.Add("size", "h7");
108 badgeParms.Add("saleBadgeType", Model.Item.GetRawValue("SaleBadgeType"));
109 badgeParms.Add("saleBadgeCssClassName", Model.Item.GetRawValue("SaleBadgeDesign"));
110 badgeParms.Add("newBadgeCssClassName", Model.Item.GetRawValue("NewBadgeDesign"));
111 badgeParms.Add("newPublicationDays", Model.Item.GetInt32("NewPublicationDays"));
112 badgeParms.Add("campaignBadgesValues", Model.Item.GetRawValueString("CampaignBadges"));
113
114 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("SaleBadgeDesign")) && Model.Item.GetRawValueString("SaleBadgeDesign") != "none" ? true : false;
115 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("NewBadgeDesign")) && Model.Item.GetRawValueString("NewBadgeDesign") != "none" ? true : false;
116 DateTime createdDate = product.Created.Value;
117 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false;
118 showBadges = (newBadgeEnabled && Model.Item.GetInt32("NewPublicationDays") == 0) || (newBadgeEnabled && (createdDate.AddDays(Model.Item.GetInt32("NewPublicationDays")) > DateTime.Now)) ? true : showBadges;
119 showBadges = !string.IsNullOrEmpty(Model.Item.GetRawValueString("CampaignBadges")) ? true : showBadges;
120 var productDB = Dynamicweb.Ecommerce.Services.Products.GetProductById(product.Id, product.VariantId, false);
121 var productState = productDB.GetCategoryValue("ImportedNAVItemAttributes", "ImportedNAVItemAttributes_1")?.ToString();
122 var deliveryTime = getProductViewModelHelper.DeliveryTime();
123 var isB2BUser = CustomCode.CustomUserHelpers.IsB2BUser();
124
125 }
126
127 @if (!string.IsNullOrWhiteSpace(googleAnalyticsMeasurementID) && allowTracking)
128 {
129 <script>
130 gtag("event", "view_item", {
131 currency: "@product.Price.CurrencyCode",
132 value: @product.Price.PriceWithVat.ToString(System.Globalization.CultureInfo.InvariantCulture),
133 items: [
134 {
135 item_id: "@product.Number",
136 item_name: "@product.Name",
137 currency: "@product.Price.CurrencyCode",
138 price: @product.Price.PriceWithVat.ToString(System.Globalization.CultureInfo.InvariantCulture)
139 }
140 ]
141 });
142 </script>
143 }
144
145
146 <div class="h-100 @(contentPadding) @(theme)">
147 <div class="d-flex flex-column gap-3 js-product @(!Model.Item.GetBoolean("HideAddToCartButton") ? "right-data" : "")">
148 @if (showBadges)
149 {
150 <div>
151 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms)
152 </div>
153 }
154
155 @if (!hideProductData)
156 {
157 <div>
158 <h1 class="@titleFontSize" itemprop="name">@product.Name</h1>
159 @if (!Model.Item.GetBoolean("HideProductNumber"))
160 {
161 <div class="@(!string.IsNullOrEmpty(product.Number) ? "" : "invisible") ">@Translate("Product number"): <span class="opacity-85" id="product-number-container">@getProductViewModelHelper.ProductNumbers()</span></div>
162 }
163
164 </div>
165
166 var isVariantMaster = product.VariantGroups().Count > 0 && product.VariantId == "";
167
168 if (!isVariantMaster)
169 {
170 var stockLevel = getProductViewModelHelper.StockLevel();
171 <div id="product-stock-amount">
172 @{ if (product.NeverOutOfstock && stockLevel <= 0)
173 {
174 if (deliveryTime != "0")
175 {
176 <div>
177 @RenderIcon("/Files/Templates/Designs/Swift/Assets/icons/check-circle.svg", "icon-2 text-success fw-bold")
178 <span class="fs-7 opacity-85 mb-2">
179 @(deliveryTime != "0" ? @Translate("[] days").Replace("[]", deliveryTime) : "")
180 </span>
181 </div>
182 }
183 }
184 else if (stockLevel > 0)
185 {
186 string defaultUnitID = product.DefaultUnitId;
187 var variantService = new Dynamicweb.Ecommerce.VariantOptionService();
188 string productLanguageID = product.LanguageId;
189 var defaultUnit = variantService.GetVariantOption(defaultUnitID, productLanguageID);
190 string defaultUnitName = defaultUnit.Name;
191
192 <div>
193 @RenderIcon("/Files/Templates/Designs/Swift/Assets/icons/check-circle.svg", "icon-2 text-success fw-bold")
194 <span class="fs-7 opacity-85 mb-2">@stockLevel @Translate(defaultUnitName)</span>
195 </div>
196 }
197 else
198 {
199 <div>
200 @if (productState != null && productState == "3")
201 {
202 @RenderIcon("/Files/Templates/Designs/Swift/Assets/icons/check-circle.svg", "icon-2 text-success fw-bold")
203 }
204 else
205 {
206 @RenderIcon("/Files/Templates/Designs/Swift/Assets/icons/minus-circle.svg", "icon-2 text-danger fw-bolder")
207 }
208
209 @(!string.IsNullOrEmpty(deliveryTime) && deliveryTime != "0" ? @Translate("[] days").Replace("[]", deliveryTime) : "")
210 </div>
211 } }
212 </div>
213 }
214
215 }
216
217 @if (!hidePrice)
218 {
219 <div id="price-container">
220 <div class="h4 m-0" itemprop="offers" itemscope itemtype="https://schema.org/Offer">
221 <span itemprop="priceCurrency" content="@product.Price.CurrencyCode" class="d-none"></span>
222
223 @if (showPricesWithVat == "false" && !neverShowVat)
224 {
225 string beforePrice = isB2BUser ? product.PriceBeforeDiscount.PriceWithoutVatFormatted : product.PriceBeforeDiscount.PriceWithVatFormatted;
226
227 <span itemprop="price" content="@product.Price.PriceWithoutVat" class="d-none"></span>
228 if (product.Price.Price != product.PriceBeforeDiscount.Price)
229 {
230 <span class="text-decoration-line-through opacity-75 me-3">@beforePrice</span>
231 }
232 }
233 else
234 {
235 string beforePrice = product.PriceBeforeDiscount.PriceFormatted;
236
237 <span itemprop="price" content="@product.Price.Price" class="d-none"></span>
238 if (product.Price.Price != product.PriceBeforeDiscount.Price)
239 {
240 <span class="text-decoration-line-through opacity-75 me-3">@beforePrice</span>
241 }
242 }
243
244 @if (showPricesWithVat == "false" && !neverShowVat)
245 {
246 string price = "";
247
248 if (isB2BUser)
249 {
250 price = product.Price.PriceFormatted;
251 if (product?.VariantInfo?.VariantInfo != null)
252 {
253 priceMin = product?.VariantInfo?.PriceMin?.PriceFormatted != null ? product.VariantInfo.PriceMin.PriceFormatted : "";
254 priceMax = product?.VariantInfo?.PriceMax?.PriceFormatted != null ? product.VariantInfo.PriceMax.PriceFormatted : "";
255 }
256 if (priceMin != priceMax)
257 {
258 price = priceMin + " - " + priceMax;
259 }
260 }
261
262 else
263 {
264 price = product.Price.PriceWithVatFormatted;
265 if (product?.VariantInfo?.VariantInfo != null)
266 {
267 priceMin = product?.VariantInfo?.PriceMin?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithVatFormatted : "";
268 priceMax = product?.VariantInfo?.PriceMax?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithVatFormatted : "";
269 }
270 if (priceMin != priceMax)
271 {
272 price = priceMin + " - " + priceMax;
273 }
274
275 }
276
277 <span class="text-price fw-bold fs-5">@price <small class="fs-8 fw-normal">(@(isB2BUser ? Translate("Excl. VAT") : Translate("Incl. VAT")))</small></span>
278 }
279 else
280 {
281 string price = product.Price.PriceFormatted;
282
283 if (product?.VariantInfo?.VariantInfo != null)
284 {
285 priceMin = product?.VariantInfo?.PriceMin?.PriceFormatted != null ? product.VariantInfo.PriceMin.PriceFormatted : "";
286 priceMax = product?.VariantInfo?.PriceMax?.PriceFormatted != null ? product.VariantInfo.PriceMax.PriceFormatted : "";
287 }
288 if (priceMin != priceMax)
289 {
290 price = priceMin + " - " + priceMax;
291 }
292 <span class="text-price">@price</span>
293 }
294 </div>
295
296 @if (CustomCode.CustomUserHelpers.IsB2BUser() == true || isAnonymousB2BUser)
297 {
298 <div>
299 @{ FieldValueViewModel rentalPrice = new FieldValueViewModel();
300 if (product.ProductFields.TryGetValue("ProductRentalPrice", out rentalPrice))
301 {
302 if (rentalPrice.Value?.ToString() != "0")
303 {
304 <span>@Translate("Rental Price:") @(rentalPrice.Value?.ToString()) @Dynamicweb.Ecommerce.Common.Context.Currency.Symbol</span>
305 }
306 }
307 }
308 </div>
309 }
310
311
312 @if (Pageview?.User != null)
313 {
314 if (Pageview.User.CurrentSecondaryUser != null)
315 {
316 <div>
317 @{ FieldValueViewModel stockLocation = new FieldValueViewModel();
318 if (product.ProductFields.TryGetValue("productStockLocation", out stockLocation))
319 {
320 <span>@Translate("Stock Location"): @(stockLocation.Value?.ToString())</span>
321 }
322 }
323 </div>
324 }
325 }
326
327 @if (product.Prices.Count > 0)
328 {
329 if (quantityPricesLayout == "list")
330 {
331 <div class="mt-3">
332 @foreach (PriceListViewModel quantityPrice in product.Prices)
333 {
334 string quantityLabel = Translate("PCS");
335 string quantityPriceSuffix = quantityPrice.Quantity > 1 ? Translate("pr. PCS") : "";
336
337 <small class="d-block opacity-75"><span>@quantityPrice.Quantity @quantityLabel</span> - <span class="fw-bold">@quantityPrice.Price.PriceFormatted @quantityPriceSuffix</span></small>
338 }
339 </div>
340 }
341 else if (quantityPricesLayout == "table")
342 {
343 <div class="grid">
344 <table class="table table-sm mt-3 g-col-12 g-col-lg-6">
345 <thead>
346 <tr>
347 <td>@Translate("QTY")</td>
348 <td>@Translate("pr. PCS")</td>
349 </tr>
350 </thead>
351 <tbody>
352 @foreach (PriceListViewModel quantityPrice in product.Prices)
353 {
354 <tr>
355 <td>@quantityPrice.Quantity</td>
356 <td>@quantityPrice.Price.PriceFormatted</td>
357 </tr>
358 }
359 </tbody>
360 </table>
361 </div>
362 }
363 }
364 </div>
365 }
366
367 @if (!string.IsNullOrEmpty(product.ShortDescription) && !hideProductData)
368 {
369 <div class="mb-0-last-child" itemprop="disambiguatingDescription">
370 @product.ShortDescription
371 </div>
372 }
373
374 @if (mainFeatures.Count > 0)
375 {
376 foreach (CategoryFieldViewModel mainFeatureGroup in mainFeatures)
377 {
378 <dl class="grid gap-0">
379 @foreach (var field in mainFeatureGroup.Fields)
380 {
381 @RenderField(field.Value, subtitleFontSize)
382 }
383 </dl>
384 }
385 }
386
387 @if (product.VariantInfo.VariantInfo != null && !Model.Item.GetBoolean("HideVariantSelector"))
388 {
389 int groupNumber = 1;
390
391 <form class="mb-3 js-variant-selector" data-combinations="@string.Join(",", product.VariantCombinations())">
392 <input type="hidden" name="variantid" />
393
394 @foreach (var variantGroup in product.VariantGroups())
395 {
396 VariantGroupViewModel group = variantGroup;
397
398 <h3 class="h6">@group.Name</h3>
399 <div class="mb-3 js-variant-group" data-group-id="@groupNumber">
400 @foreach (var option in group.Options)
401 {
402 string active = variantId.Contains(option.Id) ? "active" : "";
403
404 if (!string.IsNullOrEmpty(option.Color))
405 {
406 <button type="button" class="btn colorbox rounded-circle me-1 mb-2 d-inline-block variant-option js-variant-option @active" style="background-color: @option.Color" onclick="swift.VariantSelector.OptionClick(event)" data-variant-id="@option.Id"></button>
407 }
408 else if (!string.IsNullOrEmpty(option.Color) && !string.IsNullOrEmpty(option.Image.Value))
409 {
410 <button type="button" class="btn p-0 d-inline-block mb-2 variant-option js-variant-option @active" onclick="swift.VariantSelector.OptionClick(event)" data-variant-id="@option.Id">
411 <img src="/Admin/Public/GetImage.ashx?image=@(option.Image.Value)&width=42&Format=WebP&Quality=70" />
412 </button>
413 }
414 else
415 {
416 <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">
417 @option.Name
418 </button>
419 }
420 }
421 </div>
422
423 groupNumber++;
424 }
425 </form>
426 }
427 <div class="d-flex flex-row flex-nowrap gap-2">
428 @if (!hideAddToCart)
429 {
430 <form method="post" action="@url" class="flex-fill">
431 <input type="hidden" name="redirect" value="false" />
432 <input type="hidden" name="ProductId" value="@product.Id" />
433 <input type="hidden" name="cartcmd" value="add" />
434
435 @if (!string.IsNullOrEmpty(product.VariantId))
436 {
437 <input type="hidden" name="VariantId" value="@product.VariantId" />
438 }
439 @if (!Model.Item.GetBoolean("QuantitySelector"))
440 {
441 <input id="Quantity_@product.Id" name="Quantity" value="@valueQty" type="hidden">
442 <button type="button" onclick="swift.Cart.Update(event)" class="btn btn-primary w-100 js-add-to-cart-button @disableAddToCart" @disableAddToCart title="@Translate("Add to cart")" id="AddToCartButton@(product.Id)">@Translate("Add to cart")</button>
443 }
444 else
445 {
446 <div class="input-group input-primary-button-group js-input-group d-flex flex-row flex-nowrap">
447 <label for="Quantity_@(product.Id)" class="visually-hidden">@Translate("Quantity")</label>
448 <input id="Quantity_@product.Id" name="Quantity" value="@valueQty" step="@stepQty" @minQty class="form-control" style="max-width: 96px; min-width:64px;" type="number">
449 <button type="button" onclick="swift.Cart.Update(event)" class="btn btn-primary flex-fill js-add-to-cart-button @disableAddToCart" @disableAddToCart title="@Translate("Add to cart")" id="AddToCartButton@(product.Id)">@Translate("Add to cart")</button>
450 </div>
451
452 if (stepQty != "1")
453 {
454 <div class="invalid-feedback d-none">
455 @Translate("Please select a quantity that is dividable by") @stepQty
456 </div>
457 }
458 }
459 </form>
460 if (!anonymousUser && !hideFavoritesSelector)
461 {
462 @RenderPartial("Components/ToggleFavorite.cshtml", product, favoriteParameters)
463 }
464 }
465 else if (!anonymousUser && !hideFavoritesSelector)
466 {
467 <div class="flex-fill">
468 @Translate("Add to favorites") @RenderPartial("Components/ToggleFavorite.cshtml", product, favoriteParameters)
469 </div>
470 }
471 </div>
472
473 @if (!Model.Item.GetBoolean("HideAddToCartButton"))
474 {
475 <div id="dw-placeholder"></div>
476 }
477 </div>
478 @if (!Model.Item.GetBoolean("HideStockState"))
479 {
480 if (!IsNeverOutOfStock)
481 {
482 <div class="mt-3 js-stock-state">
483
484 @if (product.StockLevel > 0)
485 {
486 if (!Model.Item.GetBoolean("HideInventory"))
487 {
488 <p class="small text-success m-0">@product.StockLevel @Translate("Products available in stock")</p>
489 }
490 else
491 {
492 <p class="small text-success m-0">@Translate("Available in stock")</p>
493 }
494 }
495
496 else
497 {
498 <p class="small text-danger m-0">@Translate("Out of Stock")</p>
499 }
500
501 @if (hasExpectedDelivery)
502 {
503 <p>
504 <span>@Translate("Expected back in stock:")</span>
505 <span>@expectedDeliveryDate</span>
506 </p>
507 }
508
509 </div>
510 }
511 }
512
513 @if (isDiscontinued && product.ReplacementProduct != null)
514 {
515 var replacementProduct = product?.ReplacementProduct.GetProduct();
516
517 if ((product.DiscontinuedAction == 0 || product.DiscontinuedAction == 1) && product?.ReplacementProduct.ProductId != null)
518 {
519 var parms = new Dictionary<string, object>();
520 parms.Add("cssClass", "d-block mw-100 mh-100 m-auto");
521 parms.Add("fullwidth", true);
522 parms.Add("columns", Model.GridRowColumnCount);
523
524 string imagePath = CustomCode.ImageHelper.GetImagePath(product);
525
526 string link = "Default.aspx?ID=" + GetPageIdByNavigationTag("Shop");
527 if(replacementProduct.PrimaryOrDefaultGroup != null)
528 {
529 link += $"&GroupID={replacementProduct.PrimaryOrDefaultGroup.Id}";
530 }
531 link += $"&ProductID={replacementProduct.Id}";
532 link += !string.IsNullOrEmpty(replacementProduct.VariantId) ? $"&VariantID={replacementProduct.VariantId}" : "";
533 link = Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl(link);
534
535 <div class="w-100">
536 <div class="fw-bold w-100">@Translate("Sorry, this product is no longer available").</div>
537 <div>@Translate("We recommend this replacement product instead"):</div>
538
539 <a href="@link">
540 @RenderPartial("Components/Image.cshtml", new Dynamicweb.Frontend.FileViewModel { Path = imagePath }, parms)
541 </a>
542
543 <div>@replacementProduct.Name</div>
544
545 <a href="@link" class="btn btn-primary w-100">@Translate("Go to the replacement")</a>
546 </div>
547 }
548 }
549 </div>
550
551 @helper RenderField(FieldValueViewModel field, string subtitleFontSize)
552 {
553 string fieldValue = field?.Value != null ? field.Value.ToString() : "";
554 bool noValues = false;
555
556 if (!string.IsNullOrEmpty(fieldValue))
557 {
558 if (field.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>))
559 {
560 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>;
561 noValues = values.Count > 0 ? false : true;
562 }
563 }
564
565 if (!string.IsNullOrEmpty(fieldValue) && noValues == false)
566 {
567 <dt class="g-col-12 g-col-sm-4 g-col-lg-12 fw-bold m-0 @subtitleFontSize">@Translate(field.Name)</dt>
568 <dd class="g-col-12 g-col-sm-8 g-col-lg-12 mb-3">
569 @RenderFieldValue(field)
570 </dd>
571 }
572 }
573
574 @helper RenderFieldValue(FieldValueViewModel field)
575 {
576 string fieldValue = field?.Value != null ? field.Value.ToString() : "";
577
578 fieldValue = fieldValue == "False" ? Translate("No") : fieldValue;
579 fieldValue = fieldValue == "True" ? Translate("Yes") : fieldValue;
580
581 bool isColor = false;
582
583 if (field.Value.GetType() == typeof(System.Collections.Generic.List<Dynamicweb.Ecommerce.ProductCatalog.FieldOptionValueViewModel>))
584 {
585 int valueCount = 0;
586 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>;
587 int totalValues = values.Count;
588
589 foreach (FieldOptionValueViewModel option in values)
590 {
591 if (option.Value.Substring(0, 1) == "#")
592 {
593 isColor = true;
594 }
595
596 if (!isColor)
597 {
598 @option.Name
599 }
600 else
601 {
602 <span class="colorbox-sm" style="background-color: @option.Value" title="@option.Value"></span>
603 }
604
605 if (valueCount != totalValues && valueCount < (totalValues - 1))
606 {
607 if (isColor)
608 {
609 <text> </text>
610 }
611 else
612 {
613 <text>, </text>
614 }
615 }
616 valueCount++;
617 }
618 }
619 else
620 {
621 if (fieldValue.Substring(0, 1) == "#")
622 {
623 isColor = true;
624 }
625
626 if (!isColor)
627 {
628 @fieldValue
629 }
630 else
631 {
632 <span class="colorbox-sm" style="background-color: @fieldValue" title="@fieldValue"></span>
633 }
634 }
635 }
636
637 @if (product.VariantInfo.VariantInfo != null)
638 {
639 <script type="module">
640 swift.VariantSelector.init();
641 </script>
642 }
643
644 @helper RenderIcon(string icon, string iconSize)
645 {
646 if (Path.GetExtension(icon).ToLower() == ".svg" && !icon.ToLower().Contains("none"))
647 {
648 string iconPath = Dynamicweb.Context.Current.Server.MapPath(icon);
649
650 <span class="@iconSize">
651 @ReadFile(iconPath)
652 </span>
653 }
654 }
655