Error executing template "Designs/Swift/Paragraph/Swift_ProductSpecification_EA.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
at CompiledRazorTemplates.Dynamic.RazorEngine_083c9137848c4be18e92f9579203104e.<>c__DisplayClass30_0.<RenderArticle>b__0(TextWriter __razor_helper_writer) in D:\dynamicweb.net\Solutions\ContextAnd\Plus_Prod\Files\Templates\Designs\Swift\Paragraph\Swift_ProductSpecification_EA.cshtml:line 1225
at CompiledRazorTemplates.Dynamic.RazorEngine_083c9137848c4be18e92f9579203104e.<>c__DisplayClass27_0.<RenderField>b__0(TextWriter __razor_helper_writer) in D:\dynamicweb.net\Solutions\ContextAnd\Plus_Prod\Files\Templates\Designs\Swift\Paragraph\Swift_ProductSpecification_EA.cshtml:line 959
at CompiledRazorTemplates.Dynamic.RazorEngine_083c9137848c4be18e92f9579203104e.<>c__DisplayClass26_0.<RenderFieldsFromList>b__0(TextWriter __razor_helper_writer) in D:\dynamicweb.net\Solutions\ContextAnd\Plus_Prod\Files\Templates\Designs\Swift\Paragraph\Swift_ProductSpecification_EA.cshtml:line 799
at CompiledRazorTemplates.Dynamic.RazorEngine_083c9137848c4be18e92f9579203104e.Execute() in D:\dynamicweb.net\Solutions\ContextAnd\Plus_Prod\Files\Templates\Designs\Swift\Paragraph\Swift_ProductSpecification_EA.cshtml:line 409
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
3 @using Dynamicweb.Frontend
4 @using System.IO
5 @using Dynamicweb.Ecommerce.ProductCatalog
6
7 @functions {
8 public ProductViewModel product { get; set; } = new ProductViewModel();
9 public string galleryLayout { get; set; }
10 public string[] supportedImageFormats { get; set; }
11 public string[] supportedVideoFormats { get; set; }
12 public string[] supportedDocumentFormats { get; set; }
13 public string[] allSupportedFormats { get; set; }
14
15 public class RatioSettings
16 {
17 public string Ratio { get; set; }
18 public string CssClass { get; set; }
19 public string CssVariable { get; set; }
20 public string Fill { get; set; }
21 }
22
23 public RatioSettings GetRatioSettings(string size = "desktop")
24 {
25 var ratioSettings = new RatioSettings();
26
27 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", "");
28 ratio = ratio != "0" ? ratio : "";
29 string cssClass = ratio != "" && ratio != "fill" ? " ratio" : "";
30 string cssVariable = ratio != "" && ratio != "fill" ? "--bs-aspect-ratio: " + ratio : "";
31 cssClass = ratio == "fill" && size == "mobile" ? " ratio" : cssClass;
32 cssVariable = ratio == "fill" && size == "mobile" ? "--bs-aspect-ratio: 66%" : cssVariable;
33
34 ratioSettings.Ratio = ratio;
35 ratioSettings.CssClass = cssClass;
36 ratioSettings.CssVariable = cssVariable;
37 ratioSettings.Fill = ratio == "fill" ? " h-100" : "";
38
39 return ratioSettings;
40 }
41 }
42 @{
43 bool isVisualEditor = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("VisualEdit")) ? Convert.ToBoolean(Dynamicweb.Context.Current.Request.QueryString.Get("VisualEdit")) : false;
44
45 ProductViewModel product = new ProductViewModel();
46
47 ProductViewModelSettings productSetting = new ProductViewModelSettings
48 {
49 LanguageId = Dynamicweb.Ecommerce.Common.Context.LanguageID,
50 CurrencyCode = Dynamicweb.Ecommerce.Common.Context.Currency.Code,
51 CountryCode = Dynamicweb.Ecommerce.Common.Context.Country.Code2,
52 ShopId = Pageview.Area.EcomShopId
53 };
54
55 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails"))
56 {
57 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"];
58 }
59 else if (Pageview.Item["DummyProduct"] != null)
60 {
61
62 string dummyProductId = "";
63 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page);
64 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel();
65 if (productList.Products != null)
66 {
67 foreach (var p in productList.Products) { dummyProductId = p.Id; }
68 ProductViewModel dummyProduct = dummyProductId != "" ? ViewModelFactory.CreateView(productSetting, dummyProductId) : new ProductViewModel();
69 product = dummyProduct;
70 }
71 else
72 {
73 product = ViewModelFactory.CreateView(productSetting, Dynamicweb.Ecommerce.Services.Products.GetLastActiveProducts(1, Dynamicweb.Ecommerce.Common.Context.LanguageID, false).FirstOrDefault().Id);
74 }
75 }
76 else if (Pageview.Item["DummyProduct"] == null)
77 {
78 product = ViewModelFactory.CreateView(productSetting, Dynamicweb.Ecommerce.Services.Products.GetLastActiveProducts(1, Dynamicweb.Ecommerce.Common.Context.LanguageID, false).FirstOrDefault().Id);
79 }
80 }
81
82 @if (product?.Id != null)
83 {
84 IEnumerable<string> selectedDisplayGroupIds = Model.Item.GetRawValueString("DisplayGroups").Split(',').ToList();
85 List<CategoryFieldViewModel> displayGroups = new List<CategoryFieldViewModel>();
86
87 foreach (var selection in selectedDisplayGroupIds)
88 {
89 foreach (CategoryFieldViewModel group in product.FieldDisplayGroups.Values)
90 {
91 if (selection == group.Id)
92 {
93 displayGroups.Add(group);
94 }
95 }
96 }
97
98 bool showProductFields = Model.Item.GetBoolean("ProductFields");
99
100 bool hideTitle = Model.Item.GetBoolean("HideTitle");
101
102 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : "";
103
104 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "display-4");
105
106 string contentPadding = Model.Item.GetRawValueString("ContentPadding", "");
107 contentPadding = contentPadding == "none" ? string.Empty : contentPadding;
108 contentPadding = contentPadding == "small" ? " p-2 p-md-3" : contentPadding;
109 contentPadding = contentPadding == "large" ? " p-4 p-md-5" : contentPadding;
110
111 string layout = Model.Item.GetRawValueString("Layout", "list");
112 string size = Model.Item.GetRawValueString("Size", "full");
113 string gaps = size == "full" ? " gap-4" : " gap-2";
114
115 //IDictionary<string, string> customFscFields = new Dictionary<string, string>();
116 //var anyFscDisplayGrps = false;
117 IDictionary<string, string> customDisplayFields = new Dictionary<string, string>();
118 var anyCustomDisplayGrps = false;
119 var montagevejledningField = product.ProductFields.TryGetValue("InstallationPdf", out FieldValueViewModel installationPdf);
120 var overensstemmelseserklæringField = product.ProductFields.TryGetValue("CompliancePdf", out FieldValueViewModel compliancePdf);
121 var styrkeberegningerField = product.ProductFields.TryGetValue("StacticCalculationsPDF", out FieldValueViewModel stacticCalculationsPDF);
122 product.ProductFields.TryGetValue("DokumentsPDF", out FieldValueViewModel dokumentsPDF);
123 var fscField = product.ProductFields.TryGetValue("FSC", out FieldValueViewModel fscTag);
124 var fscLinkField = product.ProductFields.TryGetValue("FSC_link", out FieldValueViewModel fscLinkTag);
125 var pefcLinkField = product.ProductFields.TryGetValue("PEFC_link", out FieldValueViewModel pefcLinkTag);
126 var videoLinkField = product.ProductFields.TryGetValue("VideoLink", out FieldValueViewModel videoLinkTag);
127 var iconPath = "/Files/Templates/Designs/Swift/Assets/Icons/";
128 var imageIcon = "file-image.svg";
129 var pdfIcon = "file-pdf.svg";
130 var fileIcon = "file-regular.svg";
131 var fscIcon = "file-fsc.svg";
132 var pefcIcon = "file-pefc.svg";
133 string colCount = "";
134
135
136 if (!String.IsNullOrEmpty(fscTag.Value.ToString()))
137 {
138 if (fscTag.Value.ToString().ToLower().Contains("fsc"))
139 {
140 customDisplayFields.Add(fscTag.Value.ToString(), fscLinkTag.Value.ToString());
141 }
142 if (fscTag.Value.ToString().ToLower().Contains("pefc"))
143 {
144 customDisplayFields.Add(fscTag.Value.ToString(), pefcLinkTag.Value.ToString());
145 }
146 anyCustomDisplayGrps = true;
147 }
148
149 if (!String.IsNullOrEmpty(installationPdf.Value.ToString()))
150 {
151 customDisplayFields.Add(installationPdf.Name.ToString(), installationPdf.Value.ToString());
152 anyCustomDisplayGrps = true;
153 }
154 if (!String.IsNullOrEmpty(compliancePdf.Value.ToString()))
155 {
156 customDisplayFields.Add(compliancePdf.Name.ToString(), compliancePdf.Value.ToString());
157 anyCustomDisplayGrps = true;
158 }
159 if (!String.IsNullOrEmpty(stacticCalculationsPDF.Value.ToString()))
160 {
161 customDisplayFields.Add(stacticCalculationsPDF.Name.ToString(), stacticCalculationsPDF.Value.ToString());
162 anyCustomDisplayGrps = true;
163 }
164 if (!String.IsNullOrEmpty(dokumentsPDF.Value.ToString()))
165 {
166 customDisplayFields.Add(dokumentsPDF.Name.ToString(), dokumentsPDF.Value.ToString());
167 anyCustomDisplayGrps = true;
168 }
169 if (!String.IsNullOrEmpty(videoLinkTag.Value.ToString()))
170 {
171 customDisplayFields.Add(videoLinkTag.Name.ToString(), videoLinkTag.Value.ToString());
172 anyCustomDisplayGrps = true;
173 }
174
175
176
177 if (Pageview.IsVisualEditorMode && displayGroups.Count() == 0)
178 {
179 product.ProductFields.Clear();
180 product.ProductFields.Add(Translate("Width"), new FieldValueViewModel { Name = Translate("Width"), Value = "99cm" });
181 product.ProductFields.Add(Translate("Height"), new FieldValueViewModel { Name = Translate("Height"), Value = "195cm" });
182 showProductFields = true;
183 }
184
185 if (layout == "commas")
186 {
187 gaps = size == "full" ? " gap-4" : " gap-2";
188
189 }
190
191 <div class="grid h-100@(gaps)@(theme)@(contentPadding) item_@Model.Item.SystemName.ToLower()">
192 @if ((product.ProductFields != null && Model.Item.GetBoolean("ProductFields")) || (product.ProductCategories != null && Model.Item.GetBoolean("CategoryFields")) || (displayGroups.Count != 0))
193 {
194 if (!hideTitle)
195 {
196 <h2 class="g-col-12 @titleFontSize mb-0">@Model.Item.GetString("Title")</h2>
197 }
198 }
199
200 @if (displayGroups.Count != 0)
201 {
202 if (layout != "accordion")
203 {
204 foreach (var group in displayGroups)
205 {
206 bool hideHeader = Model.Item.GetBoolean("HideGroupHeaders");
207
208 if (!hideHeader)
209 {
210 <h4 class="g-col-12 h4 mb-0">@group.Name</h4>
211 }
212
213 { @RenderFieldsFromList(group.Fields, layout, colCount) }
214 }
215 }
216 else
217 {
218 <div class="g-col-12">
219 <div class="accordion accordion-flush w-100" id="Specifications_@Model.ID">
220 @foreach (var group in displayGroups)
221 {
222 <div class="accordion-item">
223 <h2 class="accordion-header" id="SpecificationHeading_@group.Id">
224 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#SpecificationItem_@group.Id" aria-expanded="false" aria-controls="SpecificationItem_@group.Id">
225 @group.Name
226 </button>
227 </h2>
228 <div id="SpecificationItem_@group.Id" class="accordion-collapse collapse" aria-labelledby="SpecificationHeading_@group.Id" data-bs-parent="#Specifications_@Model.ID">
229 <div class="accordion-body">
230 @{ @RenderFieldsFromList(group.Fields, "list", colCount) }
231 </div>
232 </div>
233 </div>
234 }
235 </div>
236 </div>
237 }
238 }
239
240 @if (product.ProductFields != null && showProductFields)
241 {
242 if (product.ProductFields.Count > 0)
243 {
244 if (layout != "accordion")
245 {
246 {@RenderFieldsFromList(product.ProductFields, layout, colCount) }
247 }
248 else
249 {
250 <div class="g-col-12">
251 <div class="accordion accordion-flush w-100" id="Specifications_@Model.ID">
252 <div class="accordion-item">
253 <h2 class="accordion-header" id="SpecificationHeading_@Model.ID">
254 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#SpecificationItem_@Model.ID" aria-expanded="false" aria-controls="SpecificationItem_@Model.ID">
255 @Translate("Specifications")
256 </button>
257 </h2>
258 <div id="SpecificationItem_@Model.ID" class="accordion-collapse" aria-labelledby="SpecificationHeading_@Model.ID" data-bs-parent="#Specifications_@Model.ID">
259 <div class="accordion-body">
260 @{ @RenderFieldsFromList(product.ProductFields, "List", colCount) }
261 </div>
262 </div>
263 </div>
264 </div>
265 </div>
266 }
267 }
268 }
269
270 @if (product.ProductCategories != null && Model.Item.GetBoolean("CategoryFields"))
271 {
272 if (product.ProductCategories.Count > 0)
273 {
274 if (layout != "accordion")
275 {
276 foreach (var group in product.ProductCategories)
277 {
278 CategoryFieldViewModel category = group.Value;
279 bool hideHeader = Model.Item.GetBoolean("HideGroupHeaders");
280
281 if (!hideHeader)
282 {
283 <h4 class="g-col-12 h4 mb-0">@group.Value.Name</h4>
284 }
285
286 { @RenderFieldsFromList(category.Fields, layout, "") }
287 }
288 }
289 else
290 {
291 <div class="g-col-12">
292 <div class="accordion accordion-flush w-100" id="Specifications_@Model.ID">
293 @foreach (var group in product.ProductCategories)
294 {
295 CategoryFieldViewModel category = group.Value;
296
297 <div class="accordion-item">
298 <h2 class="accordion-header" id="SpecificationHeading_@group.Value.Id">
299 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#SpecificationItem_@group.Value.Id" aria-expanded="false" aria-controls="SpecificationItem_@group.Value.Id">
300 @group.Key
301 </button>
302 </h2>
303 <div id="SpecificationItem_@group.Value.Id" class="accordion-collapse" aria-labelledby="SpecificationHeading_@group.Value.Id" data-bs-parent="#Specifications_@Model.ID">
304 <div class="accordion-body">
305 @{ @RenderFieldsFromList(category.Fields, "list", colCount) }
306 </div>
307 </div>
308 </div>
309 }
310 </div>
311 </div>
312 }
313 }
314 }
315
316 @{ string displayGroupsList = "";
317 string divider = "";
318 foreach (var group in displayGroups)
319 {
320 displayGroupsList = "tab_" + group.Id + "," + "SpecificationItem_" + group.Id + divider + displayGroupsList;
321 divider = ",";
322 } }
323
324 @if (displayGroups.Count != 0)
325 {
326 if (layout != "navtabs")
327 {
328 foreach (var group in displayGroups)
329 {
330 bool hideHeader = Model.Item.GetBoolean("HideGroupHeaders");
331
332 if (!hideHeader)
333 {
334 <h4 class="g-col-12 h4 mb-0">@group.Name</h4>
335 }
336
337 { @RenderFieldsFromList(group.Fields, layout, colCount) }
338 }
339 }
340 else
341 {
342 bool first = true;
343 var displayNumber = 0;
344 var customDisplayGrpouPos = 3;
345
346 <div class="g-col-12 diplay-fields efa-mod d-none d-sm-block">
347 <ul class="nav nav-pills px-3 px-md-5 px-lg-7" id="pills-tab" role="tablist">
348 @{ foreach (var tab in displayGroups)
349 {
350 displayNumber++;
351 if (first)
352 {
353 <li class="nav-item pe-5 py-2 py-md-2" role="presentation">
354 <button class="nav-link active text-uppercase fc-dark bg-transparent p-0 efa-mod" id="@tab.Id-tab" onclick="toogleReset('tab_@tab.Id')" data-bs-toggle="tab" data-bs-target="#tab_@tab.Id" type="button" role="tab" aria-controls="@tab.Id" aria-selected="true">@tab.Name</button>
355 </li>
356 first = false;
357 }
358 else
359 {
360 <li class="nav-item @displayGroups.Count || @displayNumber pe-5 py-2 py-md-2" role="presentation">
361 <button class="nav-link text-uppercase fc-dark bg-transparent p-0 efa-mod" id="@tab.Id-tab" onclick="toogleReset('tab_@tab.Id')" data-bs-toggle="tab" data-bs-target="#tab_@tab.Id" type="button" role="tab" aria-controls="@tab.Id" aria-selected="false">@tab.Name</button>
362 </li>
363 }
364 if (displayNumber == customDisplayGrpouPos || (displayNumber == displayGroups.Count && displayNumber <= customDisplayGrpouPos))
365 {
366 if (anyCustomDisplayGrps)
367 {
368 <li class="nav-item pe-5 py-2 py-md-2" role="presentation">
369 <button class="nav-link text-uppercase fc-dark bg-transparent p-0 efa-mod" id="@Translate("Documents_downloads")-tab" onclick="toogleReset('tab_@Translate("Documents_downloads")')" data-bs-toggle="tab" data-bs-target="#tab_@Translate("Documents_downloads")" type="button" role="tab" aria-controls="@Translate("Documents_downloads")" aria-selected="false">@Translate("Documents/downloads")</button>
370 </li>
371 }
372 }
373 }
374 displayNumber = 0; }
375 </ul>
376 @{ first = true; }
377 <div class="tab-content bg-super-light efa-mod px-3 px-md-5 px-lg-7 py-4 py-md-5" id="pills-tabContent">
378 @{
379 foreach (var item in displayGroups)
380 {
381 displayNumber++;
382
383 if (item.Fields.Count >= 4)
384 {
385 colCount = "g-col-md-6 g-col-lg-4 g-col-xl-3";
386 }
387 else if (item.Fields.Count == 3)
388 {
389 colCount = "g-col-md-6 g-col-lg-4";
390 }
391 else if (item.Fields.Count == 2)
392 {
393 colCount = "g-col-md-6 g-col-lg-6";
394 }
395 else
396 {
397 colCount = "";
398 }
399
400 @*foreach(var field in item.Fields) {
401 <p><strong>@item.Id</strong></p>
402 <p>@field.Key</p>
403 }*@
404
405 if (first)
406 {
407 <div class="tab-pane fade show active collapsed efa-mod" id="tab_@item.Id" role="tabpanel" aria-labelledby="">
408 <div>
409 @{ @RenderFieldsFromList(item.Fields, "display-fields", colCount) }
410 </div>
411 <div class="readmore-overlay efa-mod">
412 <div class="readmore-overlay_button fs-7 fc-grey efa-mod" onclick="readMore('tab_@item.Id')">
413 @Translate("Read more")
414 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-chevron-down" viewBox="0 0 16 16">
415 <path fill-rule="evenodd" d="M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z" />
416 </svg>
417 </div>
418 </div>
419 </div>
420 first = false;
421 }
422 else
423 {
424
425 <div class="tab-pane fade collapsed efa-mod" id="tab_@item.Id" role="tabpanel" aria-labelledby="">
426 <div>
427 @{ @RenderFieldsFromList(item.Fields, "display-fields", colCount) }
428 </div>
429 <div class="readmore-overlay efa-mod">
430 <div class="readmore-overlay_button fs-7 fc-grey efa-mod" onclick="readMore('tab_@item.Id')">
431 @Translate("Read more")
432 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-chevron-down" viewBox="0 0 16 16">
433 <path fill-rule="evenodd" d="M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z" />
434 </svg>
435 </div>
436 </div>
437 </div>
438 }
439
440 if (displayNumber == customDisplayGrpouPos || (displayNumber == displayGroups.Count && displayNumber <= customDisplayGrpouPos))
441 {
442 if (anyCustomDisplayGrps)
443 {
444 string icon = "";
445
446 <div class="tab-pane fade collapsed efa-mod" id="tab_@Translate("Documents_downloads")" role="tabpanel" aria-labelledby="">
447 <div>
448 <div class="g-col-12 grid gap-0">
449
450 @{
451 if (customDisplayFields.Count >= 4)
452 {
453 colCount = "g-col-md-6 g-col-lg-4 g-col-xl-3";
454 }
455 else if (customDisplayFields.Count == 3)
456 {
457 colCount = "g-col-md-6 g-col-lg-4";
458 }
459 else if (customDisplayFields.Count == 2)
460 {
461 colCount = "g-col-md-6 g-col-lg-6";
462 }
463 else
464 {
465 colCount = "";
466 }
467 }
468
469 @foreach (var field in customDisplayFields)
470 {
471 if(field.Key.Contains("Video")) {
472 <div class="g-col-12 @colCount mb-3 pe-0 pe-md-4 align-items-center">
473 @RenderVideoPreview(field.Value, field.Key)
474 </div>
475 }
476 else
477 {
478 <div class="g-col-12 @colCount mb-3 pe-0 pe-md-4 d-flex align-items-center">
479 @if (field.Key.Contains("FSC"))
480 {
481 icon = iconPath + fscIcon;
482 }
483 else if (field.Key.Contains("PEFC"))
484 {
485 icon = iconPath + pefcIcon;
486 }
487 else if (field.Value.Contains(".pdf"))
488 {
489 icon = iconPath + pdfIcon;
490 }
491 else if (field.Value.Contains(".png") || field.Value.Contains(".jpg") || field.Value.Contains(".jpeg"))
492 {
493 icon = iconPath + imageIcon;
494 }
495 else
496 {
497 icon = iconPath + fileIcon;
498 }
499 <span class="me-2 icon-3 efa-mod">
500 @ReadFile(icon)
501 </span>
502 <a name="@icon" class="fw-bold m-0" href="@field.Value" title="@field.Key" target="_blank" rel="noopener">
503 <p class="m-0">@field.Key</p>
504 </a>
505 </div>
506
507 }
508 }
509 </div>
510 </div>
511 <div class="readmore-overlay efa-mod">
512 <div class="readmore-overlay_button fs-7 fc-grey efa-mod" onclick="readMore('tab_@Translate("Documents_downloads")')">
513 @Translate("Read more")
514 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-chevron-down" viewBox="0 0 16 16">
515 <path fill-rule="evenodd" d="M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z" />
516 </svg>
517 </div>
518 </div>
519 </div>
520 }
521 }
522
523 }
524
525 displayNumber = 0; }
526 </div>
527 </div>
528
529 <div class="g-col-12 d-block d-sm-none">
530 <div class="accordion accordion-flush w-100" id="Specifications_@Model.ID">
531 @{
532 foreach (var group in displayGroups)
533 {
534 displayNumber++;
535
536 if (group.Fields.Count >= 4)
537 {
538 colCount = "g-col-md-6 g-col-lg-4 g-col-xl-3";
539 }
540 else if (group.Fields.Count == 3)
541 {
542 colCount = "g-col-md-6 g-col-lg-4";
543 }
544 else if (group.Fields.Count == 2)
545 {
546 colCount = "g-col-md-6 g-col-lg-6";
547 }
548 else
549 {
550 colCount = "";
551 }
552
553 <div class="accordion-item">
554 <h2 class="accordion-header" id="SpecificationHeading_@group.Id">
555 <button class="accordion-button collapsed text-uppercase fc-dark efa-mod" type="button" data-bs-toggle="collapse" data-bs-target="#SpecificationItem_@group.Id" aria-expanded="false" aria-controls="SpecificationItem_@group.Id">
556 @group.Name
557 </button>
558 </h2>
559 <div id="SpecificationItem_@group.Id" class="accordion-collapse collapse collapsed efa-mod" aria-labelledby="SpecificationHeading_@group.Id" data-bs-parent="#Specifications_@Model.ID">
560 <div class="accordion-body bg-super-light efa-mod">
561 @{ @RenderFieldsFromList(group.Fields, "display-fields", colCount) }
562 </div>
563 <div class="readmore-overlay pb-3 efa-mod">
564 <div class="readmore-overlay_button fs-7 fc-grey efa-mod" onclick="readMore('SpecificationItem_@group.Id')">
565 @Translate("Read more")
566 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-chevron-down" viewBox="0 0 16 16">
567 <path fill-rule="evenodd" d="M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z" />
568 </svg>
569 </div>
570 </div>
571 </div>
572 </div>
573 if (displayNumber == customDisplayGrpouPos || (displayNumber == displayGroups.Count && displayNumber <= customDisplayGrpouPos))
574 {
575 if (anyCustomDisplayGrps)
576 {
577 string icon = "";
578
579 <div class="accordion-item">
580 <h2 class="accordion-header" id="SpecificationHeading_@Translate("Documents_downloads")">
581 <button class="accordion-button collapsed text-uppercase fc-dark efa-mod" type="button" data-bs-toggle="collapse" data-bs-target="#SpecificationItem_@Translate("Documents_downloads")" aria-expanded="false" aria-controls="SpecificationItem_@Translate("Documents_downloads")">
582 @Translate("Documents/downloads")
583 </button>
584 </h2>
585 <div id="SpecificationItem_@Translate("Documents_downloads")" class="accordion-collapse collapse collapsed efa-mod" aria-labelledby="SpecificationHeading_@Translate("Documents_downloads")" data-bs-parent="#Specifications_@Model.ID">
586 <div class="accordion-body bg-super-light efa-mod">
587 <div class="g-col-12 grid gap-0">
588
589 @{
590 if (customDisplayFields.Count >= 4)
591 {
592 colCount = "g-col-md-6 g-col-lg-4 g-col-xl-3";
593 }
594 else if (customDisplayFields.Count == 3)
595 {
596 colCount = "g-col-md-6 g-col-lg-4";
597 }
598 else if (customDisplayFields.Count == 2)
599 {
600 colCount = "g-col-md-6 g-col-lg-6";
601 }
602 else
603 {
604 colCount = "";
605 }
606 }
607
608 @foreach (var field in customDisplayFields)
609 {
610 if(field.Key.Contains("Video")) {
611 <div class="g-col-12 @colCount mb-3 pe-0 pe-md-4 d-flex align-items-start">
612 @RenderVideoPreview(field.Value, field.Key)
613 </div>
614
615 } else {
616 <div class="g-col-12 @colCount mb-3 pe-0 pe-md-4 d-flex align-items-start">
617
618 @if (field.Key.Contains("FSC"))
619 {
620 icon = iconPath + fscIcon;
621 }
622 else if (field.Key.Contains("PEFC"))
623 {
624 icon = iconPath + pefcIcon;
625 }
626 else if (field.Value.Contains(".pdf"))
627 {
628 icon = iconPath + pdfIcon;
629 }
630 else if (field.Value.Contains(".png") || field.Value.Contains(".jpg") || field.Value.Contains(".jpeg"))
631 {
632 icon = iconPath + imageIcon;
633 }
634 else
635 {
636 icon = iconPath + fileIcon;
637 }
638 <span class="me-2 icon-3 efa-mod">
639 @ReadFile(icon)
640 </span>
641 <a class="fw-bold m-0" href="@field.Value" title="@field.Key" target="_blank" rel="noopener">
642 <p class="m-0">@field.Key</p>
643 </a>
644
645 </div>
646 }
647 }
648 </div>
649 </div>
650 <div class="readmore-overlay pb-3 efa-mod">
651 <div class="readmore-overlay_button fs-7 fc-grey efa-mod" onclick="readMore('SpecificationItem_@Translate("Documents_downloads")')">
652 @Translate("Read more")
653 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-chevron-down" viewBox="0 0 16 16">
654 <path fill-rule="evenodd" d="M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z" />
655 </svg>
656 </div>
657 </div>
658 </div>
659 </div>
660 }
661 }
662 }
663
664 displayNumber = 0;
665 }
666 </div>
667 </div>
668 <script type="text/javascript">
669
670 var displayGroups = '@displayGroupsList';
671 var displayGroupList = displayGroups.split(',');
672
673 for (var i = 0; i < displayGroupList.length; i++) {
674
675 let elem = document.getElementById(displayGroupList[i]);
676
677 const observer = new ResizeObserver(change => {
678 const tapElem = change[0];
679 if (tapElem.contentRect.height > 600) {
680 elem.classList.add('readmore-container');
681 }
682 });
683 observer.observe(elem);
684 }
685
686 function readMore(id) {
687 let element = document.getElementById(id);
688 element.classList.toggle('collapsed');
689
690 let toggleElement = document.querySelector(`.readmore-overlay_button[onclick="readMore('${id}')"]`);
691 if (toggleElement) {
692 let chevronIcon = toggleElement.querySelector("svg"); // Keep the chevron icon
693 toggleElement.innerHTML = element.classList.contains('collapsed')
694 ? '@Translate("Read more")'
695 : '@Translate("Read less")';
696
697 if (chevronIcon) {
698 toggleElement.appendChild(chevronIcon); // Reattach the chevron icon
699 }
700 }
701 };
702 </script>
703 }
704 }
705
706 @if (product.ProductFields != null && showProductFields)
707 {
708 if (product.ProductFields.Count > 0)
709 {
710 if (layout != "navtabs")
711 {
712 {@RenderFieldsFromList(product.ProductFields, layout, "") }
713 }
714 else
715 {
716 <div class="g-col-12">
717 <div class="accordion accordion-flush w-100" id="Specifications_@Model.ID">
718 <div class="accordion-item">
719 <h2 class="accordion-header" id="SpecificationHeading_@Model.ID">
720 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#SpecificationItem_@Model.ID" aria-expanded="false" aria-controls="SpecificationItem_@Model.ID">
721 @Translate("Specifications")
722 </button>
723 </h2>
724 <div id="SpecificationItem_@Model.ID" class="accordion-collapse" aria-labelledby="SpecificationHeading_@Model.ID" data-bs-parent="#Specifications_@Model.ID">
725 <div class="accordion-body">
726 @{ @RenderFieldsFromList(product.ProductFields, "List", colCount) }
727 </div>
728 </div>
729 </div>
730 </div>
731 </div>
732 }
733 }
734 }
735
736 @if (product.ProductCategories != null && Model.Item.GetBoolean("CategoryFields"))
737 {
738 if (product.ProductCategories.Count > 0)
739 {
740 if (layout != "navtabs")
741 {
742 foreach (var group in product.ProductCategories)
743 {
744 CategoryFieldViewModel category = group.Value;
745 bool hideHeader = Model.Item.GetBoolean("HideGroupHeaders");
746
747 if (!hideHeader)
748 {
749 <h4 class="g-col-12 h4 mb-0">@group.Value.Name</h4>
750 }
751
752 { @RenderFieldsFromList(category.Fields, layout, colCount) }
753 }
754 }
755 else
756 {
757 <div class="g-col-12">
758 <div class="accordion accordion-flush w-100" id="Specifications_@Model.ID">
759 @foreach (var group in product.ProductCategories)
760 {
761 CategoryFieldViewModel category = group.Value;
762
763 <div class="accordion-item">
764 <h2 class="accordion-header" id="SpecificationHeading_@group.Value.Id">
765 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#SpecificationItem_@group.Value.Id" aria-expanded="false" aria-controls="SpecificationItem_@group.Value.Id">
766 @group.Key
767 </button>
768 </h2>
769 <div id="SpecificationItem_@group.Value.Id" class="accordion-collapse" aria-labelledby="SpecificationHeading_@group.Value.Id" data-bs-parent="#Specifications_@Model.ID">
770 <div class="accordion-body">
771 @{ @RenderFieldsFromList(category.Fields, "list", colCount) }
772 </div>
773 </div>
774 </div>
775 }
776 </div>
777 </div>
778 }
779 }
780 }
781 </div>
782 }
783 else if (Pageview.IsVisualEditorMode)
784 {
785 <div class="alert alert-warning m-0">@Translate("No products available")</div>
786 }
787
788 @helper RenderFieldsFromList(Dictionary<string, FieldValueViewModel> fields, string layout, string colCount)
789 {
790 string size = Model.Item.GetRawValueString("Size", "full");
791 string gaps = size != "full" ? " gap-1" : string.Empty;
792 bool hideFieldLabels = Model.Item.GetBoolean("HideFieldLabels");
793
794 if (layout == "display-fields")
795 {
796 <div class="g-col-12 grid gap-0">
797 @foreach (var field in fields)
798 {
799 {@RenderField(field.Value, layout, colCount)}
800 }
801 </div>
802 }
803 if (layout == "columns")
804 {
805 <div class="g-col-12 grid@(gaps)">
806 @foreach (var field in fields)
807 {
808 {@RenderField(field.Value, layout, "")}
809 }
810 </div>
811 }
812 if (layout == "list")
813 {
814 <dl class="g-col-12 grid@(gaps)">
815 @foreach (var field in fields)
816 {
817 {@RenderField(field.Value, layout, "")}
818 }
819 </dl>
820 }
821 if (layout == "table")
822 {
823 string tableSize = size == "full" ? "" : " table-sm";
824 <div class="g-col-12">
825 <table class="table table-striped@(tableSize)">
826 @foreach (var field in fields)
827 {
828 {@RenderField(field.Value, layout, "")}
829 }
830 </table>
831 </div>
832 }
833 if (layout == "bullets")
834 {
835 string listSize = size == "full" ? "" : "m-0 p-0 lh-1 fs-7 opacity-75";
836 string listStyle = size == "full" ? "" : "style=\"list-style-position: inside\"";
837 <div class="g-col-12">
838 <ul class="@listSize" @listStyle>
839 @foreach (var field in fields)
840 {
841 {@RenderField(field.Value, layout, "")}
842 }
843 </ul>
844 </div>
845 }
846 if (layout == "commas")
847 {
848 List<string> featuresList = new List<string>();
849
850 foreach (var field in fields)
851 {
852 if (field.Value.Value is object && !string.IsNullOrEmpty(field.Value.Value.ToString()))
853 {
854 if (field.Value.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>))
855 {
856 List<string> options = new List<string>();
857 foreach (FieldOptionValueViewModel option in field.Value.Value as System.Collections.Generic.List<FieldOptionValueViewModel>)
858 {
859 if (!string.IsNullOrWhiteSpace(option.Value))
860 {
861 if (option.Value.ToString().Contains("#") && (Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour")))
862 {
863 string colorSpan = "<span class=\"colorbox-sm\" style=\"background-color: " + option.Value + "\"></span>";
864 options.Add(colorSpan);
865 }
866 else if (!string.IsNullOrEmpty(option.Value))
867 {
868 options.Add(option.Name);
869 }
870 }
871 }
872 string optionsString = (string.Join(", ", options.Select(x => x.ToString()).ToArray()));
873 if ((Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour")))
874 {
875 optionsString = (string.Join(" ", options.Select(x => x.ToString()).ToArray()));
876 }
877
878 if (!hideFieldLabels)
879 {
880 featuresList.Add(field.Value.Name + ": " + optionsString);
881 }
882 else
883 {
884 featuresList.Add(optionsString);
885 }
886 }
887 else
888 {
889 if (!string.IsNullOrWhiteSpace(field.Value.Value.ToString()))
890 {
891 if (field.Value.Value.ToString().Contains("#") && (Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour")))
892 {
893 string colorSpan = "<span class=\"colorbox-sm\" style=\"background-color: " + field.Value.Value + "\"></span>";
894
895 if (!hideFieldLabels)
896 {
897 featuresList.Add(field.Value.Name + ": " + colorSpan);
898 }
899 else
900 {
901 featuresList.Add(colorSpan);
902 }
903 }
904 else
905 {
906 if (!hideFieldLabels)
907 {
908 featuresList.Add(field.Value.Name + ": " + field.Value.Value.ToString());
909 }
910 else
911 {
912 featuresList.Add(field.Value.Value.ToString());
913 }
914 }
915 }
916 }
917 }
918 }
919
920 string featuresString = (string.Join(", ", featuresList.Select(x => x.ToString()).ToArray()));
921
922 <div class="g-col-12 opacity-75 fs-7">@featuresString</div>
923 }
924 }
925
926 @helper RenderField(FieldValueViewModel field, string layout, string colCount)
927 {
928 string size = Model.Item.GetRawValueString("Size", "full");
929 string fieldValue = field?.Value != null ? field.Value.ToString() : "";
930 bool hideFieldLabels = Model.Item.GetBoolean("HideFieldLabels");
931 bool noValues = false;
932 string orderLast = "";
933
934 if (!string.IsNullOrEmpty(fieldValue))
935 {
936 if (field.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>))
937 {
938 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>;
939 noValues = values.Count > 0 ? false : true;
940 }
941 }
942
943 if (!string.IsNullOrEmpty(fieldValue) && noValues == false)
944 {
945 if (layout == "display-fields")
946 {
947 if (fieldValue == "1") {
948 orderLast = "order-last";
949 }
950
951 <div class="g-col-12 @colCount @orderLast mb-3 pe-0 pe-md-4">
952
953 @if (field.SystemName.Contains("InspirationArticleTabId"))
954 {
955 var dummy = false;
956 if (fieldValue == "1") {
957 dummy = true;
958 } else {
959 @RenderArticle(fieldValue, dummy)
960 }
961 }
962 else
963 {
964 if (!hideFieldLabels)
965 {
966 if (field?.SystemName != "ProductLongDescription")
967 {
968 if (Dynamicweb.Ecommerce.Common.Context.LanguageID == "LANG2")
969 {
970 <p class="fw-bold m-0">@Translate(field?.Name):</p>
971 }
972 else
973 {
974 <p class="fw-bold m-0">@field.Name:</p>
975 }
976 }
977 }
978 <p class="text-break m-0">
979 @{ @RenderFieldValue(field) }
980 </p>
981 }
982 </div>
983 }
984 if (layout == "columns")
985 {
986
987 <div class="grid g-col-6 g-col-lg-4 gap-1">
988 @if (!hideFieldLabels)
989 {
990 if (field?.SystemName != "ProductLongDescription")
991 {
992 if (Dynamicweb.Ecommerce.Common.Context.LanguageID == "LANG2")
993 {
994 <dt class="g-col-12 g-col-lg-4">@Translate(field?.Name)</dt>
995 }
996 else
997 {
998 <dt class="g-col-12 g-col-lg-4">@field?.Name</dt>
999 }
1000 }
1001 }
1002 <dd class="g-col-12 g-col-lg-8 mb-0 text-break">
1003 @{ @RenderFieldValue(field) }
1004 </dd>
1005 </div>
1006 }
1007 if (layout == "list")
1008 {
1009 if (!hideFieldLabels)
1010 {
1011 if (field?.SystemName != "ProductLongDescription")
1012 {
1013 if (Dynamicweb.Ecommerce.Common.Context.LanguageID == "LANG2")
1014 {
1015 <dt class="g-col-4">@Translate(field?.Name)</dt>
1016 }
1017 else
1018 {
1019 <dt class="g-col-4">@field?.Name</dt>
1020 }
1021 }
1022 }
1023 <dd class="g-col-8 mb-0 text-break">
1024 @{ @RenderFieldValue(field) }
1025 </dd>
1026 }
1027 if (layout == "table")
1028 {
1029 <tr>
1030 @if (!hideFieldLabels)
1031 {
1032 if (field?.SystemName != "ProductLongDescription")
1033 {
1034 if (Dynamicweb.Ecommerce.Common.Context.LanguageID == "LANG2")
1035 {
1036 <th class="w-25 w-lg-50" scope="row">@Translate(field?.Name)</th>
1037 }
1038 else
1039 {
1040 <th class="w-25 w-lg-50" scope="row">@field?.Name</th>
1041 }
1042 }
1043 }
1044 <td class="text-break">
1045 @{ @RenderFieldValue(field) }
1046 </td>
1047 </tr>
1048 }
1049 if (layout == "bullets")
1050 {
1051 <li>
1052 @if (!hideFieldLabels)
1053 {
1054 if (field?.SystemName != "ProductLongDescription")
1055 {
1056 if (Dynamicweb.Ecommerce.Common.Context.LanguageID == "LANG2")
1057 {
1058 <strong>@Translate(field?.Name)</strong>
1059 }
1060 else
1061 {
1062 <strong>@field?.Name</strong>
1063 }
1064 }
1065 }
1066 <span>
1067 @{ @RenderFieldValue(field) }
1068 </span>
1069 </li>
1070 }
1071 }
1072 }
1073
1074 @helper RenderFieldValue(FieldValueViewModel field)
1075 {
1076 string fieldValue = field?.Value != null ? field.Value.ToString() : "";
1077
1078 bool isLink = field?.Type == "Link";
1079 bool isColor = false;
1080 bool isBrandName = field?.SystemName == "Brand_name";
1081 bool isYoutubeVideo = field?.SystemName == "VideoLink";
1082
1083 fieldValue = fieldValue == "False" ? Translate("No") : fieldValue;
1084 fieldValue = fieldValue == "True" ? Translate("Yes") : fieldValue;
1085
1086
1087 if (field.Value.GetType() == typeof(System.Collections.Generic.List<Dynamicweb.Ecommerce.ProductCatalog.FieldOptionValueViewModel>))
1088 {
1089 int valueCount = 0;
1090 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>;
1091 int totalValues = values.Count;
1092
1093 foreach (FieldOptionValueViewModel option in values)
1094 {
1095 if (!string.IsNullOrEmpty(option.Value))
1096 {
1097 if (option.Value.Substring(0, 1) == "#")
1098 {
1099 isColor = true;
1100 }
1101 }
1102
1103 if (!isColor)
1104 {
1105 @option.Name
1106 }
1107 else
1108 {
1109 <span class="colorbox-sm" style="background-color: @option.Value" title="@option.Name"></span>
1110 }
1111
1112 if (valueCount != totalValues && valueCount < (totalValues - 1))
1113 {
1114 if (isColor)
1115 {
1116 <text> </text>
1117 }
1118 else
1119 {
1120 <text>, </text>
1121 }
1122 }
1123 valueCount++;
1124 }
1125 }
1126 else
1127 {
1128 if (fieldValue.Substring(0, 1) == "#")
1129 {
1130 isColor = true;
1131 }
1132
1133 if (!isColor)
1134 {
1135 if (isLink)
1136 {
1137 string linktTitle = !fieldValue.Contains("aspx") ? fieldValue : Translate("Go to link");
1138 string target = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") && fieldValue.Contains("http") ? "target=\"_blank\"" : string.Empty;
1139 string rel = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") && fieldValue.Contains("http") ? "rel=\"noopener\"" : string.Empty;
1140 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/";
1141 if (isYoutubeVideo)
1142 {
1143 var videoId = linktTitle;
1144
1145 videoId = videoId.StartsWith("http://") ? videoId.Replace("http://", "") : "";
1146
1147 linktTitle = "https://img.youtube.com/vi/" + linktTitle.Substring(linktTitle.LastIndexOf('=') + 1) + "/maxresdefault.jpg";
1148
1149 RatioSettings ratioSettings = GetRatioSettings("desktop");
1150
1151 <div id="SmallScreenImagesThumbnails_@Model.ID" class="grid grid-2 gap-2 overflow-x-auto my-3">
1152 <div class="border outline-none @(ratioSettings.CssClass)" style="@(ratioSettings.CssVariable); cursor: pointer" data-bs-target="#SmallScreenImages_@Model.ID" data-bs-slide-to="">
1153 <a class="d-flex align-items-center justify-content-center" href="@field.Value" @target @rel>
1154 <div class="icon-3 position-absolute text-light" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div>
1155 <img src="@linktTitle" alt="" class="p-0 p-lg-1 w-100 h-100" style="object-fit: contain" data-video-id="@field.Value">
1156 </a>
1157 </div>
1158 </div>
1159 }
1160 else
1161 {
1162 <a href="@field.Value" title="@field.Name" @target @rel>@linktTitle</a>
1163 }
1164
1165 }
1166 else if (isBrandName)
1167 {
1168 <span itemprop="brand" itemtype="https://schema.org/Brand" itemscope>
1169 <span itemprop="name">@fieldValue</span>
1170 </span>
1171 }
1172
1173 else
1174 {
1175 @fieldValue
1176 }
1177 }
1178 else
1179 {
1180 <span class="colorbox-sm" style="background-color: @fieldValue" title="@fieldValue"></span>
1181 }
1182 }
1183 }
1184
1185 @helper RenderVideoPreview(string fieldValue, string fieldName) {
1186
1187 string linktTitle = !fieldValue.Contains("aspx") ? fieldValue : Translate("Go to link");
1188 string target = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") && fieldValue.Contains("http") ? "target=\"_blank\"" : string.Empty;
1189 string rel = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") && fieldValue.Contains("http") ? "rel=\"noopener\"" : string.Empty;
1190 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/";
1191
1192 var videoId = linktTitle;
1193
1194 videoId = videoId.StartsWith("http://") ? videoId.Replace("http://", "") : "";
1195
1196 linktTitle = "https://img.youtube.com/vi/" + linktTitle.Substring(linktTitle.LastIndexOf('=') + 1) + "/maxresdefault.jpg";
1197
1198 RatioSettings ratioSettings = GetRatioSettings("desktop");
1199
1200 <p class="fw-bold m-0">@(fieldName):</p>
1201 <div id="SmallScreenImagesThumbnails_@Model.ID" class="grid grid-2 gap-2 overflow-x-auto my-3">
1202 <div class="border outline-none @(ratioSettings.CssClass)" style="@(ratioSettings.CssVariable); cursor: pointer" data-bs-target="#SmallScreenImages_@Model.ID" data-bs-slide-to="">
1203 <a class="d-flex align-items-center justify-content-center" href="@fieldValue" @target @rel>
1204 <div class="icon-3 position-absolute text-light" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div>
1205 <img src="@linktTitle" alt="" class="p-0 p-lg-1 w-100 h-100" style="object-fit: contain" data-video-id="@fieldValue">
1206 </a>
1207 </div>
1208 </div>
1209
1210 }
1211
1212 @helper RenderArticle(string fieldValue, bool dummy) {
1213
1214 if (!string.IsNullOrEmpty(fieldValue)) {
1215
1216 if (dummy) {
1217 //dummy filler
1218 }
1219 else
1220 {
1221 int id = fieldValue != "0" ? Int32.Parse(fieldValue) : 0;
1222 if (id > 0) {
1223
1224 var specialPage = Dynamicweb.Content.Services.Pages.GetPage(id);
1225 string title = specialPage.Item.GetItem("Title").ToString();
1226 string coverImagePath = !string.IsNullOrEmpty(specialPage.Item.GetItem("CoverImage").ToString()) ? specialPage.Item.GetItem("CoverImage").ToString() : string.Empty;
1227
1228 <article class="d-flex flex-column mb-3 gap-0 theme plus_green-white_block h-100 shadow-hover overflow-hidden lift" itemscope="" itemtype="https://schema.org/CreativeWork" style="z-index:100;">
1229
1230 <a class="w-100 " title="@specialPage.Item.GetItem("Title")" href="@specialPage.GetPageHrefValue()" tabindex="-1">
1231
1232 <figure class="h-lg-100 overflow-hidden m-0 mx-auto ratio ratio-16x9" aria-label="@coverImagePath">
1233 @RenderImage(coverImagePath, title, "50% 50%", "object-fit: cover", "2", "2")
1234 </figure>
1235 </a>
1236 <div class="d-flex flex-column flex-grow-1 gap-3 w-100 p-3 p-md-4">
1237 <a class="text-decoration-none text-decoration-underline-hover" href="@specialPage.GetPageHrefValue()">
1238 <h3 class="h3 mb-0" itemprop="headline">@specialPage.Item.GetItem("Title")</h3>
1239 </a>
1240 <p class="m-0 opacity-75">@specialPage.Item.GetItem("Summary")</p>
1241 </div>
1242 </article>
1243 }
1244
1245 }
1246
1247 }
1248 }
1249
1250 @helper RenderImage(string coverImagePath, string title, string cssPosition, string imageObjectFit, string gridSettings, string carouselSettings)
1251 {
1252 switch(coverImagePath)
1253 {
1254 case string a when a.Contains(".jpeg"):
1255 coverImagePath = coverImagePath.Substring(0, coverImagePath.IndexOf(".jpeg") + 5);
1256 break;
1257 case string b when b.Contains(".jpg"):
1258 coverImagePath = coverImagePath.Substring(0, coverImagePath.IndexOf(".jpg") + 4);
1259 break;
1260 case string c when c.Contains(".png"):
1261 coverImagePath = coverImagePath.Substring(0, coverImagePath.IndexOf(".png") + 4);
1262 break;
1263
1264 }
1265
1266 coverImagePath = Dynamicweb.Context.Current.Server.UrlEncode(coverImagePath);
1267
1268 string imgSizeSelector = "50vw";
1269
1270 if (gridSettings == "1" || carouselSettings == "1")
1271 {
1272 imgSizeSelector = "100vw";
1273 }
1274 else if (gridSettings == "2" || carouselSettings == "2")
1275 {
1276 imgSizeSelector = "50vw";
1277 }
1278 else if (gridSettings == "3" || carouselSettings == "3")
1279 {
1280 imgSizeSelector = "33vw";
1281 }
1282 else if (gridSettings == "4" || carouselSettings == "4")
1283 {
1284 imgSizeSelector = "25vw";
1285 }
1286 else if (gridSettings == "5" || carouselSettings == "5")
1287 {
1288 imgSizeSelector = "17vw";
1289 }
1290
1291 string coverImagePathM = $"/Admin/Public/GetImage.ashx?image={coverImagePath}&width=640&quality=85&format=webp";
1292 string coverImagePathL = $"/Admin/Public/GetImage.ashx?image={coverImagePath}&width=960&quality=85&format=webp";
1293 string coverImagePathXL = $"/Admin/Public/GetImage.ashx?image={coverImagePath}&width=1280&quality=85&format=webp";
1294 string coverImagePathXXL = $"/Admin/Public/GetImage.ashx?image={coverImagePath}&width=1920&quality=85&format=webp";
1295 string imagePathFallBack = coverImagePathM;
1296
1297 <img srcset="
1298 @coverImagePathM 640w,
1299 @coverImagePathL 960w,
1300 @coverImagePathXL 1280w,
1301 @coverImagePathXXL 1920w"
1302 src="@imagePathFallBack"
1303 sizes="(min-width: 992px) @imgSizeSelector, 100vw"
1304 loading="lazy"
1305 decoding="async"
1306 class="img-fluid image-zoom-lg-1-hover"
1307 style="@imageObjectFit; object-position: @cssPosition;"
1308 alt="@title">
1309 }
1310
1311