Error executing template "Designs/Rapido/eCom/Product/NZProductView.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
at CompiledRazorTemplates.Dynamic.RazorEngine_c6618fdf76704013bf6f840d6a21583b.Execute() in E:\Solutions\S_DW_Lauridsen\lauridsen-live\Files\Templates\Designs\Rapido\eCom\Product\NZProductView.cshtml:line 40
at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader)
at RazorEngine.Templating.RazorEngineCore.RunTemplate(ICompiledTemplate template, TextWriter writer, Object model, DynamicViewBag viewBag)
at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
at RazorEngine.Templating.DynamicWrapperService.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 RazorEngine.Templating.RazorEngineServiceExtensions.RunCompile(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.RazorTemplateBase<Dynamicweb.Rendering.RazorTemplateModel<Dynamicweb.Rendering.Template>>
2 @using Dynamicweb.Extensibility
3 @using Dynamicweb.Content
4 @using System;
5 @using System.Globalization;
6 @using System.Linq
7 @using System.IO
8 @using Dynamicweb.Core
9 @using System.Web
10 @using Dynamicweb.Ecommerce
11 @using Dynamicweb.Ecommerce.Discounts
12 @using Dynamicweb.Ecommerce.Orders.Discounts
13 @using Dynamicweb.Ecommerce.Products
14 @using Dynamicweb.Ecommerce.Prices
15 @using NLog;
16 @using S_DW_Lauridsen.CustomCode
17 @using Dynamicweb.Security.UserManagement
18 @using NuGet.Versioning
19 @{
20
21 string favoritPage = String.Format("/Default.aspx?ID={0}&CC20=CreateFormList", GetPageIdByNavigationTag("CustomerFavorites").ToString());
22 string pageId = GetGlobalValue("Global:Page.ID").ToString();
23 string productId = GetString("Ecom:Product.ID");
24 string uniqueId = GetString("Ecom:Product.ID") + GetString("Ecom:Product.VariantID");
25 // Stock status
26 int stockAmount = GetInteger("Ecom:Product.AvailableAmount");
27 string currentPrice = GetString("Ecom:Product.Discount.Price.PriceFormatted") == GetString("Ecom:Product.Price.PriceFormatted") ? GetString("Ecom:Product.Price.PriceFormatted") : GetString("Ecom:Product.Discount.Price.PriceFormatted");
28 string hideHelpText = "";
29 string requestQuery = GetGlobalValue("Global:Request.Query");
30 string variantId = HttpContext.Current.Request.QueryString.Get("variantId");
31 string feedId = pageId + "&ProductID=" + productId + "&VariantID=" + variantId + "&Feed=True&redirect=false";
32 //CFP bool
33 bool cfp = GetBoolean("Ecom:Product:Field.CFP");
34
35 // Documents
36 string documentValue = GetString("Ecom:Product:Field.PDF_Path");
37 var results = DocumentInfo.FileInformation(documentValue);
38
39
40 string externalLink = GetValue("Ecom:Product:Field.ExternalLinks.Value").ToString();
41 var testList = externalLink.Split(new string[] { "</p>" }, StringSplitOptions.RemoveEmptyEntries).ToList();
42
43
44
45 int featuresCount = 0;
46 string brand = GetString("Ecom:Product:Field.brand");
47 bool onlyPreviewForAnonymousUsers = Pageview.AreaSettings.GetBoolean("OnlyPreviewForAnonymous");
48 bool onlyPreview = onlyPreviewForAnonymousUsers ? Pageview.User == null : false;
49
50 foreach (LoopItem customField in GetLoop("CustomFieldValues"))
51 {
52 if (!String.IsNullOrEmpty(customField.GetString("Product.CustomField.Value.Clean")) && customField.GetString("Product.CustomField.Name") != "CustomSticker")
53 {
54 featuresCount++;
55 }
56 }
57
58 foreach (LoopItem customField in GetLoop("ProductCategories"))
59 {
60 foreach (LoopItem field in customField.GetLoop("ProductCategoryFields"))
61 {
62 if (!String.IsNullOrEmpty(field.GetString("Ecom:Product.CategoryField.Value")))
63 {
64 featuresCount++;
65 }
66 }
67 }
68
69 foreach (LoopItem variantgroup in GetLoop("VariantGroups"))
70 {
71 foreach (LoopItem variantoption in variantgroup.GetLoop("VariantAvailableOptions"))
72 {
73 if (variantoption.GetBoolean("Ecom:VariantOption.Selected"))
74 {
75 hideHelpText = "u-hidden";
76 }
77 }
78 }
79
80 int relatedProductsPageSize = 4;
81 int relatedProductsColumnWidth = 3;
82
83 if (Pageview.Device.ToString() == "Mobile")
84 {
85 relatedProductsPageSize = 1;
86 relatedProductsColumnWidth = 12;
87 }
88
89 if (Pageview.Device.ToString() == "Tablet")
90 {
91 relatedProductsPageSize = 3;
92 relatedProductsColumnWidth = 4;
93 }
94
95 string pageNavTag = GetPageIdByNavigationTag("ProductsPage").ToString();
96 string feedFullUrl = String.Format("/Default.aspx?ID={0}&PageSize={1}&ProdID={2}&feed=true", pageNavTag, relatedProductsPageSize, productId);
97 string relatedProductsFeed = String.Format("/Default.aspx?ID={0}&PageSize={1}&ProdID={2}&feed=true&RelateredeProdukter=", pageNavTag, relatedProductsPageSize, productId);
98 string productContainerId = "Product" + productId;
99 string video = GetString("Ecom:Product:Field.video");
100 string siteURL = Dynamicweb.Context.Current.Request.Url.Scheme + "://" + Dynamicweb.Context.Current.Request.Url.Host;
101 Dynamicweb.Ecommerce.Products.Product currentProduct = Dynamicweb.Ecommerce.Services.Products.GetProductById(GetString("Ecom:Product.ID"), GetString("Ecom:Product.VariantID"), GetString("Ecom:Product.LanguageID"));
102 var imagePathValue = Dynamicweb.Ecommerce.Services.Products.GetProductFieldValue(currentProduct, "ImagePath");
103 string imagePath = !string.IsNullOrWhiteSpace(imagePathValue.ToString()) ? "/Files/Images/Ecom/LHI_Products/Images/" + imagePathValue + ".jpg" : "";
104 string productImage = !string.IsNullOrWhiteSpace(imagePath) ? imagePath : GetString("Ecom:Product.ImageLarge.Default.Clean");
105 string canonicalURL = GetString("Ecom:Product.Canonical");
106 string vismaUnitName = !string.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.Enhed")) ? GetString("Ecom:Product:Field.Enhed")?.ToLower() : Translate("UnitPriceTextStk");
107
108 //gallery
109 int imagesCount = 1;
110 List<string> galleryImages = new List<string>();
111
112 galleryImages.Add(productImage);
113 if (!string.IsNullOrWhiteSpace(video))
114 {
115 foreach (var image in video.Split('|'))
116 {
117 string videoThumb = String.Format("https://img.youtube.com/vi/{0}/0.jpg", image);
118 galleryImages.Add(videoThumb);
119 imagesCount++;
120 }
121 }
122
123 foreach (LoopItem alternativeImage in GetLoop("Ecom:Product.AlternativeImages"))
124 {
125 if (!String.IsNullOrEmpty(alternativeImage.GetString("Ecom:Product.AlternativeImages.Image")))
126 {
127 string fullImage = "/Admin/Public/GetImage.ashx?width=550&height=550&crop=5&FillCanvas=True&DoNotUpscale=true&Compression=75&image=" + alternativeImage.GetString("Ecom:Product.AlternativeImages.Image");
128 galleryImages.Add(fullImage);
129 imagesCount++;
130 }
131 }
132
133 foreach (LoopItem detail in GetLoop("Details"))
134 {
135 string fullImage = "/Admin/Public/GetImage.ashx?width=550&height=550&crop=5&FillCanvas=True&DoNotUpscale=true&Compression=75&image=" + detail.GetString("Ecom:Product:Detail.Image.Clean");
136
137 if (!String.IsNullOrEmpty(fullImage))
138 {
139 galleryImages.Add(fullImage);
140 imagesCount++;
141 }
142 }
143
144 string firstImage = galleryImages.FirstOrDefault();
145 string galleryImagesArray = string.Join(", ", galleryImages);
146 }
147
148 @* Set product canonical URL *@
149 @SnippetStart("canonical")
150 @{
151 string host = GetGlobalValue("Global:Request.Host");
152 string url = GetGlobalValue("Global:Pageview.Url");
153 int queryIndex = url.IndexOf("?", StringComparison.Ordinal);
154 if (queryIndex > -1)
155 {
156 url = url.Substring(0, queryIndex);
157 }
158 }
159 <link rel="canonical" href="https://@host@url">
160 @SnippetEnd("canonical")
161 <!-- Trigger for the gallery modal -->
162 <input type="checkbox" id="GalleryModalTrigger" class="modal-trigger" />
163
164 <!-- Gallery modal -->
165 <div class="modal-container">
166 <label for="GalleryModalTrigger" id="GalleryModalOverlay" class="modal-overlay"></label>
167 <div class="modal modal--lg modal--full modal-no-bg" id="GalleryModal">
168 <div class="modal__body">
169 <div class="gallery-slider js-gallery-slider" data-total-images="@imagesCount" data-images="@galleryImagesArray">
170 <div class="gallery-slider__image" data-icon-nz="play">
171 <img id="FullImage" src="@firstImage" class="w-100 js-video-button js-gallery-image" />
172 </div>
173 <div class="gallery-slider__image-counter" id="FullImage_counter">
174 </div>
175 <label class="gallery-slider__close-btn" for="GalleryModalTrigger"></label>
176
177 @if (imagesCount > 1)
178 {
179 <button class="gallery-slider__previous-btn" id="FullImage_prev" onclick="Gallery.prevImage('FullImage')"></button>
180 <button class="gallery-slider__next-btn" id="FullImage_next" onclick="Gallery.nextImage('FullImage')"></button>
181 }
182
183 </div>
184 </div>
185 </div>
186 </div>
187
188 <div class="paragraph-container__grid--bleed-x paragraph-container__grid--bleed-y product-view">
189 <div class="grid product js-product" id="productGrid">
190 @* Image block with optional thumbs *@
191 @if (!String.IsNullOrEmpty(productImage))
192 {
193 <div class="grid__col-md-6 grid__col-sm-6">
194 <div class="grid grid--bleed product-slider">
195 @{
196 int thumbCounter = 0;
197 }
198 <div class="grid__col-2 product-slider__thumbnails dw-mod ">
199 <div class="carousel js-carousel-container u-max-h500px dw-mod">
200 <div class="thumb-list carousel__container dw-mod m-0">
201 <div class="carousel__container__slide carousel__container__slide--vertical product-slider__inner dw-mod">
202
203 @*Main image thumb*@
204 <div class="carousel__container__slide product-slider__slide dw-mod">
205 <div class="thumb-list__item thumb-list__item--active dw-mod js-thumb js-thumb-btn js-gallery" onmouseover="Gallery.openImage(this)" data-number="@thumbCounter" data-for="Image_@productId" data-image="/Admin/Public/GetImage.ashx?width=550&height=550&crop=5&DoNotUpscale=true&Compression=75&image=@productImage">
206 <label for="GalleryModalTrigger">
207 <img src="/Admin/Public/GetImage.ashx?width=100&height=100&crop=5&DoNotUpscale=true&Compression=75&image=@productImage" class="js-gallery" alt="@GetString("Ecom:Product.Name")" data-for="FullImage" data-number="@thumbCounter" onclick="Gallery.openImage(this)" data-image="/Admin/Public/GetImage.ashx?width=550&height=550&crop=5&DoNotUpscale=true&Compression=75&image=@productImage">
208 </label>
209 </div>
210 </div>
211
212 @{ thumbCounter++; }
213
214 @foreach (LoopItem alternativeImage in GetLoop("Ecom:Product.AlternativeImages"))
215 {
216 if (!String.IsNullOrEmpty(alternativeImage.GetString("Ecom:Product.AlternativeImages.Image")))
217 {
218 string image = "/Admin/Public/GetImage.ashx?width=550&height=550&crop=5&FillCanvas=True&DoNotUpscale=true&Compression=75&image=" + alternativeImage.GetString("Ecom:Product.AlternativeImages.Image");
219 string thumb = "/Admin/Public/GetImage.ashx?width=100&height=100&crop=5&FillCanvas=True&DoNotUpscale=true&Compression=75&image=" + alternativeImage.GetString("Ecom:Product.AlternativeImages.Image");
220
221 <div class="carousel__container__slide product-slider__slide dw-mod">
222 <div class="thumb-list__item dw-mod js-thumb-btn js-thumb js-gallery" onmouseover="Gallery.openImage(this)" data-number="@thumbCounter" data-for="Image_@productId" data-image="@image">
223 <label for="GalleryModalTrigger">
224 <img src="@thumb" class="js-gallery" alt="@GetString("Ecom:Product.Name")" data-for="FullImage" data-number="@thumbCounter" onclick="Gallery.openImage(this)" data-image="@image" data-src="@image">
225 </label>
226 </div>
227 </div>
228 thumbCounter++;
229 }
230 }
231
232 @foreach (var youtubeLink in video.Split('|'))
233 {
234 if (!String.IsNullOrEmpty(youtubeLink))
235 {
236 string image = String.Format("https://img.youtube.com/vi/{0}/0.jpg", youtubeLink.Split('?'));
237 string thumb = String.Format("https://img.youtube.com/vi/{0}/0.jpg", youtubeLink.Split('?'));
238
239 <div class="carousel__container__slide product-slider__slide dw-mod">
240 <a class="d-block thumb-list__item has-video dw-mod js-thumb js-thumb-btn js-video-button js-gallery" data-icon-nz="play" onmouseover="Gallery.openImage(this)" data-number="@thumbCounter" data-for="Image_@productId" data-image="@image" href="@youtubeLink" data-type="youtube" data-toggle="modal">
241 <label for="GalleryModalTrigger">
242 <img src="@thumb" class="js-gallery" alt="@GetString("Ecom:Product.Name")" data-for="FullImage" data-number="@thumbCounter" onclick="Gallery.openImage(this)" data-image="@image" data-src="@image" data-cookieconsent="ignore">
243 </label>
244 </a>
245 </div>
246 thumbCounter++;
247 }
248 }
249
250 @foreach (LoopItem detail in GetLoop("Details"))
251 {
252 if (!String.IsNullOrEmpty(detail.GetString("Ecom:Product:Detail.Image.Clean")))
253 {
254 string image = "/Admin/Public/GetImage.ashx?width=550&height=550&crop=5&FillCanvas=True&DoNotUpscale=true&Compression=75&image=" + detail.GetString("Ecom:Product:Detail.Image.Clean");
255 string thumb = "/Admin/Public/GetImage.ashx?width=100&height=100&crop=5&FillCanvas=True&DoNotUpscale=true&Compression=75&image=" + detail.GetString("Ecom:Product:Detail.Image.Clean");
256
257 <div class="carousel__container__slide product-slider__slide dw-mod">
258 <div class="thumb-list__item dw-mod js-thumb-btn js-thumb js-gallery" onmouseover="Gallery.openImage(this)" data-number="@thumbCounter" data-for="Image_@productId" data-image="@image">
259 <label for="GalleryModalTrigger">
260 <img src="@thumb" class="js-gallery" alt="@GetString("Ecom:Product.Name")" data-for="FullImage" data-number="@thumbCounter" onclick="Gallery.openImage(this)" data-image="@image" data-src="@image">
261 </label>
262 </div>
263 </div>
264 thumbCounter++;
265 }
266 }
267 </div>
268 </div>
269
270 <div class="js-carousel-data" data-total-slides="@(imagesCount + 1)" data-carousel-slide-time="0" data-current-slide="0" data-direction="vertical" data-sliding-type="items" slides-in-view="6">
271 <div class="carousel-prev-btn carousel-prev-btn--vertical dw-mod" onclick="Carousel.GetPreviousSlide(this)"></div>
272 <div class="carousel-next-btn carousel-next-btn--vertical dw-mod" onclick="Carousel.GetNextSlide(this)"></div>
273 </div>
274
275 </div>
276 </div>
277
278 <div class="grid__col-10 product-slider__large-image">
279 <div class="stickers-container dw-mod">
280 @{
281 if (Converter.ToBoolean(Pageview.Area.Item["EnableSaleTags"]))
282 {
283 string contentType = Pageview.Area.Item["EcommerceSaleTagContentType"].ToString();
284 string text = "";
285 var currency = Dynamicweb.Ecommerce.Services.Currencies.GetDefaultCurrency();
286
287 switch (contentType)
288 {
289 case "Name":
290 foreach (LoopItem discount in GetLoop("ProductDiscounts"))
291 {
292 text = discount.GetString("Ecom:Product.Discount.Name");
293 <div class="stickers-container__tag stickers-container__tag--sale dw-mod">@text</div>
294 }
295 break;
296 case "Amount":
297 if (GetLoop("ProductDiscounts").Count > 0)
298 {
299 text = Dynamicweb.Ecommerce.Services.Currencies.Format(currency, GetDouble("Ecom:Product.Discount.Price.Price") - GetDouble("Ecom:Product.Price.Price"));
300 <div class="stickers-container__tag stickers-container__tag--sale dw-mod">@text</div>
301 }
302 break;
303 case "Percents":
304 double percents = 0;
305 foreach (LoopItem discount in GetLoop("ProductDiscounts"))
306 {
307 if (discount.GetDouble("Ecom:Product.Discount.ProductQuantity").Equals(1))
308 {
309 percents = discount.GetDouble("Ecom:Product.Discount.PercentWithoutVAT");
310 }
311 }
312 if (percents > 0)
313 {
314 text = Math.Round(percents, 0) + "%";
315 <div class="stickers-container__tag stickers-container__tag--sale dw-mod">@text</div>
316 }
317 break;
318 case "Amount and percents":
319 double amount = 0;
320 double percent = 0;
321 foreach (LoopItem discount in GetLoop("ProductDiscounts"))
322 {
323 if (discount.GetString("Ecom:Product.Discount.Type") == "PERCENT")
324 {
325 percent += discount.GetDouble("Ecom:Product.Discount.PercentWithoutVAT");
326 }
327 else if (discount.GetString("Ecom:Product.Discount.Type") == "AMOUNT")
328 {
329 amount += discount.GetDouble("Ecom:Product.Discount.AmountWithVAT");
330 }
331 }
332 if (percent > 0)
333 {
334 text = percent + "%";
335 <div class="stickers-container__tag stickers-container__tag--sale dw-mod">@text</div>
336 }
337 if (amount > 0)
338 {
339 text = Dynamicweb.Ecommerce.Services.Currencies.Format(currency, amount);
340 <div class="stickers-container__tag stickers-container__tag--sale dw-mod">@text</div>
341 }
342 break;
343 default:
344 if (GetLoop("ProductDiscounts").Count > 0)
345 {
346 text = Translate("Sale!");
347 <div class="stickers-container__tag stickers-container__tag--sale dw-mod">@text</div>
348 }
349 break;
350 }
351 }
352
353 if (Converter.ToBoolean(Pageview.Area.Item["NewStickersEnable"]) && GetDate("Ecom:Product.Created").AddDays(Converter.ToDouble(Pageview.Area.Item["NewStickersExpiration"])) > DateTime.Now)
354 {
355 <div class="stickers-container__tag stickers-container__tag--new dw-mod">@Translate("New!")</div>
356 }
357
358 if (!String.IsNullOrEmpty(GetString("Ecom:Product:Field.CustomSticker.Value")))
359 {
360 <div class="stickers-container__tag stickers-container__tag--custom dw-mod">@GetString("Ecom:Product:Field.CustomSticker.Value")</div>
361 }
362 }
363 </div>
364
365 <label for="GalleryModalTrigger" class="product__image-container">
366 <img class="thumb-image-view product__image-container__image js-gallery-slider js-video-button dw-mod" src="/Admin/Public/GetImage.ashx?crop=5&width=550&height=550&Compression=75&image=@productImage" data-current-image="0" data-total-images="@imagesCount" data-images="@galleryImagesArray" data-src="/Admin/Public/GetImage.ashx?crop=5&width=550&height=550&Compression=75&image=@productImage" alt="@GetString("Ecom:Product.Name")" id="Image_@productId" data-for="FullImage" data-number="0" onclick="Gallery.openImageByNum(this)" />
367 </label>
368
369 </div>
370 </div>
371 </div>
372 }
373
374 @* Primary product informations *@
375 <div class="grid__col-12 grid__col-md-6 product__info dw-mod">
376 <div>
377 <h1 class="product__title u-no-margin">@GetString("Ecom:Product.Name") @GetString("Ecom:Product.SelectedVariantComboName")</h1>
378 <small class="x-small text-gray-light font-weight-light">@(!string.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.Brand")) ? String.Format("{0}: {1}", Translate("Manufacturer"), GetString("Ecom:Product:Field.Brand")) : "") @Translate("product number"): @GetString("Ecom:Product.Number") @(!string.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.VVSNumber")) ? String.Format("{0}: {1}", Translate("VVS nr."), GetString("Ecom:Product:Field.VVSNumber")) : "") @(!string.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.DBN")) ? String.Format("{0}: {1}", Translate("DB nr."), GetString("Ecom:Product:Field.DBN")) : "") </small>
379 <div>
380 @* Delivery + stock information *@
381 @if (onlyPreview)
382 {
383 <div>
384 <h5 class="mb-2">@Translate("LoginText", "Login")</h5>
385 <label for="SignInModalTrigger" class="btn btn--primary u-no-margin sign-in-modal-trigger-button dw-mod" onclick="setTimeout(function() { document.getElementById('LoginUsername').focus() }, 10)">@Translate("Sign in")</label>
386 </div>
387 }
388
389 @* Variants *@
390 @*@if (GetLoop("VariantGroups").Count > 0)
391 {
392 string containerId = "Variants" + productId;
393
394 <div>
395 <div id="@containerId" data-product-id="@productId">
396 @foreach (LoopItem variantGoup in GetLoop("VariantGroups"))
397 {
398 <div>
399 <div class="u-bold">@variantGoup.GetString("Ecom:VariantGroup.Name")</div>
400 <div>
401 @foreach (LoopItem variantOption in variantGoup.GetLoop("VariantAvailableOptions"))
402 {
403 string selected = variantOption.GetBoolean("Ecom:VariantOption.Selected") ? "checked" : "";
404
405 if (!string.IsNullOrEmpty(variantOption.GetString("Ecom:VariantOption.ImgSmall.Clean")))
406 {
407 <img src="/Admin/Public/GetImage.ashx?width=100&height=50&crop=5&Compression=75&image=/Images/@variantOption.GetString("Ecom:VariantOption.ImgSmall.Clean")" title="@variantOption.GetString("Ecom:VariantOption.Name")" id="@productId@variantOption.GetString("Ecom:VariantOption.ID")" data-variant-id="@variantOption.GetString("Ecom:VariantOption.ID")" data-product-id="@productId" onclick="Variants.UpdateVariants(this, false, true)" class="btn btn--tag @selected" data-check="@selected" />
408 }
409 else
410 {
411 <button type="button" data-id="@GetString("Ecom:Product.ID")" id="@productId@variantOption.GetString("Ecom:VariantOption.ID")" data-variant-id="@variantOption.GetString("Ecom:VariantOption.ID")" data-product-id="@productId" onclick="Variants.UpdateVariants(this, false, true)" class="btn btn--tag @selected" data-check="@selected">@variantOption.GetString("Ecom:VariantOption.Name")</button>
412 }
413 }
414 </div>
415 </div>
416 }
417 </div>
418 <small id="helpText_@productId" class="help-text @hideHelpText">@Translate("Please select variant!")</small>
419 </div>
420 }*@
421 @if (GetLoop("VariantGroups").Count > 0)
422 {
423 var variantCombinationsObject = new List<Array>();
424 foreach (LoopItem variantcomb in GetLoop("VariantStockCombinations"))
425 {
426 string[] combinations = variantcomb.GetString("Ecom:VariantStockCombination.VariantID").Split('.');
427 variantCombinationsObject.Add(combinations);
428 }
429
430 string combinationsJson = Newtonsoft.Json.JsonConvert.SerializeObject(variantCombinationsObject).Replace("\"", "\'");
431
432 var variantGroupsObject = new List<List<String>>();
433 var selectedItem = new Dictionary<string, string>();
434 string variantSelection = !String.IsNullOrEmpty(HttpContext.Current.Request.QueryString.Get("variantId")) ? HttpContext.Current.Request.QueryString.Get("variantId").Replace(".", ",") : "";
435
436 var selectedText = Translate("Select variant");
437
438 foreach (LoopItem variantGroup in GetLoop("VariantGroups"))
439 {
440 var variantsObject = new List<String>();
441 foreach (LoopItem variantOption in variantGroup.GetLoop("VariantAvailableOptions"))
442 {
443 variantsObject.Add(variantOption.GetString("Ecom:VariantOption.ID"));
444
445
446 if (variantOption.GetBoolean("Ecom:VariantOption.Selected"))
447 {
448 selectedItem.Add(variantGroup.GetString("Ecom:VariantGroup.ID"), variantOption.GetString("Ecom:VariantOption.Name"));
449 }
450 }
451 variantGroupsObject.Add(variantsObject);
452 }
453 string variantsJson = Newtonsoft.Json.JsonConvert.SerializeObject(variantGroupsObject).Replace("\"", "\'");
454 string productGroupId = HttpContext.Current.Request["GroupId"];
455 int variantCounter = 0;
456
457 <div class="variants-container">
458 <div class="js-variants" data-total-variant-groups="@GetLoop("VariantGroups").Count" data-combinations="@combinationsJson" data-variants="@variantsJson" data-selection-complete="UpdatePage" data-page-id="@pageId" data-product-id="@productId" data-group-id="@productGroupId">
459 @foreach (LoopItem variantGroup in GetLoop("VariantGroups"))
460 {
461 string groupId = variantGroup.GetString("Ecom:VariantGroup.ID");
462 variantCounter++;
463 <div class="dropdown-container u-margin-bottom--lg">
464 <div class="product__variant-group-name u-margin-bottom-5px dw-mod">@variantGroup.GetString("Ecom:VariantGroup.Name")</div>
465 <div class="display-value" id="displayValue-@variantCounter" onclick="toggleOptions('selectContainer-@variantCounter')">
466 <span class="value-text" id="valueText-@variantCounter">@(selectedItem.ContainsKey(variantGroup.GetString("Ecom:VariantGroup.ID")) ? selectedItem[variantGroup.GetString("Ecom:VariantGroup.ID")] : selectedText)</span>
467 <span class="arrow arrow-down" id="arrowControl"></span>
468 </div>
469 <ul tabindex="0" class="select-container" id="selectContainer-@variantCounter" onblur="toggleOptions('selectContainer-@variantCounter')">
470 @foreach (LoopItem variantOption in variantGroup.GetLoop("VariantAvailableOptions"))
471 {
472 string selected = variantOption.GetBoolean("Ecom:VariantOption.Selected") ? "checked" : "";
473
474
475
476 <li value="@variantOption.GetString("Ecom:VariantOption.ID")"
477 type="button"
478 data-variant-id="@variantOption.GetString("Ecom:VariantOption.ID")"
479 onclick="MatchVariants.SelectThis(event), selected('@variantOption.GetString("Ecom:VariantOption.Name")','valueText-@variantCounter', 'selectContainer-@variantCounter')"
480 data-variant-group="@groupId"
481 class="btn btn--tag @selected js-variant-option select-option"
482 data-check="@selected">@variantOption.GetString("Ecom:VariantOption.Name")</li>
483 }
484 </ul>
485 </div>
486 }
487 </div>
488 </div>
489 }
490
491 @* BOMProducts *@
492 @if (GetLoop("BOMProducts").Count > 0)
493 {
494 <h2 class="section-title">@Translate("Including products")</h2>
495 foreach (LoopItem BOMProductItem in GetLoop("BOMProducts"))
496 {
497 string link = "/" + BOMProductItem.GetString("Ecom:Product.LinkGroup.Clean") + (!String.IsNullOrEmpty(BOMProductItem.GetString("Ecom:Product.VariantID")) ? "&VariantID=" + BOMProductItem.GetString("Ecom:Product.VariantID") : "");
498 <div class="grid__col--border grid">
499 <div class="grid__cell grid__cell--align-middle-left">
500 <a href="@link" class="u-pull--left u-margin-right">
501 <img src="/Admin/Public/GetImage.ashx?width=50&image=@BOMProductItem.GetString("Ecom:Product.ImageSmall.Default.Clean")&Compression=99" />
502 </a>
503 <a href="@link">@BOMProductItem.GetString("Ecom:Product.Name")</a>
504 </div>
505 </div>
506 }
507 }
508 </div>
509 </div>
510
511 @* Buy block *@
512 <div class="w-100 mt-5">
513 <div class="js-handlebars-root dw-mod" id="PriceAndActions" data-template="PricesAndActionsTemplate" data-json-feed="/Default.aspx?ID=@feedId"></div>
514 <input type="hidden" value="@GetString("Ecom:Product.VariantID.Extented")" name="Variant" id="Variant_@GetString("Ecom:Product.ID")" />
515 </div>
516 <div class="mt-4">@GetString("Ecom:Product:Field.LongDescription")</div>
517 <div class="product-page__specs">@ProductPropertiesMainArea()</div>
518 </div>
519 </div>
520
521 <section class="product-page__product-info">
522 <div class="grid__col-12 grid__col-lg-9 m-auto">
523 <div class="nav nav-pills nav-justified">
524 <a class="nav-item nav-link js-toggle-class js-toggle-retrigger is-active" data-target-retrigger="target-retrigger" data-target=".product-page__content--description">@Translate("Product information")</a>
525 <a class="nav-item nav-link js-toggle-class js-toggle-retrigger" data-target-retrigger="target-retrigger" data-target=".product-page__content--video">@Translate("Documents/Video")</a>
526 <a class="nav-item nav-link js-toggle-class js-toggle-retrigger" data-target-retrigger="target-retrigger" data-target=".product-page__content--accessories">@Translate("Accessories")</a>
527 </div>
528 </div>
529
530 <div class="grid__col-12 grid__col-lg-10 m-auto grid__col--bleed-x">
531 <div class="grid product-page__content product-page__content--description js-toggle-retrigger is-active">
532 @* Features lists *@
533 <div class="grid__col-12 grid__col-md-6 product__features dw-mod">
534 <h1 class="h3 section-title">@Translate("Product information")</h1>
535 @GetString("Ecom:Product.LongDescription")
536 @GetString("Ecom:Product:Field.description")
537 </div>
538 <div class="grid__col-12 grid__col-md-6 product__features dw-mod">
539 <h1 class="h3 section-title">@Translate("Product specs")</h1>
540 <div class="product-page__specs">@ProductProperties()</div>
541 </div>
542 </div>
543 <div class="grid product-page__content product-page__content--video js-toggle-retrigger justify-content-center">
544 <div class="grid__col-12 grid__col-md-8 p-0 p-md-4">
545 <h1 class="h3 section-title">@Translate("Documents/Video")</h1>
546 <div class="table-responsive small order-list">
547 <table class="table table-striped product-page-table">
548 <thead>
549 <tr>
550 <th class="font-weight-bold border-bottom-0" scope="col">@Translate("Name")</th>
551 <th class="font-weight-bold border-bottom-0" scope="col">@Translate("Type")</th>
552 <th class="font-weight-bold border-bottom-0" scope="col">@Translate("Size")</th>
553 </tr>
554 </thead>
555 <tbody>
556
557 @foreach (var documentPath in results)
558 {
559 <tr>
560 <td scope="row">
561 <a href="@documentPath.FilePath" target="_blank">
562 <img src="/Files/Images/Lauridsen/icons/icon-pdf.svg" class="product-page-table__icon d-inline-block align-middle mr-3" alt="@documentPath.FileName" />
563 <span class="d-inline-block align-middle u-underline">@documentPath.FileName</span>
564 </a>
565 </td>
566 <td>@documentPath.FileType.Replace(".pdf", "PDF")</td>
567 <td>@documentPath.FileSize.ToSize(ByteExtension.SizeUnits.KB) KB</td>
568 </tr>
569 }
570
571
572
573 @foreach (var youtubeLink in video.Split('|'))
574 {
575 if (!String.IsNullOrEmpty(youtubeLink))
576 {
577 int thumbCounter = 0;
578 string image = String.Format("https://img.youtube.com/vi/{0}/0.jpg", youtubeLink);
579 string thumb = String.Format("https://img.youtube.com/vi/{0}/0.jpg", youtubeLink);
580 <tr>
581 <td>
582 <div class="carousel__container__slide dw-mod">
583 <a class="d-block dw-mod js-thumb-btn js-video-button" onmouseover="Gallery.openImage(this)" data-number="@thumbCounter" data-for="Image_@productId" href="@youtubeLink" data-type="youtube" data-toggle="modal">
584 <label for="GalleryModalTrigger">
585 <img src="/Files/Images/Lauridsen/icons/icon-video.svg" class="product-page-table__icon d-inline-block align-middle mr-4" alt="@GetString("Ecom:Product:Field.video")" />
586 <span class="d-inline-block align-middle u-underline">@GetString("Ecom:Product:Field.video")</span>
587 </label>
588 </a>
589 </div>
590 </td>
591 <td>@GetString("Ecom:Product:Field.video.Name")</td>
592 <td>-</td>
593 </tr>
594 thumbCounter++;
595 }
596 }
597
598
599
600 @foreach (var value in testList)
601 {
602
603 if (externalLink.IsNotNullOrEmpty() && !string.IsNullOrWhiteSpace(value))
604 {
605 <tr>
606 <td scope="row">
607 <i class="product-page-table__icon d-inline-block align-middle mr-3 fa fa-link fa-2x"></i>
608 <span class="d-inline-block align-middle u-underline product-external-link">@value</span>
609 </td>
610 <td>@Translate("Link")</td>
611 <td>-</td>
612 </tr>
613 }
614 }
615 </tbody>
616 </table>
617 </div>
618 </div>
619 </div>
620 <div class="grid product-page__content product-page__content--accessories js-toggle-retrigger">
621 <h1 class="h3 section-title">@Translate("Accessories")</h1>
622 <div class="grid accessories-list dw-mod">
623 <form name="multiForm" id="multiForm" class="accessories-list__form js-load-more-container" data-initial-load="5" method="post">
624 <input type="hidden" name="CartCmd" id="CartCmd" value="addMulti" />
625 @foreach (LoopItem relatedGroup in GetLoop("ProductRelatedGroups").Where(group => group.GetString("Ecom:Product:RelatedGroup.Name") == "Tilbehør" || group.GetString("Ecom:Product:RelatedGroup.Name") == "Reservedele"))
626 {
627 foreach (LoopItem relatedProduct in relatedGroup.GetLoop("RelatedProducts"))
628 {
629 Dynamicweb.Ecommerce.Products.Product currentRelatedProduct = Dynamicweb.Ecommerce.Services.Products.GetProductById(relatedProduct.GetString("Ecom:Product.ID"), relatedProduct.GetString("Ecom:Product.VariantID"), relatedProduct.GetString("Ecom:Product.LanguageID"));
630 var relatedProductImagePathValue = Dynamicweb.Ecommerce.Services.Products.GetProductFieldValue(currentRelatedProduct, "ImagePath");
631 string imgUrl = "/Files/Images/missing_image.jpg";
632 string relatedProductImagePath = !string.IsNullOrWhiteSpace(relatedProductImagePathValue.ToString()) ? "/Files/Images/Ecom/LHI_Products/Images/" + relatedProductImagePathValue + ".jpg" : "";
633 string relatedProductImage = !string.IsNullOrWhiteSpace(relatedProductImagePath) ? relatedProductImagePath : relatedProduct.GetString("Ecom:Product.ImageLarge.Default.Clean");
634
635 if (!string.IsNullOrWhiteSpace(relatedProductImage))
636 {
637 imgUrl = relatedProductImage;
638 }
639
640 string name = relatedProduct.GetString("Ecom:Product.Name");
641 string number = relatedProduct.GetString("Ecom:Product.Number");
642 string price = relatedProduct.GetString("Ecom:Product.Price");
643 string groupId = relatedProduct.GetString("Ecom:Product.PrimaryGroupID");
644 string id = relatedProduct.GetString("Ecom:Product.ID");
645 var stock = relatedProduct.GetInteger("Ecom:Product.Stock");
646 string stockText = relatedProduct.GetString("Ecom:Product:Stock.Text");
647 string shortDescription = relatedProduct.GetString("Ecom:Product.ShortDescription");
648
649
650 string link = "Default.aspx?" + "ID=" + GetInteger("Ecom:Product.PrimaryOrCurrentPageID") + "&GroupID=" + relatedProduct.GetString("Ecom:Product.PrimaryOrFirstGroupID") + "&ProductID=" + id;
651 List<Dynamicweb.Security.UserManagement.Group> userGroups = new List<Dynamicweb.Security.UserManagement.Group>();
652 bool userisLoggedIn = Dynamicweb.Security.UserManagement.User.IsExtranetUserLoggedIn();
653 bool isEmpty = !userGroups.Any();
654
655 if (isEmpty && Dynamicweb.Security.UserManagement.User.IsExtranetUserLoggedIn())
656 {
657 userGroups = Dynamicweb.Security.UserManagement.User.GetCurrentExtranetUser().Groups.ToList();
658 }
659
660 <div class="accessories-list__item accessorie grid__col-12 grid__col--bleed-x js-load-more-item is-hidden">
661 <div class="grid">
662 <figure class="grid__col-sm-2 grid__col-12 accessorie__figure">
663 <div class="stickers-container dw-mod">
664
665
666
667 @if (Converter.ToBoolean(Pageview.Area.Item["EnableSaleTags"]))
668 {
669 string contentType = Pageview.Area.Item["EcommerceSaleTagContentType"].ToString();
670 string text = "";
671 var currency = Dynamicweb.Ecommerce.Services.Currencies.GetDefaultCurrency();
672
673
674 switch (contentType)
675 {
676 case "Name":
677 foreach (LoopItem relatedDiscounts in GetLoop("ProductDiscounts"))
678 {
679 text = relatedDiscounts.GetString("Ecom:Product.Discount.Name");
680 <div class="stickers-container__tag stickers-container__tag--sale dw-mod">@text</div>
681 }
682 break;
683 case "Amount":
684 if (GetLoop("ProductDiscounts").Count > 0)
685 {
686 text = Dynamicweb.Ecommerce.Services.Currencies.Format(currency, GetDouble("Ecom:Product.Discount.Price.Price") - GetDouble("Ecom:Product.Price.Price"));
687 <div class="stickers-container__tag stickers-container__tag--sale dw-mod">@text</div>
688 }
689 break;
690 case "Percents":
691 double percents = 0;
692 if (relatedProduct.GetLoop("ProductDiscounts").Count > 0)
693 {
694 foreach (LoopItem discount in relatedProduct.GetLoop("ProductDiscounts"))
695 {
696 if (discount.GetDouble("Ecom:Product.Discount.ProductQuantity") == 1)
697 {
698 percents = discount.GetDouble("Ecom:Product.Discount.PercentWithoutVAT");
699 }
700 }
701 if (percents > 0)
702 {
703 text = Math.Round(percents, 0) + "%";
704 <div class="stickers-container__tag stickers-container__tag--sale dw-mod">@text</div>
705 }
706
707
708 }
709 break;
710 case "Amount and percents":
711 double amount = 0;
712 double percent = 0;
713 foreach (LoopItem relatedDiscounts in GetLoop("ProductDiscounts"))
714 {
715 if (relatedDiscounts.GetString("Ecom:Product.Discount.Type") == "PERCENT")
716 {
717 percent += relatedDiscounts.GetDouble("Ecom:Product.Discount.PercentWithoutVAT");
718 }
719 else if (relatedDiscounts.GetString("Ecom:Product.Discount.Type") == "AMOUNT")
720 {
721 amount += relatedDiscounts.GetDouble("Ecom:Product.Discount.AmountWithVAT");
722 }
723 }
724 if (percent > 0)
725 {
726 text = percent + "%";
727 <div class="stickers-container__tag stickers-container__tag--sale dw-mod">@text</div>
728 }
729 if (amount > 0)
730 {
731 text = Dynamicweb.Ecommerce.Services.Currencies.Format(currency, amount);
732 <div class="stickers-container__tag stickers-container__tag--sale dw-mod">@text</div>
733 }
734 break;
735 default:
736 if (GetLoop("ProductDiscounts").Count > 0)
737 {
738 text = Translate("Sale!");
739 <div class="stickers-container__tag stickers-container__tag--sale dw-mod">@text</div>
740 }
741 break;
742 }
743
744
745 if (Converter.ToBoolean(Pageview.Area.Item["NewStickersEnable"]) && GetDate("Ecom:Product.Created").AddDays(Converter.ToDouble(Pageview.Area.Item["NewStickersExpiration"])) > DateTime.Now)
746 {
747 <div class="stickers-container__tag stickers-container__tag--new dw-mod">@Translate("New!")</div>
748 }
749
750 if (!String.IsNullOrEmpty(GetString("Ecom:Product:Field.CustomSticker.Value")))
751 {
752 <div class="stickers-container__tag stickers-container__tag--custom dw-mod">@GetString("Ecom:Product:Field.CustomSticker.Value")</div>
753 }
754
755 }
756 </div>
757 <div class="grid__cell">
758 <a href="@link" onclick="Scroll.SavePosition(event)" title="@name">
759 <img class="grid__cell-img accessorie__image"
760 src="/Admin/Public/GetImage.ashx?width=350&height=350&crop=5&Compression=75&DoNotUpscale=true&image=@imgUrl"
761 alt="@name" />
762 </a>
763 </div>
764 </figure>
765 <div class="grid__col-12 grid__col-sm-4 accessorie__description">
766 <a class="" href="@link" onclick="Scroll.SavePosition(event)" title="@name">
767 <div class="accessorie__number"><b>Vare nr.</b> @number</div>
768 <h3 class="accessorie__name">@name</h3>
769 <div class="accessorie__text">@shortDescription</div>
770 <div class="product-list-item__stock">
771 <div class="stock-icon ml-0 @(stock > 0 ? "stock-icon--in" : "stock-icon--not")"></div>
772 <span>@stockText</span>
773 </div>
774 <span class="product-card__quantity-alert js-product-quantity-error-@id ">@Translate("Max stock level is reached")</span>
775 </a>
776 </div>
777
778 @if (!onlyPreview)
779 {
780 List<Price> priceCollectionList = new List<Price>();
781 List<Price> getLowestPriceList = new List<Price>();
782 List<Price> orderedPriceList = new List<Price>();
783 List<Price> getPriceCollection = new List<Price>();
784
785 if (userisLoggedIn)
786 {
787 if (currentProduct.Prices.ToList().Any())
788 {
789 priceCollectionList = currentProduct.Prices.ToList();
790 }
791
792 string userPriceGroups = string.Empty;
793 if (userGroups.Any())
794 {
795 <div class=" grid__col-12 grid__col-sm-3 accessorie__details" style="text-align: center">
796 @foreach (Price priceItem in priceCollectionList.Where(priceItem => priceItem.ProductId.Equals(uniqueId)))
797 {
798 if (Int32.TryParse(priceItem.UserGroupId, out int priceUserGroupId))
799 {
800 foreach (Dynamicweb.Security.UserManagement.Group userGroupItem in userGroups.Where(userGroup => userGroup.ID.Equals(priceUserGroupId)))
801 {
802 userPriceGroups = userGroupItem.ID.ToString();
803 if (userPriceGroups != null && priceCollectionList != null)
804 {
805 Price getLowest = priceCollectionList.Where(priceGroup => priceGroup.UserGroupId.Contains(userPriceGroups)).OrderBy(sortQuantity => sortQuantity.Quantity).ThenBy(sortOrder => sortOrder.Amount).ToList().FirstOrDefault();
806 getLowestPriceList.Add(getLowest);
807 getPriceCollection = priceCollectionList.Where(priceGroup => priceGroup.UserGroupId.Contains(userPriceGroups)).ToList();
808 if (getPriceCollection.Exists(check => check.Quantity > 2) && getPriceCollection != null)
809 {
810 orderedPriceList.Add(getPriceCollection.FirstOrDefault());
811 }
812 }
813 }
814 }
815 }
816 @if (userPriceGroups != null && getLowestPriceList.Any())
817 {
818 string priceWithDiscount = relatedProduct.GetString("Ecom:Product.Discount.Price.PriceFormatted");
819
820 Price getLowestPrice = getLowestPriceList.OrderBy(lowerPrice => lowerPrice.Amount).ToList().FirstOrDefault();
821 if (getLowestPrice.Quantity < 2)
822 {
823 <div class="accessorie__price price">@priceWithDiscount</div>
824 <div class="">
825 @Translate("UnitPriceText") @getLowestPrice.Quantity @vismaUnitName
826 </div>
827 }
828
829 List<LoopItem> priceLoop = relatedProduct.GetLoop("Product.Prices");
830 if (priceLoop.Any(x => x.GetDouble("Ecom:Product.Prices.Quantity") > 1))
831 {
832 <div class="js-popup">
833
834 <a class="js-popup-trigger">@Translate("DiscountText")</a>
835 <div class="js-popup-modal popup_productrelatedview">
836 <div class="js-popout-close nz-icon icon-nz-close-circled"></div>
837 <ul class="grid__col-12 pt-5 p-0 d-flex flex-row flex-wrap">
838
839 @foreach (LoopItem priceItem in priceLoop.Where(x => x.GetDouble("Ecom:Product.Prices.Quantity") > 1))
840 {
841 string relatedVismaUnitName = !string.IsNullOrWhiteSpace(priceItem.GetString("Ecom:Product:Field.Enhed")) ? priceItem.GetString("Ecom:Product:Field.Enhed")?.ToLower() : Translate("UnitPriceTextStk");
842 <li class="grid__col-lg-6 p-0 mb-0 grid__col-12 align-items-center align-items-xl-start">@Translate("UnitPriceText") @priceItem.GetDouble("Ecom:Product.Prices.Quantity") @relatedVismaUnitName</li>
843 <li class="grid__col-lg-6 p-0 pb-3 grid__col-12 align-items-center align-items-xl-end">@priceItem.GetString("Ecom:Product.Prices.AmountFormatted")</li>
844 }
845 </ul>
846 </div>
847 </div>
848 }
849 }
850
851 </div>
852 if (getLowestPriceList.Any())
853 {
854
855 if (relatedProduct.GetLoop("VariantGroups").Count > 0)
856 {
857 <div class="grid__col-12 grid__col-sm-3 accessorie__controls">
858 <div class="accessorie__controls-inner">
859
860 <a href="@link" class="btn btn-secondary dw-mod js-scroll-btn d-inline-block px-5">@Translate("Select variant")</a>
861 </div>
862 </div>
863
864 }
865 else
866 {
867 <div class="grid__col-12 grid__col-sm-3 accessorie__controls">
868 <div class="accessorie__controls-inner">
869 <div class="accessorie__button-container d-inline-block">
870 <button type="button" id="CartButton_@id" class="btn btn--primary accessorie__cart" name="submit" onclick="Cart.AddToCart(event, '@id', document.getElementById('Quantity_@id').value, 'Unit_@id', 'Variant_@id');">
871 <i class="fa fa-shopping-cart"></i>
872 <span class="u-hidden-xs u-hidden-xxs"> @Translate("Add to cart")</span>
873 </button>
874 </div>
875
876 <div class="product-card__quantity--condenced product-card__quantity accessorie__quantity">
877 <input id="Quantity_@id" class="product-card__quantity-input pointer-events js-quantity-input" name="Quantity@accessoriesProduct.Id" data-id="@id" type="number" min="1" max="@stock" value="1">
878 <span class="product-card__quantity-add js-product-quantity-add" data-icon-nz="chevron-up"></span>
879 <span class="product-card__quantity-subtract js-product-quantity-subtract" data-icon-nz="chevron-down"></span>
880 </div>
881
882 </div>
883 </div>
884 }
885 }
886 else
887 {
888 <div class="grid__col-12 grid__col-sm-3 accessorie__controls">
889 <div class="accessorie__controls-inner">
890 <div class="accessorie__button-container d-inline-block">
891 <button type="button" id="CartButton_@id" class="btn btn-primary dw-mod js-scroll-btn d-inline-block px-5" class="btn btn-primary dw-mod js-scroll-btn d-inline-block px-5" name="submit" data-target=".js-target">
892 <span class="u-hidden-xs u-hidden-xxs"> @Translate("CallForPrice")</span>
893 </button>
894 </div>
895 </div>
896 </div>
897 }
898 }
899 else
900 {
901 <div class=" grid__col-12 grid__col-sm-3 accessorie__details" style="text-align: center">
902 <div class="accessorie__price price"></div>
903 <div class="accessorie__before-price before-price "></div>
904 <div class="">
905 </div>
906 <div class="js-popup">
907 <div class="js-popup-modal popup_productrelatedview">
908 <div class="js-popout-close nz-icon icon-nz-close-circled"></div>
909 </div>
910 </div>
911 </div>
912 if (getLowestPriceList.Any())
913 {
914
915 <div class="grid__col-12 grid__col-sm-3 accessorie__controls">
916 <div class="accessorie__controls-inner">
917
918 <div class="accessorie__button-container d-inline-block">
919 <button type="button" id="CartButton_@id" class="btn btn--primary accessorie__cart" name="submit" onclick="Cart.AddToCart(event, '@id', document.getElementById('Quantity_@id').value, 'Unit_@id', 'Variant_@id');">
920 <i class="fa fa-shopping-cart"></i>
921 <span class="u-hidden-xs u-hidden-xxs"> @Translate("Add to cart")</span>
922 </button>
923 </div>
924
925 <div class="product-card__quantity--condenced product-card__quantity accessorie__quantity">
926 <input id="Quantity_@id" class="product-card__quantity-input pointer-events js-quantity-input" name="Quantity@accessoriesProduct.Id" data-id="@id" type="number" min="1" max="@stock" value="1">
927 <span class="product-card__quantity-add js-product-quantity-add" data-icon-nz="chevron-up"></span>
928 <span class="product-card__quantity-subtract js-product-quantity-subtract" data-icon-nz="chevron-down"></span>
929 </div>
930
931 </div>
932 </div>
933 }
934 else
935 {
936
937 <div class="grid__col-12 grid__col-sm-3 accessorie__controls">
938 <div class="accessorie__controls-inner">
939 <div class="accessorie__button-container d-inline-block">
940 <button type="button" id="CartButton_@id" class="btn btn-primary dw-mod js-scroll-btn d-inline-block px-5" class="btn btn-primary dw-mod js-scroll-btn d-inline-block px-5" name="submit" data-target=".js-target">
941 <span class="u-hidden-xs u-hidden-xxs"> @Translate("CallForPrice")</span>
942 </button>
943 </div>
944 </div>
945 </div>
946 }
947 }
948 }
949 }
950 else
951 {
952 <div class="grid__col-12 grid__col-sm-6 align-items-center align-items-lg-end">
953 <div class="grid__cell">
954 <div class="grid__col-12 pb-0">
955 <h5>@Translate("LoginText")</h5>
956 </div>
957 <div class="grid__col-12 pb-0">
958 <label for="SignInModalTrigger" class="btn btn--primary u-no-margin sign-in-modal-trigger-button dw-mod" onclick="setTimeout(function() { document.getElementById('LoginUsername').focus() }, 10)">@Translate("Sign in")</label>
959 </div>
960 </div>
961 </div>
962 }
963 </div>
964 </div>
965 }
966 }
967 <div class="grid__col--bleed-y mb-6">
968 <a class="btn btn-teritary btn--load-more js-load-more-trigger dw-mod">@Translate("Load more")</a>
969 </div>
970 </form>
971 </div>
972 </div>
973 </div>
974 </section>
975 @if (GetLoop("ProductRelatedGroups").Any())
976 {
977 <section class="related-products">
978 <div class="grid related-products__inner-container">
979
980 <div class="grid__col-xs-12 related-products__headline-container">
981 <h2 class="related-products__headline h2 section-title section-title--condensed">@Translate("Related products")</h2>
982 </div>
983
984 <div class="grid__col-12 grid__col-md-10 grid__col--bleed m-auto">
985 <div class="grid">
986
987 @foreach (LoopItem relatedGroup in GetLoop("ProductRelatedGroups").Where(group => group.GetString("Ecom:Product:RelatedGroup.Name") == "Relaterede produkter"))
988 {
989 foreach (LoopItem related in relatedGroup.GetLoop("RelatedProducts"))
990 {
991 Dynamicweb.Ecommerce.Products.Product currentRelatedProduct = Dynamicweb.Ecommerce.Services.Products.GetProductById(related.GetString("Ecom:Product.ID"), related.GetString("Ecom:Product.VariantID"), related.GetString("Ecom:Product.LanguageID"));
992 var relatedProductImagePathValue = Dynamicweb.Ecommerce.Services.Products.GetProductFieldValue(currentRelatedProduct, "ImagePath");
993 string imgUrl = "/Files/Images/missing_image.jpg";
994 string relatedProductImagePath = !string.IsNullOrWhiteSpace(relatedProductImagePathValue.ToString()) ? "/Files/Images/Ecom/LHI_Products/Images/" + relatedProductImagePathValue + ".jpg" : "";
995 string relatedProductImage = !string.IsNullOrWhiteSpace(relatedProductImagePath) ? relatedProductImagePath : related.GetString("Ecom:Product.ImageLarge.Default.Clean");
996
997 if (!string.IsNullOrWhiteSpace(relatedProductImage))
998 {
999 imgUrl = relatedProductImage;
1000 }
1001
1002 string name = related.GetString("Ecom:Product.Name");
1003 string number = related.GetString("Ecom:Product.Number");
1004 string price = related.GetString("Ecom:Product.Price");
1005 string groupId = related.GetString("Ecom:Product.PrimaryGroupID");
1006 string id = related.GetString("Ecom:Product.ID");
1007 var stock = related.GetInteger("Ecom:Product.Stock");
1008 string stockText = related.GetString("Ecom:Product:Stock.Text");
1009
1010 <div class="grid__col-12 grid__col-sm-6 grid__col-lg-3">
1011 <div class="related-products__product related-product">
1012 <a href="@related.GetString("Ecom:Product.Link.Clean")">
1013 <div class="stickers-container dw-mod">
1014
1015 @if (Converter.ToBoolean(Pageview.Area.Item["EnableSaleTags"]))
1016 {
1017 string contentType = Pageview.Area.Item["EcommerceSaleTagContentType"].ToString();
1018 string text = "";
1019 var currency = Dynamicweb.Ecommerce.Services.Currencies.GetDefaultCurrency();
1020
1021
1022 switch (contentType)
1023 {
1024 case "Name":
1025 foreach (LoopItem relatedDiscounts in GetLoop("ProductDiscounts"))
1026 {
1027 text = relatedDiscounts.GetString("Ecom:Product.Discount.Name");
1028 <div class="stickers-container__tag stickers-container__tag--sale dw-mod">@text</div>
1029 }
1030 break;
1031 case "Amount":
1032 if (GetLoop("ProductDiscounts").Count > 0)
1033 {
1034 text = Dynamicweb.Ecommerce.Services.Currencies.Format(currency, GetDouble("Ecom:Product.Discount.Price.Price") - GetDouble("Ecom:Product.Price.Price"));
1035 <div class="stickers-container__tag stickers-container__tag--sale dw-mod">@text</div>
1036 }
1037 break;
1038 case "Percents":
1039 double percents = 0;
1040 if (related.GetLoop("ProductDiscounts").Count > 0)
1041 {
1042 foreach (LoopItem discount in related.GetLoop("ProductDiscounts"))
1043 {
1044 if (discount.GetDouble("Ecom:Product.Discount.ProductQuantity") == 1)
1045 {
1046 percents = discount.GetDouble("Ecom:Product.Discount.PercentWithoutVAT");
1047 }
1048 }
1049 if (percents > 0)
1050 {
1051 text = Math.Round(percents, 0) + "%";
1052 <div class="stickers-container__tag stickers-container__tag--sale dw-mod">@text</div>
1053 }
1054
1055
1056 }
1057 break;
1058 case "Amount and percents":
1059 double amount = 0;
1060 double percent = 0;
1061 foreach (LoopItem relatedDiscounts in GetLoop("ProductDiscounts"))
1062 {
1063 if (relatedDiscounts.GetString("Ecom:Product.Discount.Type") == "PERCENT")
1064 {
1065 percent += relatedDiscounts.GetDouble("Ecom:Product.Discount.PercentWithoutVAT");
1066 }
1067 else if (relatedDiscounts.GetString("Ecom:Product.Discount.Type") == "AMOUNT")
1068 {
1069 amount += relatedDiscounts.GetDouble("Ecom:Product.Discount.AmountWithVAT");
1070 }
1071 }
1072 if (percent > 0)
1073 {
1074 text = percent + "%";
1075 <div class="stickers-container__tag stickers-container__tag--sale dw-mod">@text</div>
1076 }
1077 if (amount > 0)
1078 {
1079 text = Dynamicweb.Ecommerce.Services.Currencies.Format(currency, amount);
1080 <div class="stickers-container__tag stickers-container__tag--sale dw-mod">@text</div>
1081 }
1082 break;
1083 default:
1084 if (GetLoop("ProductDiscounts").Count > 0)
1085 {
1086 text = Translate("Sale!");
1087 <div class="stickers-container__tag stickers-container__tag--sale dw-mod">@text</div>
1088 }
1089 break;
1090 }
1091
1092
1093 if (Converter.ToBoolean(Pageview.Area.Item["NewStickersEnable"]) && GetDate("Ecom:Product.Created").AddDays(Converter.ToDouble(Pageview.Area.Item["NewStickersExpiration"])) > DateTime.Now)
1094 {
1095 <div class="stickers-container__tag stickers-container__tag--new dw-mod">@Translate("New!")</div>
1096 }
1097
1098 if (!String.IsNullOrEmpty(GetString("Ecom:Product:Field.CustomSticker.Value")))
1099 {
1100 <div class="stickers-container__tag stickers-container__tag--custom dw-mod">@GetString("Ecom:Product:Field.CustomSticker.Value")</div>
1101 }
1102
1103 }
1104
1105
1106 </div>
1107 <img class="related-product__image b-lazy" src="/Files/Images/placeholder.gif" data-src="/Admin/Public/GetImage.ashx?width=350&height=350&crop=5&Compression=75&DoNotUpscale=true&image=@imgUrl" alt="@name" />
1108 <h4 class="related-product__name">@name</h4>
1109 <div class="related-product__number">@Translate("product number") @number</div>
1110
1111 <div class="product-list-item__stock">
1112 <div class="stock-icon ml-0 @(stock > 0 ? "stock-icon--in" : "stock-icon--not")"></div>
1113 <span>@stockText</span>
1114 </div>
1115
1116 <span class="product-card__quantity-alert js-product-quantity-error-@id ">@Translate("Max stock level is reached")</span>
1117 </a>
1118
1119 @if (!onlyPreview)
1120 {
1121 string priceWithDiscount = related.GetString("Ecom:Product.Discount.Price.Price");
1122 string currencySymbol = related.GetString("Ecom:Product.Currency.Symbol");
1123 string priceWithOutDiscount = related.GetString("Ecom:Product.Price.Price");
1124 bool productHasDiscount = GetBoolean("Ecom:Product.HaveDiscount");
1125
1126 <div class="related-product__button-container d-inline-block">
1127 @if (priceWithDiscount != "0,00" || priceWithOutDiscount != "0,00")
1128 {
1129 if (GetString("Ecom:Product.Discount.TotalAmount") != "DKK 0,00")
1130 {
1131 <div class="related-product__price withdiscount">@currencySymbol @priceWithDiscount</div>
1132 }
1133
1134 else
1135 {
1136 <div class="related-product__price withoutdiscount">@currencySymbol @priceWithOutDiscount</div>
1137 }
1138
1139 List<LoopItem> priceLoop = related.GetLoop("Product.Prices");
1140 if (priceLoop.Any(x => x.GetDouble("Ecom:Product.Prices.Quantity") > 1))
1141 {
1142 <div class="js-popup">
1143 <a class="js-popup-trigger">@Translate("DiscountText")</a>
1144 <div class="js-popup-modal popup_productrelatedview">
1145 <div class="js-popout-close nz-icon icon-nz-close-circled"></div>
1146 <ul class="grid__col-12 pt-5 p-0 d-flex flex-row flex-wrap">
1147
1148 @foreach (LoopItem priceItem in priceLoop.Where(x => x.GetDouble("Ecom:Product.Prices.Quantity") > 1))
1149 {
1150 <li class="grid__col-lg-6 p-0 mb-0 grid__col-12 align-items-center align-items-xl-start">@Translate("UnitPriceText") @priceItem.GetDouble("Ecom:Product.Prices.Quantity") @Translate("UnitPriceTextStk")</li>
1151 <li class="grid__col-lg-6 p-0 pb-3 grid__col-12 align-items-center align-items-xl-end">@priceItem.GetString("Ecom:Product.Prices.AmountFormatted")</li>
1152 }
1153 </ul>
1154 </div>
1155 </div>
1156 }
1157
1158 if (related.GetLoop("VariantGroups").Count > 0)
1159 {
1160 <div class="related-product__controls">
1161
1162
1163 <a href="@related.GetString("Ecom:Product.Link.Clean")" class="btn btn-secondary dw-mod js-scroll-btn d-inline-block px-5">@Translate("Select variant")</a>
1164
1165 </div>
1166
1167 }
1168 else
1169 {
1170 <div class="related-product__controls">
1171 <div class="product-card__quantity--condenced product-card__quantity related-product__quantity">
1172 <input id="Quantity_@id" class="product-card__quantity-input pointer-events js-quantity-input" name="Quantity@id" data-id="@id" type="number" min="1" max="@stock" value="1">
1173 <span class="product-card__quantity-add js-product-quantity-add" data-icon-nz="chevron-up"></span>
1174 <span class="product-card__quantity-subtract js-product-quantity-subtract" data-icon-nz="chevron-down"></span>
1175 </div>
1176 <button type="button" id="CartButton_@id" class="btn btn--primary related-product__cart" name="submit" onclick="Cart.AddToCart(event, '@id', document.getElementById('Quantity_@id').value, 'Unit_@id', 'Variant_@id');">
1177 <i class="fa fa-shopping-cart"></i>
1178 <span class="u-hidden-xs u-hidden-xxs"> @Translate("Add to cart")</span>
1179 </button>
1180 </div>
1181 }
1182
1183
1184 }
1185 else
1186 {
1187 <button type="button" id="CartButton_@id" class="btn btn-primary dw-mod js-scroll-btn d-inline-block px-5" name="submit" data-target=".js-target">
1188 <span class="u-hidden-xs u-hidden-xxs"> @Translate("CallForPrice")</span>
1189 </button>
1190 }
1191 </div>
1192
1193
1194 }
1195 else
1196 {
1197 <div class="related-product__login-label">@Translate("LoginText")</div>
1198 <label for="SignInModalTrigger" class="btn btn--primary u-no-margin sign-in-modal-trigger-button dw-mod related-product__login" onclick="setTimeout(function () { document.getElementById('LoginUsername').focus() }, 10)">@Translate("Sign in")</label>
1199 }
1200
1201
1202 </div>
1203 </div>
1204
1205
1206
1207
1208 }
1209
1210 }
1211
1212 </div>
1213 </div>
1214 </div>
1215 </section>
1216
1217
1218
1219 }
1220 </div>
1221
1222 @*
1223 @if(GetLoop("eCom:Related.CustomersWhoSawThisAlsoSaw").Any())
1224 {
1225 foreach (LoopItem item in GetLoop("eCom:Related.CustomersWhoSawThisAlsoSaw"))
1226 {
1227 relatedProductsFeed += item.GetString("Ecom:Product.ID");
1228 relatedProductsFeed += "";
1229 }
1230
1231 <div class="grid">
1232 <div class="grid__col-12">
1233 <div class="bg-gray bg-gray-full-width">
1234 <h2 class="h1 section-title section-title--condensed">@Translate("ecom-related-products-3", "Related products")</h2>
1235 <div class="js-handlebars-root" id="ProductList_@productId" data-template="ProductContainer" data-pre-render-template="ProductPreRenderContainer" data-json-feed="@relatedProductsFeed" data-preloader="overlay"></div>
1236 </div>
1237 </div>
1238 </div>
1239 }
1240 *@
1241
1242 @helper ProductPropertiesMainArea()
1243 {
1244 if (GetLoop("CustomFieldValues").Count > 0)
1245 {
1246 var visibleCount = 0;
1247
1248 <div class="specs-list specs-list--5">
1249 <dl class="table table--clean table--compact product-page__specs-list">
1250 @foreach (LoopItem customField in GetLoop("CustomFieldValues").Where(customField => customField.GetString("Product.CustomField.System") != "description").Where(customField => customField.GetString("Product.CustomField.System") != "LongDescription").Where(customField => customField.GetString("Product.CustomField.System") != "ShortDescription"))
1251 {
1252 string fieldValue = customField.GetString("Product.CustomField.Value.Clean");
1253
1254 if (customField.GetLoop("Product.CustomField.Options").Count > 0)
1255 {
1256 fieldValue = "";
1257 int count = 0;
1258
1259 foreach (LoopItem customFieldOptions in customField.GetLoop("Product.CustomField.Options").Where(customFieldOptions => !string.IsNullOrWhiteSpace(customFieldOptions.GetString("Product.CustomField.Option.Name"))))
1260 {
1261 if (customFieldOptions.GetBoolean("Product.CustomField.Option.IsSelected"))
1262 {
1263 if (count != 0)
1264 {
1265 fieldValue += ", ";
1266 }
1267
1268 fieldValue += customFieldOptions.GetString("Product.CustomField.Option.Name");
1269 count++;
1270 }
1271 }
1272 }
1273
1274 if (fieldValue == "False")
1275 {
1276 fieldValue = Translate("No");
1277 }
1278
1279 if (fieldValue == "True")
1280 {
1281 fieldValue = Translate("Yes");
1282 }
1283
1284
1285 if (!String.IsNullOrEmpty(customField.GetString("Product.CustomField.Name"))
1286 && !String.IsNullOrWhiteSpace(fieldValue)
1287 && customField.GetString("Product.CustomField.Name") != "Custom sticker"
1288 && customField.GetString("Product.CustomField.Name").Contains("Produkt tekst") == true)
1289 {
1290 if (!String.IsNullOrEmpty(customField.GetString("Document.FullPath")))
1291 {
1292 visibleCount++;
1293 <dd class="product-page__specs-value"><a href="@customField.GetString("Document.FullPath")" download title="@Translate("Download")" class="product__document"><span property="identifier">@getIconForFile(fieldValue) @Path.GetFileName(fieldValue)</span></a></dd>
1294 }
1295 else
1296 {
1297 visibleCount++;
1298 <dd class="product-page__specs-value"><span property="identifier">@fieldValue</span></dd>
1299 }
1300 }
1301 }
1302 </dl>
1303
1304 @if (visibleCount > 5)
1305 {
1306 <div class="js-load-more-specs specs-list__trigger">
1307 <span class="specs-list__load-more">@Translate("Show more")</span>
1308 <span class="specs-list__load-less">@Translate("Show less")</span>
1309 </div>
1310 }
1311
1312 </div>
1313 }
1314 }
1315 @helper ProductProperties()
1316 {
1317 if (GetLoop("CustomFieldValues").Count > 0)
1318 {
1319 var visibleCount = 0;
1320
1321 <div class="specs-list specs-list--10">
1322 <dl class="table table--clean table--compact product-page__specs-list">
1323 @foreach (LoopItem customField in GetLoop("CustomFieldValues").Where(customField => customField.GetString("Product.CustomField.System") != "description").Where(customField => customField.GetString("Product.CustomField.System") != "LongDescription").Where(customField => customField.GetString("Product.CustomField.System") != "ShortDescription"))
1324 {
1325 string fieldValue = customField.GetString("Product.CustomField.Value.Clean");
1326
1327 if (customField.GetLoop("Product.CustomField.Options").Count > 0)
1328 {
1329 fieldValue = "";
1330 int count = 0;
1331
1332 foreach (LoopItem customFieldOptions in customField.GetLoop("Product.CustomField.Options").Where(customFieldOptions => !string.IsNullOrWhiteSpace(customFieldOptions.GetString("Product.CustomField.Option.Name"))))
1333 {
1334 if (customFieldOptions.GetBoolean("Product.CustomField.Option.IsSelected"))
1335 {
1336 if (count != 0)
1337 {
1338 fieldValue += ", ";
1339 }
1340
1341 fieldValue += customFieldOptions.GetString("Product.CustomField.Option.Name");
1342 count++;
1343 }
1344 }
1345 }
1346 if (fieldValue == "False")
1347 {
1348 fieldValue = Translate("No");
1349 }
1350
1351 if (fieldValue == "True")
1352 {
1353 fieldValue = Translate("Yes");
1354 }
1355
1356 if (!String.IsNullOrEmpty(customField.GetString("Product.CustomField.Name"))
1357 && !String.IsNullOrWhiteSpace(fieldValue)
1358 && customField.GetString("Product.CustomField.Name") != "Custom sticker"
1359 && !customField.GetString("Product.CustomField.Name").Contains("Produktions ansvarlig")
1360 && !customField.GetString("Product.CustomField.Name").Contains("Video")
1361 && !customField.GetString("Product.CustomField.Name").Contains("PDF")
1362 && !customField.GetString("Product.CustomField.Name").Contains("Lokation")
1363 && !customField.GetString("Product.CustomField.Name").Contains("Status")
1364 && !customField.GetString("Product.CustomField.Name").Contains("Produktgr. (CO)")
1365 && !customField.GetString("Product.CustomField.Name").Contains("Produktprisgr. 1 (rabatgruppe)")
1366 && !customField.GetString("Product.CustomField.Name").Contains("Prisliste")
1367 && !customField.GetString("Product.CustomField.Name").Contains("Godkendelse")
1368 && !customField.GetString("Product.CustomField.Name").Contains("Salgsstatistik")
1369 && !customField.GetString("Product.CustomField.Name").Contains("ABC")
1370 && !customField.GetString("Product.CustomField.Name").Contains("Billedefilnr (LFR)")
1371 && !customField.GetString("Product.CustomField.Name").Contains("Rabat gruppe 2")
1372 && !customField.GetString("Product.CustomField.Name").Contains("Visma changed by user")
1373 && !customField.GetString("Product.CustomField.Name").Contains("Visma changed date")
1374 && customField.GetString("Product.CustomField.Value") != "0"
1375 && customField.GetString("Product.CustomField.Value") != ""
1376 && customField.GetString("Product.CustomField.Name").Contains("Produkt tekst") != true)
1377 {
1378 <dt class="product-page__specs-display" width="160">@Translate(customField.GetString("Product.CustomField.Name")): </dt>
1379 if (!String.IsNullOrEmpty(customField.GetString("Document.FullPath")))
1380 {
1381 visibleCount++;
1382 <dd class="text-right product-page__specs-value"><a href="@customField.GetString("Document.FullPath")" download title="@Translate("Download")" class="product__document"><span property="identifier">@getIconForFile(fieldValue) @Path.GetFileName(fieldValue)</span></a></dd>
1383 }
1384 else
1385 {
1386 visibleCount++;
1387 <dd class="text-right product-page__specs-value"><span property="identifier">@fieldValue</span></dd>
1388 }
1389 }
1390 }
1391 </dl>
1392
1393 @if (visibleCount > 10)
1394 {
1395 <div class="js-load-more-specs specs-list__trigger">
1396 <span class="specs-list__load-more">@Translate("Show more")</span>
1397 <span class="specs-list__load-less">@Translate("Show less")</span>
1398 </div>
1399 }
1400 </div>
1401 }
1402
1403 if (GetLoop("ProductCategories").Count > 0)
1404 {
1405 <dl class="product-page__specs-list">
1406 @foreach (LoopItem categoryGroup in GetLoop("ProductCategories"))
1407 {
1408 int fieldsCount = 0;
1409 foreach (LoopItem categoryField in categoryGroup.GetLoop("ProductCategoryFields"))
1410 {
1411 if (!String.IsNullOrEmpty(categoryField.GetString("Ecom:Product.CategoryField.Value")))
1412 {
1413 fieldsCount++;
1414 }
1415 }
1416 if (fieldsCount > 0)
1417 {
1418 <dd class="product-page__specs-display">@categoryGroup.GetString("Ecom:Product.Category.Name")</dd>
1419
1420 foreach (LoopItem categoryField in categoryGroup.GetLoop("ProductCategoryFields"))
1421 {
1422 string fieldValue = categoryField.GetString("Ecom:Product.CategoryField.Value");
1423
1424 if (fieldValue == "False")
1425 {
1426 fieldValue = Translate("No");
1427 }
1428
1429 if (fieldValue == "True")
1430 {
1431 fieldValue = Translate("Yes");
1432 }
1433
1434 if (!String.IsNullOrEmpty(categoryField.GetString("Ecom:Product.CategoryField.Label")) && !String.IsNullOrEmpty(fieldValue))
1435 {
1436 <dt class="product-page__specs-display">@categoryField.GetString("Ecom:Product.CategoryField.Label"):</dt>
1437 <dd class="text-right product-page__specs-value"><span property="identifier">@fieldValue</span></dd>
1438 }
1439 }
1440 }
1441 }
1442 </dl>
1443 }
1444 }
1445
1446 <script id="PricesAndActionsTemplate" type="text/x-template">
1447 {{#.}}
1448
1449 @if (!onlyPreview)
1450 {
1451 if (cfp == false)
1452 {
1453 <text>
1454 {{#ifCond hasVariants "!==" "disabled"}}
1455
1456 @if (GetInteger("Ecom:Product.Discount.TotalAmount") > 0)
1457 {
1458 <small class="text-gray font-weight-light">@Translate("NettoPrice")</small>
1459 <div class="text-gray font-weight-light product__price--discount {{onSale}}">{{#Discount}}{{#if @@first}}{{this.discountCurrency}} {{this.discount}}{{/if}}{{/Discount}}</div>
1460 <br />
1461 <small class="text-gray font-weight-light">@Translate("ProductDiscount")</small>
1462 <div class="text-gray font-weight-light product__price--discount {{onSale}}">{{#Discount}}{{#if @@first}}{{this.discountCurrency}} {{this.subtractPriceWithoutDecimals}}{{/if}}{{/Discount}}</div>
1463 <br />
1464 <small class="text-gray font-weight-light">@Translate("PriceWithDiscount")</small>
1465 }
1466 else
1467 {
1468 <small class="text-gray font-weight-light">@Translate("PriceWithoutDiscount")</small>
1469 }
1470
1471 @if (GetLoop("ProductDiscounts").Any())
1472 {
1473 <div class="h1 my-3 text-gray font-weight-light">{{price}}</div>
1474 }
1475 else
1476 {
1477 <div class="h1 my-3 text-gray font-weight-light">{{price}}</div>
1478 }
1479
1480 {{/ifCond}}
1481 </text>
1482
1483
1484 }
1485 if (!String.IsNullOrEmpty(GetString("Ecom:Product:Stock.Text")) || !String.IsNullOrEmpty(GetString("Ecom:Product:Stock.DeliveryText")))
1486 {
1487 if (GetLoop("Product.Prices").Count >= 1)
1488 {
1489
1490 var priceLoop = GetLoop("Product.Prices");
1491 var excludedIds = "1";
1492 var priceQuantity = priceLoop.Where(i => !excludedIds.Contains(i.GetString("Ecom:Product.Prices.Quantity")));
1493
1494 if (GetLoop("Product.Prices").Count > 1 && priceQuantity.Any())
1495 {
1496
1497
1498 <div class="js-popup">
1499 <a class="js-popup-trigger">@Translate("DiscountText")</a>
1500 <div class="js-popup-modal popup_productview">
1501 <div class="js-popout-close nz-icon icon-nz-close-circled"></div>
1502 <ul class="grid__col-12 pt-5 p-0 d-flex flex-row flex-wrap">
1503 @* FOREACH DISCOUNT LOOP FROM JSON FEED *@
1504 {{#each Discount}}
1505 {{#unless @@first}}
1506 <li class="grid__col-lg-6 p-0 mb-0 grid__col-12 align-items-center align-items-xl-start">@Translate("UnitPriceText") {{this.discountQuantity}} {{this.vismaUnitName}}</li>
1507 <li class="grid__col-lg-6 p-0 pb-3 grid__col-12 align-items-center align-items-xl-end">{{this.discountCurrency}} {{this.discount}}</li>
1508 {{/unless}}
1509 {{/each}}
1510
1511 </ul>
1512 </div>
1513 </div>
1514 }
1515 }
1516 <br>
1517
1518 if (!String.IsNullOrEmpty(GetString("Ecom:Product:Stock.Text")) || !String.IsNullOrEmpty(GetString("Ecom:Product:Stock.DeliveryText")))
1519 {
1520 string stockIcon = GetInteger("Ecom:Product.Stock") > 0 ? "product__stock-icon--in" : "product__stock-icon--not";
1521
1522 if (cfp == false)
1523 {
1524 <table class="table table--clean delivery-and-stock-info dw-mod">
1525 <tr>
1526 <td class="u-no-padding">
1527 <div class="stock-icon ml-0 @stockIcon"></div>
1528 <span class="font-weight-bold">@GetString("Ecom:Product:Stock.Text")</span>
1529 </td>
1530 </tr>
1531 <tr>
1532 @*
1533 @if (!String.IsNullOrEmpty(GetString("Ecom:Product:Stock.DeliveryText")))
1534 {
1535 <td class="u-no-padding">@Translate("Shipping") @GetString("Ecom:Product:Stock.DeliveryText") @GetString("Ecom:Product:Stock.DeliveryUnit")</td>
1536 }
1537 *@
1538 </tr>
1539 </table>
1540 }
1541 }
1542
1543 <div class="buttons-collection product__price-actions__actions dw-mod d-inline-block d-lg-flex mt-5 w-100">
1544
1545 @if (GetBoolean("Ecom:Product:Field.CFP"))
1546 {
1547 <div class="cfp-btn-container pt-4 pt-lg-0">
1548 <button type="button" id="CartButton_{{id}}" class="btn btn-primary dw-mod js-scroll-btn d-inline-block px-5" name="submit" data-target=".js-target">@Translate("CallForPrice")</button>
1549 </div>
1550 }
1551
1552 else
1553 {
1554 var disabledClass = String.IsNullOrWhiteSpace(variantId) && GetLoop("VariantGroups").Count > 0 ? "disabled" : "";
1555 @* Product quantity *@
1556 <div class="product-card__quantity product-card__quantity--condenced">
1557 <input id="Quantity_{{id}}" class="product-card__quantity-input pointer-events js-quantity-input" data-id="{{id}}" name="Quantity" type="number" min="1" max="@stockAmount" value="1">
1558 <span class="product-card__quantity-add js-product-quantity-add" data-icon-nz="chevron-up"></span>
1559 <span class="product-card__quantity-subtract js-product-quantity-subtract" data-icon-nz="chevron-down"></span>
1560 </div>
1561 <div class="product-btn-buy-container pt-4 pt-lg-0 pl-lg-3">
1562 {{#ifCond hasVariants "==" "disabled"}}
1563 <a href="{{link}}" class="btn btn-secondary dw-mod js-scroll-btn d-inline-block px-5 @disabledClass">@Translate("Select variant")</a>
1564 {{else}}
1565 <button type="button" id="CartButton_{{id}}" class="btn btn-primary dw-mod d-inline-block px-5 @disabledClass" name="submit" onclick="Cart.AddToCart(event, '{{productId}}', document.getElementById('Quantity_{{id}}').value, 'Unit_{{id}}', 'Variant_{{productId}}');">@Translate("Add to cart")</button>
1566 {{/ifCond}}
1567 </div>
1568 }
1569
1570 @if (Dynamicweb.Core.Converter.ToBoolean(GetGlobalValue("Global:Extranet.UserName")))
1571 {
1572 string favoriteId = "Favorite" + GetString("Ecom:Product.ID");
1573
1574 <div id="@favoriteId" class="favorites fav-product-container favorites--md d-inline-block dw-mod p-0 pl-lg-3">
1575 <label for="FavoriteTrigger" class="btn mb-0 btn-secondary"><span class="pr-2 product__add-to-favorites"></span><small class="x-small">@Translate("Add to list")</small></label>
1576 <input type="checkbox" id="FavoriteTrigger" class="dropdown-trigger">
1577 <div class="dropdown">
1578 <div class="dropdown__content dropdown__content--show-left dropdown__content--padding u-w220px dw-mod">
1579 @if (GetLoop("CustomerCenter.ListTypes").Any())
1580 {
1581 <ul class="list list--clean dw-mod">
1582 @foreach (LoopItem listType in GetLoop("CustomerCenter.ListTypes"))
1583 {
1584 foreach (LoopItem list in listType.GetLoop("CustomerCenter.ProductLists"))
1585 {
1586 <li>
1587 @{string favLinkType = list.GetString("Ecom:Product.List.IsProductInThisList") == "True" ? list.GetString("Ecom:Product.RemoveFromThisList") : list.GetString("Ecom:Product.AddToThisListAction");}
1588 @{string isInListIcon = list.GetString("Ecom:Product.List.IsProductInThisList") == "True" ? "fa fa-star" : "fa fa-star-o";}
1589 <a href="@favLinkType" class="list__link dw-mod"><i class="@isInListIcon"></i> @list.GetValue("Ecom:CustomerCenter.List.Name")</a>
1590 </li>
1591 }
1592 }
1593 </ul>
1594 }
1595
1596 else
1597 {
1598 <p>@Translate("CreateFavoritList")</p><a class="btn-sm btn-primary d-block text-center" href="@favoritPage">@Translate("CreateFavoritListButton")</a>
1599 }
1600
1601 </div>
1602 <label class="dropdown-trigger-off" for="FavoriteTrigger"></label>
1603 </div>
1604 </div>
1605 }
1606
1607
1608 </div>
1609
1610 <span class="product-card__quantity-alert js-product-quantity-error-{{id}}">@Translate("Max stock level is reached")</span>
1611 }
1612 }
1613
1614 else
1615 {
1616 <button type="button" id="CartButton_{{id}}" class="u-hidden"></button>
1617 }
1618 {{/.}}
1619 </script>
1620
1621 <script id="Units" type="text/x-template">
1622 <div class="dropdown__item dw-mod" onclick="HandlebarsBolt.UpdateContent('PriceAndActions', '/Default.aspx?ID=@feedId&UnitID={{value}}')">{{name}}</div>
1623 </script>
1624
1625
1626 @* Script templates for related products *@
1627 <script id="ProductPreRenderContainer" type="text/x-template">
1628 <div class="u-h600px u-full-width">
1629 <div class="grid">
1630 <div class="grid__col-12">
1631 <div class="pre-render-element pre-render-element--md"></div>
1632 </div>
1633 </div>
1634 </div>
1635 </script>
1636
1637 <script id="ProductContainer" type="text/x-template">
1638 {{#.}}
1639 <div class="u-min-h400px u-full-width">
1640 <div class="grid">
1641 <div class="grid__col-45px grid__col--bleed-x">
1642 <div class="grid__cell grid__cell--align-middle-left">
1643 <button class="btn btn--condensed btn--clean {{prevdisabled}} dw-mod" onclick="HandlebarsBolt.UpdateContent('ProductList_{{groupName}}', '{{prevPage}}')" {{prevdisabled}}><i class="fa fa-chevron-left fa-2x"></i></button>
1644 </div>
1645 </div>
1646 <div class="grid__col-auto grid__col--bleed-x">
1647 <div id="ProductsContainer" class="grid product-list dw-mod">
1648 {{#ProductsContainer}}
1649 <div id="Product{{productId}}" class="grid__col-@relatedProductsColumnWidth product-list__list-item dw-mod">
1650 <div class="product-list__inner">
1651 {{#Product}}
1652 <div class="grid__cell product-list__grid-item__image dw-mod {{noImage}}">
1653 <div class="stickers-container dw-mod">
1654 {{#Stickers}}
1655 {{>Sticker}}
1656 {{/Stickers}}
1657 </div>
1658 <a href="{{link}}" onclick="Scroll.SavePosition(event)"><img class="grid__cell-img grid__cell-img--centered b-lazy" src="/Files/Images/placeholder.gif" data-src="/Admin/Public/GetImage.ashx?width=300&height=300&crop=5&Compression=75&image={{image}}" alt="{{name}}" /></a>
1659 </div>
1660
1661 <div class="grid__cell product-list__grid-item__price-info {{shortGridInfo}} dw-mod">
1662 <a href="{{link}}" onclick="Scroll.SavePosition(event)" title="{{name}}"><h6 class="u-condensed-text">{{name}}</h6></a>
1663 <div class="item-number dw-mod">{{number}}</div>
1664
1665 @if (!onlyPreview && cfp == true)
1666 {
1667 <div class="price dw-mod">{{price}}</div>
1668 <div class="before-price {{onSale}} dw-mod">{{discount}}</div>
1669 }
1670 </div>
1671
1672 <div class="product-list__grid-item__footer dw-mod">
1673 <div class="u-ta-center">
1674
1675 @if (!onlyPreview && cfp == true)
1676 {
1677 <a href="{{link}}" id="CartButton_{{id}}" class="btn btn--secondary text-uppercase u-no-margin dw-mod">@Translate("View")</a>
1678 }
1679 else
1680 {
1681 <h5 class="mb-2">@Translate("LoginText", "Login")</h5>
1682 <label for="SignInModalTrigger" class="btn btn--primary u-no-margin sign-in-modal-trigger-button dw-mod" onclick="setTimeout(function () { document.getElementById('LoginUsername').focus() }, 10)">@Translate("Sign in")</label>
1683 }
1684 </div>
1685 </div>
1686 {{/Product}}
1687 </div>
1688 </div>
1689 {{/ProductsContainer}}
1690 </div>
1691 </div>
1692
1693 <div class="grid__col-45px grid__col--bleed-x">
1694 <div class="grid__cell grid__cell--align-middle-right">
1695 <button class="btn btn--condensed btn--clean {{nextdisabled}} dw-mod" onclick="HandlebarsBolt.UpdateContent('ProductList_{{groupName}}', '{{nextPage}}')" {{nextdisabled}}><i class="fa fa-chevron-right fa-2x"></i></button>
1696 </div>
1697 </div>
1698
1699 </div>
1700 </div>
1701 {{/.}}
1702 </script>
1703
1704 <script id="Sticker" type="text/x-template">
1705 <div class="stickers-container__tag {{className}} dw-mod">{{text}}</div>
1706 </script>
1707
1708 @* Units templates *@
1709
1710 <script id="UnitOption" type="text/x-template">
1711 <div class="dropdown__item dw-mod" onclick="HandlebarsBolt.UpdateContent('PriceAndActions', '{{link}}&feed=true&UnitID={{value}}&rid={{id}}')">{{name}}</div>
1712 </script>
1713
1714
1715 @*Variants data generation*@
1716
1717 <script>
1718 document.addEventListener("DOMContentLoaded", function (event) {
1719 var variants = [];
1720
1721 document.getElementById("productGrid").addEventListener('contentLoaded', function (e) {
1722 if (e.srcElement.classList.contains("js-variants-wrap")) {
1723 Variants.SetVariantOptionStatesForProductList(e.srcElement);
1724 }
1725 }, false);
1726
1727 @foreach (LoopItem variantgroup in GetLoop("VariantGroups"))
1728 {
1729 <text>var optionsArray = [];</text>
1730
1731 foreach (LoopItem variantoption in variantgroup.GetLoop("VariantAvailableOptions"))
1732 {
1733 string variantSelection = variantoption.GetBoolean("Ecom:VariantOption.Selected") ? "checked" : "";
1734
1735 <text>
1736 var option = new Variants.VariantOption("@uniqueId", "@GetString("Ecom:Product.ID")", "@variantoption.GetValue("Ecom:VariantOption.ID")", "@variantoption.GetString("Ecom:VariantOption.Name")", "@variantSelection", "", "@variantoption.GetString("Ecom:VariantOption.Color")", "@variantoption.GetString("Ecom:VariantOption.ImgSmall.Clean")");
1737 optionsArray.push(option);
1738 </text>
1739 }
1740
1741 <text>
1742 var group = new Variants.VariantGroup("@GetString("Ecom:Product.ID")_@variantgroup.GetValue("Ecom:VariantGroup.ID")", "@variantgroup.GetValue("Ecom:VariantGroup.Name")", optionsArray);
1743 variants.push(group);
1744 </text>
1745 }
1746
1747 var combinations = [];
1748 @foreach (LoopItem variantcomb in GetLoop("VariantStockCombinations"))
1749 {
1750 <text>
1751 combinations.push(new Variants.CombinationItem("@variantcomb.GetValue("Ecom:VariantStockCombination.VariantID")"));
1752 </text>
1753 }
1754
1755 variants = variants.sort(function (a, b) {
1756 var firstVariantId = a.VariantOptions[0].variantId;
1757 for (var i = 0; i < combinations.length; i++) {
1758 var combinationIndex = combinations[i].id.indexOf(firstVariantId);
1759 if (combinationIndex != -1) {
1760 return combinationIndex;
1761 }
1762 }
1763 //should never happen
1764 return 0;
1765 });
1766
1767 Variants.SetProductFeedId('@feedId');
1768 Variants.SetProductUrl('/@GetString("Ecom:Product.VariantLinkGroup.Clean")');
1769
1770 HandlebarsBolt.SetDataInCache(("Variants" + "@productId"), variants);
1771 HandlebarsBolt.SetDataInCache(("Combinations" + "@productId"), combinations);
1772
1773 document.getElementById("PriceAndActions").addEventListener('contentLoaded', function (e) {
1774 Variants.InitVariants(variants, combinations, "@productId", "@uniqueId");
1775 }, false);
1776
1777
1778 Handlebars.registerHelper('compare', function (lvalue, operator, rvalue, options) {
1779
1780 var operators, result;
1781
1782 if (arguments.length < 3) {
1783 throw new Error("Handlerbars Helper 'compare' needs 2 parameters");
1784 }
1785
1786 if (options === undefined) {
1787 options = rvalue;
1788 rvalue = operator;
1789 operator = "===";
1790 }
1791
1792 operators = {
1793 '==': function (l, r) { return l == r; },
1794 '===': function (l, r) { return l === r; },
1795 '!=': function (l, r) { return l != r; },
1796 '!==': function (l, r) { return l !== r; },
1797 '<': function (l, r) { return l < r; },
1798 '>': function (l, r) { return l > r; },
1799 '<=': function (l, r) { return l <= r; },
1800 '>=': function (l, r) { return l >= r; },
1801 'typeof': function (l, r) { return typeof l == r; }
1802 };
1803
1804 if (!operators[operator]) {
1805 throw new Error("Handlerbars Helper 'compare' doesn't know the operator " + operator);
1806 }
1807
1808 result = operators[operator](lvalue, rvalue);
1809
1810 if (result) {
1811 return options.fn(this);
1812 } else {
1813 return options.inverse(this);
1814 }
1815
1816 });
1817
1818
1819 });
1820 </script>
1821
1822 @functions {
1823 string getIconForFile(string fileName)
1824 {
1825 string ext = Path.GetExtension(fileName);
1826 string icon = "";
1827 switch (ext.ToLower())
1828 {
1829 case ".xls":
1830 case ".xlsx":
1831 icon = "fa-file-excel-o";
1832 break;
1833 case ".ppt":
1834 case ".pptx":
1835 icon = "fa-file-powerpoint-o";
1836 break;
1837 case ".doc":
1838 case ".docx":
1839 icon = "fa-file-word-o";
1840 break;
1841 case ".jpg":
1842 case ".jpeg":
1843 case ".png":
1844 case ".gif":
1845 case ".pdf":
1846 return "<img class='product__document-icon b-lazy' src='/Files/Images/placeholder.gif' data-src='/Admin/Public/GetImage.ashx?crop=5&height=70&width=120&Compression=75&DoNotUpscale=true&image=" + fileName + "' />";
1847 default:
1848 icon = "fa-file-o";
1849 break;
1850 }
1851 return "<i class='fa " + icon + "'></i> ";
1852 }
1853
1854 public static string ToPascalCase(string str)
1855 {
1856 return CultureInfo.InvariantCulture.TextInfo
1857 .ToTitleCase(str.ToLowerInvariant())
1858 .Replace("-", "")
1859 .Replace("_", "")
1860 .Replace(" ", "");
1861 }
1862 }
1863
1864 <script type="application/ld+json">
1865 {
1866 "@@context": "http://schema.org/",
1867 "@@type": "Product",
1868 "name": "@GetString("Ecom:Product.Name")",
1869 @if (!string.IsNullOrEmpty(GetString("Ecom:Product.ImageLarge.Default.Clean")))
1870 {
1871 <text>"image": [
1872 "@siteURL/Admin/Public/GetImage.ashx?width=400&height=400&crop=0&Compression=75&DoNotUpscale=true&image=@productImage",
1873 "@siteURL/Admin/Public/GetImage.ashx?width=400&height=300&crop=0&Compression=75&DoNotUpscale=true&image=@productImage",
1874 "@siteURL/Admin/Public/GetImage.ashx?width=448&height=225&crop=0&Compression=75&DoNotUpscale=true&image=@productImage"
1875 ],</text>
1876 }
1877 "description": "@GetString("Ecom: Product.ShortDescription")",
1878 "mpn": "925872",
1879 @if (!string.IsNullOrEmpty(brand))
1880 {
1881 <text>"brand": {
1882 "@@type": "Thing",
1883 "name": "@brand"
1884 },</text>
1885 }
1886 "offers": {
1887 "@@type": "Offer",
1888 "priceCurrency": "@GetString("Ecom:Product.Price.Currency.Code")",
1889 "price": "@GetString("Ecom:Product.Price.Price")",
1890 "availability": "@(GetInteger("Ecom:Product.Stock") > 0 ? "InStock" : "OutOfStock")"
1891 }
1892 }
1893 </script>
1894 <script>
1895 document.addEventListener("DOMContentLoaded", function () {
1896 if (document.getElementById("PriceAndActions")) {
1897 document.getElementById("PriceAndActions").addEventListener("contentLoaded", function (event) {
1898 if (document.querySelector(".js-variants") != null) {
1899 MatchVariants.Update(document.querySelector(".js-variants"), "DoNothing");
1900 }
1901 });
1902 }
1903 });
1904
1905 //Custom dropdown
1906 var isOpen = false;
1907
1908 function toggleOptions(id) {
1909
1910 isOpen = !isOpen;
1911 if (isOpen) {
1912 document.getElementById(id).style.visibility = 'visible';
1913 document.getElementById(id).classList.add("active");
1914 document.getElementById(id).focus();
1915 } else {
1916 document.getElementById(id).blur();
1917 document.getElementById(id).classList.remove("active");
1918 document.getElementById(id).style.visibility = 'hidden';
1919 }
1920
1921 }
1922
1923 function selected(val, id, blurId) {
1924 document.getElementById(id).innerHTML = val;
1925 toggleOptions(blurId);
1926 var variantContainer = $(".js-variants");
1927 variantContainer.find('.dropdown-container').each(function () {
1928
1929 var $this = $(this);
1930 var disabledItem = $this.find('.disabled');
1931 if (disabledItem) {
1932 var displayValueItem = $this.find('.value-text');
1933
1934 if (displayValueItem.html() == disabledItem.html()) {
1935 displayValueItem.html("@Translate("Select variant")");
1936 }
1937 }
1938
1939 });
1940 }
1941 </script>