Error parsing template "/Designs/Swift/Paragraph/Swift_ProductSpecification_Custom.cshtml"
Line 214: (213:39) - The "string," element was not closed. All elements must be either self-closing or have a matching end tag.
1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel>
2 @using Dynamicweb.Ecommerce.ProductCatalog
3
4 @{
5 ProductViewModel product = null;
6 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails") && !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("ProductID")))
7 {
8 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"];
9 }
10 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode)
11 {
12 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page);
13 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel();
14
15 if (productList?.Products is object)
16 {
17 product = productList.Products[0];
18 }
19 }
20 }
21
22 @if (product is object && product != null) {
23 {
24 IEnumerable<string> selectedDisplayGroupIds = Model.Item.GetRawValueString("DisplayGroups").Split(',').ToList();
25 List<CategoryFieldViewModel> displayGroups = new List<CategoryFieldViewModel>();
26
27 foreach (var selection in selectedDisplayGroupIds)
28 {
29 foreach (CategoryFieldViewModel group in product.FieldDisplayGroups.Values)
30 {
31 if (selection == group.Id)
32 {
33 int fieldsWithNoValueOrZero = 0;
34
35 foreach (var field in group.Fields)
36 {
37 if (string.IsNullOrEmpty(field.Value.Value.ToString()))
38 {
39 fieldsWithNoValueOrZero++;
40 }
41 }
42
43 if (fieldsWithNoValueOrZero != group.Fields.Count)
44 {
45 displayGroups.Add(group);
46 }
47 }
48 }
49 }
50
51 bool showProductFields = Model.Item.GetBoolean("ProductFields");
52
53 bool hideTitle = Model.Item.GetBoolean("HideTitle");
54
55 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : "";
56
57 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "display-4");
58
59 string contentPadding = Model.Item.GetRawValueString("ContentPadding", "");
60 contentPadding = contentPadding == "none" ? string.Empty : contentPadding;
61 contentPadding = contentPadding == "small" ? " p-2 p-md-3" : contentPadding;
62 contentPadding = contentPadding == "large" ? " p-4 p-md-5" : contentPadding;
63
64 string layout = Model.Item.GetRawValueString("Layout", "list");
65 string size = Model.Item.GetRawValueString("Size", "full");
66 string gaps = size == "full" ? " gap-4" : " gap-2";
67
68
69 if (Pageview.IsVisualEditorMode && displayGroups.Count() == 0)
70 {
71 product.ProductFields.Clear();
72 product.ProductFields.Add(Translate("Width"), new FieldValueViewModel { Name = Translate("Width"), Value = "99cm" });
73 product.ProductFields.Add(Translate("Height"), new FieldValueViewModel { Name = Translate("Height"), Value = "195cm" });
74 showProductFields = true;
75 }
76
77 if (layout == "commas")
78 {
79 gaps = size == "full" ? " gap-4" : " gap-2";
80
81 }
82
83 <div class="h-100@(gaps)@(theme)@(contentPadding) item_@Model.Item.SystemName.ToLower()">
84 <div class="grid">
85 @if ((product.ProductFields != null && Model.Item.GetBoolean("ProductFields")) || (product.ProductCategories != null && Model.Item.GetBoolean("CategoryFields")) || (displayGroups.Count != 0)) {
86 if (!hideTitle)
87 {
88 <h2 class="g-col-12 @titleFontSize">@Model.Item.GetString("Title")</h2>
89 }
90 }
91
92 @if (displayGroups.Count != 0)
93 {
94 if (layout != "accordion")
95 {
96 foreach (var group in displayGroups)
97 {
98 bool hideHeader = Model.Item.GetBoolean("HideGroupHeaders");
99
100 if (!hideHeader) {
101 <h4 class="g-col-12 h4 mb-0">@group.Name</h4>
102 }
103
104 { @RenderFieldsFromList(group.Fields, layout) }
105
106 }
107 }
108 else
109 {
110 <div class="g-col-12">
111 <div class="accordion accordion-flush w-100" id="Specifications_@Model.ID">
112 @foreach (var group in displayGroups)
113 {
114 <div class="accordion-item">
115 <h2 class="accordion-header" id="SpecificationHeading_@group.Id">
116 <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">
117 @group.Name
118 </button>
119 </h2>
120 <div id="SpecificationItem_@group.Id" class="accordion-collapse collapse" aria-labelledby="SpecificationHeading_@group.Id" data-bs-parent="#Specifications_@Model.ID">
121 <div class="accordion-body">
122 @{ @RenderFieldsFromList(group.Fields, "list") }
123 </div>
124 </div>
125 </div>
126 }
127 </div>
128 </div>
129 }
130 }
131
132 @if (product.ProductFields != null && showProductFields)
133 {
134 if (product.ProductFields.Count > 0)
135 {
136 if (layout != "accordion")
137 {
138 {@RenderFieldsFromList(product.ProductFields, layout) }
139 }
140 else
141 {
142 <div class="g-col-12">
143 <div class="accordion accordion-flush w-100" id="Specifications_@Model.ID">
144 <div class="accordion-item">
145 <h2 class="accordion-header" id="SpecificationHeading_@Model.ID">
146 <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">
147 @Translate("Specifications")
148 </button>
149 </h2>
150 <div id="SpecificationItem_@Model.ID" class="accordion-collapse" aria-labelledby="SpecificationHeading_@Model.ID" data-bs-parent="#Specifications_@Model.ID">
151 <div class="accordion-body">
152 @{ @RenderFieldsFromList(product.ProductFields, "List") }
153 </div>
154 </div>
155 </div>
156 </div>
157 </div>
158 }
159 }
160 }
161
162 @if (product.ProductCategories != null && Model.Item.GetBoolean("CategoryFields"))
163 {
164 if (product.ProductCategories.Count > 0)
165 {
166 if (layout != "accordion")
167 {
168 foreach (var group in product.ProductCategories)
169 {
170 CategoryFieldViewModel category = group.Value;
171 bool hideHeader = Model.Item.GetBoolean("HideGroupHeaders");
172
173 if (!hideHeader) {
174 <h4 class="g-col-12 h4 mb-0">@group.Value.Name</h4>
175 }
176
177 { @RenderFieldsFromList(category.Fields, layout) }
178 }
179 }
180 else
181 {
182 <div class="g-col-12">
183 <div class="accordion accordion-flush w-100" id="Specifications_@Model.ID">
184 @foreach (var group in product.ProductCategories)
185 {
186 CategoryFieldViewModel category = group.Value;
187
188 <div class="accordion-item">
189 <h2 class="accordion-header" id="SpecificationHeading_@group.Value.Id">
190 <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">
191 @group.Value.Name
192 </button>
193 </h2>
194 <div id="SpecificationItem_@group.Value.Id" class="accordion-collapse" aria-labelledby="SpecificationHeading_@group.Value.Id" data-bs-parent="#Specifications_@Model.ID">
195 <div class="accordion-body">
196 @{ @RenderFieldsFromList(category.Fields, "list") }
197 </div>
198 </div>
199 </div>
200 }
201 </div>
202 </div>
203 }
204 }
205 }
206 </div>
207 </div>
208 }
209 else if (Pageview.IsVisualEditorMode)
210 {
211 <div class="alert alert-warning m-0">@Translate("No products available")</div>
212 }
213
214 @helper RenderFieldsFromList(Dictionary<string, FieldValueViewModel> fields, string layout)
215 {
216 string size = Model.Item.GetRawValueString("Size", "full");
217 string gaps = size != "full" ? " gap-1" : string.Empty;
218 bool hideFieldLabels = Model.Item.GetBoolean("HideFieldLabels");
219 bool hideFieldsWithZeroValue = Model.Item.GetBoolean("HideFieldsWithZeroValue");
220
221 if (layout == "columns"){
222 <div class="g-col-12">
223 <div class="grid@(gaps)">
224 @foreach (var field in fields)
225 {
226 {@RenderField(field.Value, layout)}
227 }
228 </div>
229 </div>
230 }
231 if (layout == "list") {
232 <div class="g-col-12">
233 <dl class="grid@(gaps)">
234 @foreach (var field in fields)
235 {
236 {@RenderField(field.Value, layout)}
237 }
238 </dl>
239 </div>
240 }
241 if (layout == "table")
242 {
243 string tableSize = size == "full" ? "" : " table-sm";
244 <div class="g-col-12">
245 <table class="table table-striped@(tableSize)">
246 @foreach (var field in fields)
247 {
248 {@RenderField(field.Value, layout)}
249 }
250 </table>
251 </div>
252 }
253 if (layout == "bullets")
254 {
255 string listSize = size == "full" ? "" : "m-0 p-0 lh-1 fs-7 opacity-75";
256 string listStyle = size == "full" ? "" : "style=\"list-style-position: inside\"";
257 <div class="g-col-12">
258 <ul class="@listSize" @listStyle>
259 @foreach (var field in fields)
260 {
261 {@RenderField(field.Value, layout)}
262 }
263 </ul>
264 </div>
265 }
266 if (layout == "commas")
267 {
268 List<string> featuresList = new List<string>();
269
270 foreach (var field in fields)
271 {
272 if (field.Value.SystemName == "MaxCanBeBuilt")
273 {
274 if (field.Value.Value is int)
275 {
276 field.Value.Value = (int)field.Value.Value > 0 ? Translate("Yes") : Translate("No");
277 }
278 }
279 string firstListItemValue = string.Empty; //Hack to support field type providers with a single value
280
281 if (field.Value?.Value != null)
282 {
283 if (field.Value.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>))
284 {
285 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value.Value as System.Collections.Generic.List<FieldOptionValueViewModel>;
286
287 //Hack to support field type providers with a single value
288 if (values.FirstOrDefault() != null)
289 {
290 firstListItemValue = values.FirstOrDefault().Value;
291 }
292 }
293 }
294
295 if (!hideFieldsWithZeroValue || (firstListItemValue != "0" && firstListItemValue != "0.0" && field.Value.Value.ToString() != "0" && field.Value.Value.ToString() != "0.0"))
296 {
297 if (field.Value.Value is object && !string.IsNullOrEmpty(field.Value.Value.ToString()))
298 {
299 if (field.Value.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>))
300 {
301 List<string> options = new List<string>();
302 foreach (FieldOptionValueViewModel option in field.Value.Value as System.Collections.Generic.List<FieldOptionValueViewModel>)
303 {
304 if (!string.IsNullOrWhiteSpace(option.Value))
305 {
306 if (option.Value.ToString().Contains("#") && (Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour")))
307 {
308 string colorSpan = "<span class=\"colorbox-sm\" style=\"background-color: " + option.Value + "\"></span>";
309 options.Add(colorSpan);
310 }
311 else if (!string.IsNullOrEmpty(option.Value))
312 {
313 options.Add(option.Name);
314 }
315 }
316 }
317 string optionsString = (string.Join(", ", options.Select(x => x.ToString()).ToArray()));
318 if ((Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour")))
319 {
320 optionsString = (string.Join(" ", options.Select(x => x.ToString()).ToArray()));
321 }
322
323 if (!string.IsNullOrEmpty(optionsString))
324 {
325 if (!hideFieldLabels)
326 {
327 featuresList.Add(field.Value.Name + ": " + optionsString);
328 }
329 else
330 {
331 featuresList.Add(optionsString);
332 }
333 }
334 }
335 else
336 {
337 if (!string.IsNullOrWhiteSpace(field.Value.Value.ToString()))
338 {
339 if (field.Value.Value.ToString().Contains("#") && (Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour")))
340 {
341 string colorSpan = "<span class=\"colorbox-sm\" style=\"background-color: " + field.Value.Value + "\"></span>";
342
343 if (!hideFieldLabels)
344 {
345 featuresList.Add(field.Value.Name + ": " + colorSpan);
346 }
347 else
348 {
349 featuresList.Add(colorSpan);
350 }
351 }
352 else
353 {
354 if (!hideFieldLabels)
355 {
356 featuresList.Add(field.Value.Name + ": " + field.Value.Value.ToString());
357 }
358 else
359 {
360 featuresList.Add(field.Value.Value.ToString());
361 }
362 }
363 }
364 }
365 }
366 }
367 }
368
369 string featuresString = (string.Join(", ", featuresList.Select(x => x.ToString()).ToArray()));
370
371 <div class="g-col-12 opacity-75 fs-7">@featuresString</div>
372 }
373 }
374
375 @helper RenderField(FieldValueViewModel field, string layout)
376 {
377
378 string size = Model.Item.GetRawValueString("Size", "full");
379 string fieldValue = field?.Value != null ? field.Value.ToString() : "";
380 bool hideFieldLabels = Model.Item.GetBoolean("HideFieldLabels");
381 bool noValues = false;
382 string firstListItemValue = string.Empty; //Hack to support field type providers with a single value
383 bool hideFieldsWithZeroValue = Model.Item.GetBoolean("HideFieldsWithZeroValue");
384
385 if (!string.IsNullOrEmpty(fieldValue))
386 {
387 if (field.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>))
388 {
389 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>;
390 noValues = values.Count > 0 ? false : true;
391
392 //Hack to support field type providers with a single value
393 if (values.FirstOrDefault() != null)
394 {
395 firstListItemValue = values.FirstOrDefault().Value;
396 }
397 }
398 }
399
400 if (!string.IsNullOrEmpty(fieldValue) && noValues == false)
401 {
402 if (!hideFieldsWithZeroValue || (firstListItemValue != "0" && firstListItemValue != "0.0" && field.Value.ToString() != "0" && field.Value.ToString() != "0.0"))
403 {
404 if (layout == "columns")
405 {
406
407 <div class="grid g-col-6 g-col-lg-4 gap-1">
408 @if (!hideFieldLabels)
409 {
410 <dt class="g-col-12 g-col-lg-4">@field.Name</dt>
411 }
412 <dd class="g-col-12 g-col-lg-8 mb-0 text-break">
413 @{ @RenderFieldValue(field) }
414 </dd>
415 </div>
416 }
417 if (layout == "list")
418 {
419 if (!hideFieldLabels)
420 {
421 <dt class="g-col-4">@field.Name</dt>
422 }
423 <dd class="g-col-8 mb-0 text-break">
424 @{ @RenderFieldValue(field) }
425 </dd>
426 }
427 if (layout == "table")
428 {
429 <tr>
430 @if (!hideFieldLabels)
431 {
432 <th class="w-25 w-lg-50" scope="row">@field.Name</th>
433 }
434 <td class="text-break">
435 @{ @RenderFieldValue(field) }
436 </td>
437 </tr>
438 }
439 if (layout == "bullets")
440 {
441 <li>
442 @if (!hideFieldLabels)
443 {
444 <strong>@field.Name</strong>
445 }
446 <span>
447 @{ @RenderFieldValue(field) }
448 </span>
449 </li>
450 }
451 }
452 }
453 }
454
455 @helper RenderFieldValue(FieldValueViewModel field)
456 {
457 string fieldValue = field?.Value != null ? field.Value.ToString() : "";
458
459 bool isLink = field?.Type == "Link";
460 bool isColor = false;
461 bool isBrandName = field?.SystemName == "Brand_name";
462
463 fieldValue = fieldValue == "False" ? Translate("No") : fieldValue;
464 fieldValue = fieldValue == "True" ? Translate("Yes") : fieldValue;
465
466
467 if (field.SystemName == "MaxCanBeBuilt")
468 {
469 fieldValue = (int)field.Value > 0 ? Translate("Yes") : Translate("No");
470 }
471
472 if (field.Value.GetType() == typeof(System.Collections.Generic.List<Dynamicweb.Ecommerce.ProductCatalog.FieldOptionValueViewModel>))
473 {
474 int valueCount = 0;
475 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>;
476 int totalValues = values.Count;
477
478 foreach (FieldOptionValueViewModel option in values)
479 {
480 if (!string.IsNullOrEmpty(option.Value))
481 {
482 if (option.Value.Substring(0, 1) == "#")
483 {
484 isColor = true;
485 }
486 }
487
488 if (!isColor)
489 {
490 @option.Name
491 }
492 else
493 {
494 <span class="colorbox-sm" style="background-color: @option.Value" title="@option.Name"></span>
495 }
496
497 if (valueCount != totalValues && valueCount < (totalValues - 1))
498 {
499 if (isColor)
500 {
501 <text> </text>
502 }
503 else
504 {
505 <text>, </text>
506 }
507 }
508 valueCount++;
509 }
510 }
511 else
512 {
513 if (fieldValue.Substring(0, 1) == "#")
514 {
515 isColor = true;
516 }
517
518 if (!isColor)
519 {
520 if (isLink)
521 {
522 string linktTitle = !fieldValue.Contains("aspx") ? fieldValue : Translate("Go to link");
523 string target = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") && fieldValue.Contains("http") ? "target=\"_blank\"" : string.Empty;
524 string rel = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") && fieldValue.Contains("http") ? "rel=\"noopener\"" : string.Empty;
525
526 <a href="@field.Value" title="@field.Name" @target @rel>@linktTitle</a>
527 }
528 else if (isBrandName)
529 {
530 <span itemprop="brand" itemtype="https://schema.org/Brand" itemscope>
531 <span itemprop="name">@fieldValue</span>
532 </span>
533 }
534 else
535 {
536 @fieldValue
537 }
538
539 }
540 else
541 {
542 <span class="colorbox-sm" style="background-color: @fieldValue" title="@fieldValue"></span>
543 }
544 }
545 }
546