Error executing template "Designs/Swift/Swift_Page.cshtml"
System.IndexOutOfRangeException: Height
at System.Data.SqlClient.SqlDataReader.GetOrdinal(String name)
at Dynamicweb.Ecommerce.Products.ProductFieldValueCollection..ctor(String languageId, IDataReader dataReader, Boolean checkIfColumnExists)
at Dynamicweb.Ecommerce.Products.ProductRepository.GetProductById(String productId, String productVariantId, String productLanguageId)
at Dynamicweb.Ecommerce.Products.ProductService.FetchMissingProductsInternal(IProductRepository repo, IEnumerable`1 keys)
at Dynamicweb.Caching.ServiceCache`2.GetCache(IEnumerable`1 keys)
at Dynamicweb.Ecommerce.Products.ProductService.GetProductById(String productId, String productVariantId, String productLanguageId, User user, Boolean showUntranslated)
at Dynamicweb.Ecommerce.Products.ProductService.GetProductById(String productId, String productVariantId, String productLanguageId)
at CompiledRazorTemplates.Dynamic.RazorEngine_b007da28acc84a24adacee58d69b812c.ExecuteAsync()
at RazorEngine.Templating.TemplateBase.Run(ExecuteContext context, TextWriter reader)
at RazorEngine.Templating.RazorEngineCore.RunTemplate(ICompiledTemplate template, TextWriter writer, Object model, DynamicViewBag viewBag)
at RazorEngine.Templating.RazorEngineService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
at RazorEngine.Templating.DynamicWrapperService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass23_0.<Run>b__0(TextWriter writer)
at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
at RazorEngine.Templating.RazorEngineServiceExtensions.Run(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.ViewModelTemplate<Dynamicweb.Frontend.PageViewModel>
2 @using System
3 @using Dynamicweb
4 @using Dynamicweb.Environment
5 @using Dynamicweb.Frontend
6
7 @functions {
8 string GetCookieOptInPermission(string category)
9 {
10 bool categoryOrAllGranted = false;
11
12 if (CookieManager.IsCookieManagementActive)
13 {
14 var cookieOptInLevel = CookieManager.GetCookieOptInLevel();
15 var cookieOptInCategories = CookieManager.GetCookieOptInCategories();
16 categoryOrAllGranted = cookieOptInCategories.Contains(category) || cookieOptInLevel == CookieOptInLevel.All;
17 }
18
19 return categoryOrAllGranted ? "granted" : "denied";
20 }
21
22 bool AllowTracking()
23 {
24 bool allowTracking = true;
25 if (CookieManager.IsCookieManagementActive)
26 {
27 var cookieOptInLevel = CookieManager.GetCookieOptInLevel();
28 var cookieOptInCategories = CookieManager.GetCookieOptInCategories();
29
30 bool consentEither = (cookieOptInCategories.Contains("Statistical") || cookieOptInCategories.Contains("Marketing"));
31 bool consentFunctional = cookieOptInLevel == CookieOptInLevel.Functional;
32 bool consentAtLeastOne = cookieOptInLevel == CookieOptInLevel.All || (consentFunctional && consentEither);
33
34 allowTracking = consentAtLeastOne;
35 }
36 return allowTracking;
37 }
38 }
39
40 @{
41 var cartSummaryPageId = Dynamicweb.Content.Services.Pages.GetPageByNavigationTag(Model.Area.ID, "CartSummary")?.ID;
42 bool enableMiniCart = Model.Area.Item?.GetBoolean("EnableOffcanvasMiniCart") ?? false;
43 var offcanvasMiniCartBehaviour = Model.Area.Item?.GetRawValueString("OffcanvasMinicartBehaviour", "3") ?? "3";
44 bool miniCartEnabled = cartSummaryPageId != null && enableMiniCart;
45 var brandingPageId = Model.Area.Item?.GetInt32("BrandingPage") ?? 0;
46 var themePageId = Model.Area.Item?.GetInt32("ThemesPage") ?? 0;
47 var cssPageId = Model.Area.Item?.GetInt32("CssPage") ?? 0;
48 var brandingPage = brandingPageId != 0 ? Dynamicweb.Content.Services.Pages?.GetPage(brandingPageId) ?? null : null;
49 var themesParagraphs = themePageId != 0 ? Dynamicweb.Content.Services.Paragraphs?.GetParagraphsByPageId(themePageId) ?? null : null;
50 var cssParagraphs = cssPageId != 0 ? Dynamicweb.Content.Services.Paragraphs?.GetParagraphsByPageId(cssPageId) ?? null : null;
51 }
52
53 @if (themesParagraphs != null || brandingPage != null)
54 {
55 string swiftVersion = ReadFile("/Files/Templates/Designs/Swift/swift_version.txt");
56 bool renderAsResponsive = Model.Area.Item.GetString("DeviceRendering", "responsive").Equals("responsive", StringComparison.OrdinalIgnoreCase);
57 bool renderMobile = Pageview.Device == Dynamicweb.Frontend.Devices.DeviceType.Mobile || Pageview.Device == Dynamicweb.Frontend.Devices.DeviceType.Tablet;
58 string responsiveClassDesktop = string.Empty;
59 string responsiveClassMobile = string.Empty;
60 if (renderAsResponsive)
61 {
62 responsiveClassDesktop = " d-none d-xl-block";
63 responsiveClassMobile = " d-block d-xl-none";
64 }
65
66 var headerDesktopLink = Model.Area.Item?.GetLink("HeaderDesktop") ?? null;
67 var headerMobileLink = Model.Area.Item?.GetLink("HeaderMobile") ?? null;
68
69 var footerDesktopLink = Model.Area.Item?.GetLink("FooterDesktop") ?? null;
70 var footerMobileLink = Model.Area.Item?.GetLink("FooterMobile") ?? null;
71
72 var disableWideBreakpoints = Model.Area?.Item?.GetRawValueString("DisableWideBreakpoints", "default");
73
74 string customHeaderInclude = !string.IsNullOrEmpty(Model.Area.Item.GetRawValueString("CustomHeaderInclude")) ? Model.Area.Item.GetFile("CustomHeaderInclude").Name : string.Empty;
75
76 var themesParagraphLastChanged = Dynamicweb.Content.Services.Paragraphs.GetParagraphsByPageId(themePageId).OrderByDescending(p => p.Audit.LastModifiedAt).FirstOrDefault();
77 var cssLastModified = brandingPage.Audit.LastModifiedAt > themesParagraphLastChanged.Audit.LastModifiedAt ? brandingPage.Audit.LastModifiedAt : themesParagraphLastChanged.Audit.LastModifiedAt;
78
79 var cssThemeAndBrandingStyleFileInfo = new System.IO.FileInfo(Dynamicweb.Core.SystemInformation.MapPath($"/Files/Templates/Designs/Swift/_parsed/Swift_css/Swift_styles_{Model.Area.ID}.min.css"));
80
81
82 if (cssPageId != 0)
83 {
84 var cssFileInfo = new System.IO.FileInfo(Dynamicweb.Core.SystemInformation.MapPath($"/Files/Templates/Designs/Swift/_parsed/Swift_css/Swift_css_styles_{Model.Area.ID}.css"));
85 var cssParagraphLastChanged = Dynamicweb.Content.Services.Paragraphs.GetParagraphsByPageId(cssPageId).OrderByDescending(p => p.Audit.LastModifiedAt).FirstOrDefault();
86 if (!cssThemeAndBrandingStyleFileInfo.Exists || cssThemeAndBrandingStyleFileInfo.LastWriteTime < cssParagraphLastChanged.Audit.LastModifiedAt)
87 {
88 var cssPageview = Dynamicweb.Frontend.PageView.GetPageviewByPageID(cssPageId);
89 cssPageview.Redirect = false;
90 cssPageview.Output();
91 }
92 }
93
94 if (!cssThemeAndBrandingStyleFileInfo.Exists || cssThemeAndBrandingStyleFileInfo.LastWriteTime < brandingPage.Audit.LastModifiedAt)
95 {
96 //Branding page has been saved or the file is missing. Rewrite the file to disc.
97 if (brandingPageId > 0)
98 {
99 var brandingPageview = Dynamicweb.Frontend.PageView.GetPageviewByPageID(brandingPageId);
100 brandingPageview.Redirect = false;
101 brandingPageview.Output();
102 }
103 }
104
105 if (!cssThemeAndBrandingStyleFileInfo.Exists || cssThemeAndBrandingStyleFileInfo.LastWriteTime < themesParagraphLastChanged.Audit.LastModifiedAt)
106 {
107 //Branding page has been saved or the file is missing. Rewrite the file to disc.
108 if (themePageId > 0)
109 {
110 var themePageview = Dynamicweb.Frontend.PageView.GetPageviewByPageID(themePageId);
111 themePageview.Redirect = false;
112 themePageview.Output();
113 }
114 }
115
116 // Schema.org details for PDP
117 bool isProductDetailsPage = Dynamicweb.Context.Current.Request.QueryString.AllKeys.Contains("ProductID");
118 bool isArticlePage = Model.ItemType == "Swift_Article";
119 string schemaOrgType = string.Empty;
120
121 if (isProductDetailsPage)
122 {
123 schemaOrgType = "itemscope=\"\" itemtype=\"https://schema.org/Product\"";
124 }
125
126 if (isArticlePage)
127 {
128 schemaOrgType = "itemscope=\"\" itemtype=\"https://schema.org/Article\"";
129 }
130
131
132 var cssStyleFileInfo = new System.IO.FileInfo(Dynamicweb.Core.SystemInformation.MapPath("/Files/Templates/Designs/Swift/Assets/css/styles.css"));
133 var jsFileInfo = new System.IO.FileInfo(Dynamicweb.Core.SystemInformation.MapPath("/Files/Templates/Designs/Swift/Assets/js/scripts.js"));
134
135 string masterTheme = !string.IsNullOrWhiteSpace(Model.Area.Item.GetRawValueString("Theme")) ? " theme " + Model.Area.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : "";
136
137 string favicon = Model.Area.Item.GetRawValueString("Favicon", "/Files/Templates/Designs/Swift/Assets/Images/favicon.png");
138 string appleTouchIcon = Model.Area.Item.GetRawValueString("AppleTouchIcon", "/Files/Templates/Designs/Swift/Assets/Images/apple-touch-icon.png");
139
140 string headerCssClass = "sticky-top";
141 bool movePageBehind = false;
142
143 if (Model.PropertyItem != null)
144 {
145 headerCssClass = Model.PropertyItem.GetRawValueString("MoveThisPageBehindTheHeader", "sticky-top");
146 movePageBehind = headerCssClass == "fixed-top" && !Pageview.IsVisualEditorMode ? true : false;
147 }
148
149 headerCssClass = headerCssClass == "" ? "sticky-top" : headerCssClass;
150 headerCssClass = Pageview.IsVisualEditorMode ? "" : headerCssClass;
151
152 string googleTagManagerID = Model.Area.Item.GetString("GoogleTagManagerID").Trim();
153 string googleAnalyticsMeasurementID = Model.Area.Item.GetString("GoogleAnalyticsMeasurementID").Trim();
154
155 bool allowTracking = AllowTracking();
156
157 Dynamicweb.Context.Current.Response.AddHeader("link", $"</Files/Templates/Designs/Swift/Assets/css/styles.css?{cssStyleFileInfo.LastWriteTime.Ticks}>; rel=preload; as=style;");
158 Dynamicweb.Context.Current.Response.AddHeader("link", $"</Files/Templates/Designs/Swift/_parsed/Swift_css/Swift_styles_{Model.Area.ID}.min.css?{cssLastModified.Ticks}>; rel=preload; as=style;");
159 Dynamicweb.Context.Current.Response.AddHeader("link", $"</Files/Templates/Designs/Swift/Assets/js/scripts.js?{jsFileInfo.LastWriteTime.Ticks}>; rel=preload; as=script;");
160
161
162 SetMetaTags();
163
164 List<Dynamicweb.Content.Page> languages = new List<Dynamicweb.Content.Page>();
165
166 var masterPage = Pageview.Area.IsMaster ? Pageview.Page : Pageview.Page.MasterPage;
167 languages.Add(masterPage);
168 if (masterPage?.Languages != null)
169 {
170 foreach (var language in masterPage.Languages)
171 {
172 languages.Add(language);
173 }
174 }
175
176 Uri url = Dynamicweb.Context.Current.Request.Url;
177 string hostName = url.Host;
178
179 <!doctype html>
180 <html lang="@Pageview.Area.CultureInfo.TwoLetterISOLanguageName">
181 <head>
182 <!-- @swiftVersion -->
183 @* Required meta tags *@
184 <meta charset="utf-8">
185 <meta name="viewport" content="height=device-height, width=device-width, initial-scale=1.0">
186 <link rel="shortcut icon" href="@favicon">
187 <link rel="apple-touch-icon" href="@appleTouchIcon">
188 <meta name="google-site-verification" content="7TZ70uf34T_EuoBn-IZgrJv1WvzvMr-wM2SxRB93R7c" />
189
190 @Model.MetaTags
191
192 @{
193 var alreadyWrittenTwoletterIsos = new List<string>();
194 @* Languages meta data *@
195 foreach (var language in languages)
196 {
197 hostName = url.Host;
198 if (language?.Area != null)
199 {
200 if (language.Area?.MasterArea != null && !string.IsNullOrEmpty(language.Area.MasterArea.DomainLock))
201 {
202 hostName = language.Area.MasterArea.DomainLock; //dk.domain.com or dk-domain.dk
203 }
204 if (language != null && language.Area != null && language.Published && language.Area.Active && language.Area.Published)
205 {
206 if (!string.IsNullOrEmpty(language.Area.DomainLock))
207 {
208 hostName = language.Area.DomainLock; //dk.domain.com or dk-domain.dk
209 }
210 string querystring = $"Default.aspx?ID={language.ID}";
211 if (!string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["GroupID"]))
212 {
213 querystring += $"&GroupID={Dynamicweb.Context.Current.Request.QueryString["GroupID"]}";
214 }
215 if (!string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["ProductID"]))
216 {
217 querystring += $"&ProductID={Dynamicweb.Context.Current.Request.QueryString["ProductID"]}";
218 }
219 if (!string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["VariantID"]))
220 {
221 querystring += $"&VariantID={Dynamicweb.Context.Current.Request.QueryString["VariantID"]}";
222 }
223
224 string friendlyUrl = Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl(querystring);
225 if (language.Area.RedirectFirstPage && language.ParentPageId == 0 && language.Sort == 1)
226 {
227 friendlyUrl = "/";
228 }
229 string href = $"{url.Scheme}://{hostName}{friendlyUrl}";
230
231
232 <link rel="alternate" hreflang="@language.Area.CultureInfo.Name.ToLower()" href="@href">
233 if (!alreadyWrittenTwoletterIsos.Contains(language.Area.CultureInfo.TwoLetterISOLanguageName))
234 {
235 alreadyWrittenTwoletterIsos.Add(language.Area.CultureInfo.TwoLetterISOLanguageName);
236 <link rel="alternate" hreflang="@language.Area.CultureInfo.TwoLetterISOLanguageName.ToLower()" href="@href">
237 }
238 }
239 }
240 }
241 }
242
243 <title>@Model.Title</title>
244 @* Bootstrap + Swift stylesheet *@
245 <link href="/Files/Templates/Designs/Swift/Assets/css/styles.css?@cssStyleFileInfo.LastWriteTime.Ticks" rel="stylesheet" media="all" type="text/css">
246
247 @if (disableWideBreakpoints != "disableBoth")
248 {
249 <style>
250 @@media ( min-width: 1600px ) {
251 .container-xxl,
252 .container-xl,
253 .container-lg,
254 .container-md,
255 .container-sm,
256 .container {
257 max-width: 1520px;
258 }
259 }
260 </style>
261
262
263
264 if (disableWideBreakpoints != "disableUltraWideOnly")
265 {
266 <style>
267 @@media ( min-width: 1920px ) {
268 .container-xxl,
269 .container-xl,
270 .container-lg,
271 .container-md,
272 .container-sm,
273 .container {
274 max-width: 1820px;
275 }
276 }
277 </style>
278 }
279 }
280
281 @* Branding and Themes min stylesheet *@
282 <link href="/Files/Templates/Designs/Swift/_parsed/Swift_css/Swift_styles_@(Model.Area.ID).min.css?@cssLastModified.Ticks" rel="stylesheet" media="all" type="text/css" data-last-modified-content="@cssLastModified">
283 <script src="/Files/Templates/Designs/Swift/Assets/js/scripts.js?@jsFileInfo.LastWriteTime.Ticks"></script>
284 <script type="module">
285 swift.Scroll.hideHeadersOnScroll();
286 swift.Scroll.handleAlternativeTheme();
287
288 //Only load if AOS
289 const aosColumns = document.querySelectorAll('[data-aos]');
290 if (aosColumns.length > 0) {
291 swift.AssetLoader.Load('/Files/Templates/Designs/Swift/Assets/js/aos.js?@jsFileInfo.LastWriteTime.Ticks', 'js');
292 document.addEventListener('load.swift.assetloader', function () {
293 AOS.init({ duration: 400, delay: 100, easing: 'ease-in-out', mirror: false, disable: window.matchMedia('(prefers-reduced-motion: reduce)') });
294 });
295 }
296 </script>
297
298 @* Google gtag method - always include even if it is not used for anything *@
299 <script>
300 window.dataLayer = window.dataLayer || [];
301 function gtag() { dataLayer.push(arguments); }
302 </script>
303 @* Google tag manager *@
304 @if (!string.IsNullOrWhiteSpace(googleTagManagerID))
305 {
306 <script>
307 gtag('consent', 'default', {
308 'ad_storage': 'denied',
309 'ad_user_data': 'denied',
310 'ad_personalization': 'denied',
311 'analytics_storage': 'denied'
312 });
313 </script>
314 <script>
315 (function (w, d, s, l, i) {
316 w[l] = w[l] || []; w[l].push({
317 'gtm.start':
318 new Date().getTime(), event: 'gtm.js'
319 }); var f = d.getElementsByTagName(s)[0],
320 j = d.createElement(s), dl = l != 'dataLayer' ? '&l=' + l : ''; j.async = true; j.src =
321 'https://www.googletagmanager.com/gtm.js?id=' + i + dl; f.parentNode.insertBefore(j, f);
322 })(window, document, 'script', 'dataLayer', '@(googleTagManagerID)');
323 </script>
324 if (allowTracking)
325 {
326 string adConsent = GetCookieOptInPermission("Marketing");
327 string analyticsConsent = GetCookieOptInPermission("Statistical");
328 <script>
329 gtag('consent', 'update', {
330 'ad_storage': '@adConsent',
331 'ad_user_data': '@adConsent',
332 'ad_personalization': '@adConsent',
333 'analytics_storage': '@analyticsConsent'
334 });
335 </script>
336 }
337 }
338
339 @if (!string.IsNullOrWhiteSpace(googleAnalyticsMeasurementID) && allowTracking)
340 {
341 var GoogleAnalyticsDebugMode = "";
342
343 if (Model.Area.Item.GetBoolean("EnableGoogleAnalyticsDebugMode"))
344 {
345 GoogleAnalyticsDebugMode = ", {'debug_mode': true}";
346 }
347
348 <script async src="https://www.googletagmanager.com/gtag/js?id=@googleAnalyticsMeasurementID"></script>
349 <script>
350 gtag('js', new Date());
351 gtag('config', '@googleAnalyticsMeasurementID'@GoogleAnalyticsDebugMode);
352 </script>
353 }
354
355 @if (!string.IsNullOrWhiteSpace(customHeaderInclude))
356 {
357 @RenderPartial($"Components/Custom/{customHeaderInclude}")
358 }
359 </head>
360 <body class="brand @(masterTheme)" id="page@(Model.ID)">
361
362 @* Google tag manager *@
363 @if (!string.IsNullOrWhiteSpace(googleTagManagerID) && allowTracking)
364 {
365 <noscript>
366 <iframe src="https://www.googletagmanager.com/ns.html?id=@(googleTagManagerID)"
367 height="0" width="0" style="display:none;visibility:hidden"></iframe>
368 </noscript>
369 }
370
371 @if (renderAsResponsive || !renderMobile)
372 {
373 <header class="page-header @headerCssClass top-0@(responsiveClassDesktop)" id="page-header-desktop">
374 @if (headerDesktopLink != null)
375 {
376 @RenderGrid(headerDesktopLink.PageId)
377 }
378 </header>
379 }
380
381 @if ((renderAsResponsive || renderMobile))
382 {
383 <header class="page-header @headerCssClass top-0@(responsiveClassMobile)" id="page-header-mobile">
384 @if (headerMobileLink != null)
385 {
386 @RenderGrid(headerMobileLink.PageId)
387 }
388 </header>
389 }
390
391 <div data-intersect></div>
392
393 <main id="content" @(schemaOrgType)>
394 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.PageViewModel>
395 @using System
396 @using Dynamicweb.Ecommerce.ProductCatalog
397
398
399 @{
400 string productIdFromUrl = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("ProductID")) ? Dynamicweb.Context.Current.Request.QueryString.Get("ProductID") : string.Empty;
401 bool isProductDetail = !string.IsNullOrEmpty(productIdFromUrl) && Pageview.Page.NavigationTag.ToLower() == "shop";
402
403 bool isArticlePagePage = Model.ItemType == "Swift_Article";
404 bool isArticleListPage = Model.ItemType == "Swift_ArticleListPage";
405 string schemaOrgProp = string.Empty;
406 if(isArticlePagePage)
407 {
408 schemaOrgProp = "itemprop=\"articleBody\"";
409 }
410
411 string theme = "";
412 string gridContent = "";
413
414 if (Model.PropertyItem != null)
415 {
416 theme = !string.IsNullOrWhiteSpace(Model.PropertyItem.GetRawValueString("Theme")) ? "theme " + Model.PropertyItem.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : "";
417 }
418
419 if (Model.Item != null || Pageview.IsVisualEditorMode)
420 {
421 if (!isProductDetail)
422 {
423 gridContent = Model.Grid("Grid", "Grid", "default:true;sort:1", "Page");
424 }
425 else
426 {
427 var productObject = Dynamicweb.Ecommerce.Services.Products.GetProductById(productIdFromUrl, "", Pageview.Area.EcomLanguageId);
428 var detailPage = Dynamicweb.Ecommerce.Services.ProductGroups.GetGroup(productObject.PrimaryGroupId)?.Meta.PrimaryPage ?? string.Empty;
429 var detailPageId = detailPage != string.Empty ? Convert.ToInt16(detailPage.Substring(detailPage.LastIndexOf('=') + 1)) : GetPageIdByNavigationTag("ProductDetailPage");
430
431 @RenderGrid(detailPageId)
432 }
433 }
434
435 bool doNotRenderPage = false;
436
437 //Check if we are on the poduct detail page, and if there is data to render
438 ProductViewModel product = new ProductViewModel();
439 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails"))
440 {
441 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"];
442 if (string.IsNullOrEmpty(product.Id)) {
443 doNotRenderPage = true;
444 }
445 }
446
447 //Render the page
448 if (!doNotRenderPage) {
449 string itemIdentifier = Model?.Item?.SystemName != null ? "item_" + Model.Item.SystemName.ToLower() : "item_Swift_Page";
450
451 if (Pageview.IsVisualEditorMode) {
452 @Model.Placeholder("dwcontent", "content", "default:true;sort:1")
453 }
454
455 <div class="@theme @itemIdentifier" @schemaOrgProp>
456 @if (isArticleListPage)
457 {
458 var hx = $"hx-get=\"{Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl(Model.ID)}\" hx-select=\"#content\" hx-target=\"#content\" hx-swap=\"outerHTML\" hx-trigger=\"change\" hx-headers='{{\"feed\": \"true\"}}' hx-push-url=\"true\" hx-indicator=\"#ArticleFacetForm\"";
459
460 <form @hx id="ArticleFacetForm">
461 @gridContent
462 </form>
463 <script type="module" src="/Files/Templates/Designs/Swift/Assets/js/htmx.js"></script>
464 <script type="module">
465 document.addEventListener('htmx:confirm', (event) => {
466 let filters = event.detail.elt.querySelectorAll('select');
467 for (var i = 0; i < filters.length; i++) {
468 let input = filters[i];
469 if (input.name && !input.value) {
470 input.name = '';
471 }
472 }
473 });
474
475 document.addEventListener('htmx:beforeOnLoad', (event) => {
476 swift.Scroll.stopIntersectionObserver();
477 });
478
479 document.addEventListener('htmx:afterOnLoad', () => {
480 swift.Scroll.hideHeadersOnScroll();
481 swift.Scroll.handleAlternativeTheme();
482 });
483 </script>
484 }
485 else
486 {
487 @gridContent
488 }
489 </div>
490
491 } else {
492 <div class="container">
493 <div class="alert alert-info" role="alert">@Translate("Sorry. There is nothing to view here")</div>
494 </div>
495 }
496
497 if (!Model.IsCurrentUserAllowed)
498 {
499 int signInPage = GetPageIdByNavigationTag("SignInPage");
500 int dashboardPage = GetPageIdByNavigationTag("MyAccountDashboardPage");
501
502 if (!Pageview.IsVisualEditorMode)
503 {
504 if (signInPage != 0)
505 {
506 if (signInPage != Model.ID) {
507 Dynamicweb.Context.Current.Response.Redirect("/Default.aspx?ID=" + signInPage);
508 } else {
509 if (dashboardPage != 0) {
510 Dynamicweb.Context.Current.Response.Redirect("/Default.aspx?ID=" + dashboardPage);
511 } else {
512 Dynamicweb.Context.Current.Response.Redirect("/");
513 }
514 }
515 }
516 else
517 {
518 <div class="alert alert-dark m-0" role="alert">
519 <span>@Translate("You do not have access to this page")</span>
520 </div>
521 }
522 }
523 else
524 {
525 <div class="alert alert-dark m-0" role="alert">
526 <span>@Translate("To work on this page, you must be signed in, in the frontend")</span>
527 </div>
528 }
529 }
530 }
531
532 </main>
533
534 @if (renderAsResponsive || !renderMobile)
535 {
536 <footer class="page-footer@(responsiveClassDesktop)" id="page-footer-desktop">
537 @if (footerDesktopLink != null)
538 {
539 @RenderGrid(footerDesktopLink.PageId)
540 }
541 </footer>
542 }
543
544 @if (renderAsResponsive || renderMobile)
545 {
546 <footer class="page-footer@(responsiveClassMobile)" id="page-footer-mobile">
547 @if (footerMobileLink != null)
548 {
549 @RenderGrid(footerMobileLink.PageId)
550 }
551 </footer>
552 }
553
554 @* Render any offcanvas menu here *@
555 @RenderSnippet("offcanvas")
556
557 @{
558 bool isErpConnectionDown = !Dynamicweb.Core.Converter.ToBoolean(Context.Current.Items["IsWebServiceConnectionAvailable"]);
559 }
560
561 @* Language selector modal *@
562 <div class="modal fade" id="PreferencesModal" tabindex="-1" aria-hidden="true">
563 <div class="modal-dialog modal-dialog-centered modal-sm" id="PreferencesModalContent">
564 @* The content here comes from an external request *@
565 </div>
566 </div>
567
568 @* Favorite toast *@
569 <div aria-live="polite" aria-atomic="true">
570 <div class="position-fixed bottom-0 end-0 p-3" style="z-index: 11">
571 <div id="favoriteNotificationToast" class="toast" role="alert" aria-live="assertive" aria-atomic="true">
572 <div class="toast-header">
573 <strong class="me-auto">@Translate("Favorite list updated")</strong>
574 <button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
575 </div>
576 <div class="toast-body d-flex gap-3">
577 <div id="favoriteNotificationToast_Image"></div>
578 <div id="favoriteNotificationToast_Text"></div>
579 </div>
580 </div>
581 </div>
582 </div>
583
584 @* Modal for dynamic content *@
585 <div class="modal fade js-product" id="DynamicModal" tabindex="-1" aria-hidden="true">
586 <div class="modal-dialog modal-dialog-centered modal-md">
587 <div class="modal-content theme light" id="DynamicModalContent">
588 @* The content here comes from an external request *@
589 </div>
590 </div>
591 </div>
592
593 @* Offcanvas for dynamic content *@
594 <div class="offcanvas offcanvas-end theme light" tabindex="-1" id="DynamicOffcanvas">
595 @* The content here comes from an external request *@
596 </div>
597
598 @if (Model.Area.Item.GetBoolean("ShowErpDownMessage") && !Dynamicweb.Core.Converter.ToBoolean(Context.Current.Items["IsWebServiceConnectionAvailable"]))
599 {
600 string erpDownMessageTheme = !string.IsNullOrWhiteSpace(Model.Area.Item.GetRawValueString("ErpDownMessageTheme")) ? " theme " + Model.Area.Item.GetRawValueString("ErpDownMessageTheme").Replace(" ", "").Trim().ToLower() : "theme light";
601
602 <div class="position-fixed bottom-0 end-0 p-3" style="z-index: 1040">
603 <div class="toast fade show border-0 @erpDownMessageTheme" role="alert" aria-live="assertive" aria-atomic="true">
604 <div class="toast-header">
605 <strong class="me-auto">@Translate("Connection down")</strong>
606 <button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
607 </div>
608 <div class="toast-body">
609 @Translate("We are experiencing some connectivity issues. Not all features may be available to you.")
610 </div>
611 </div>
612 </div>
613 }
614
615 @if (miniCartEnabled)
616 {
617 @* Open MiniCart when the cart is updated *@
618 <script type="module">
619 document.addEventListener('updated.swift.cart', (event) => {
620 let orderContext = event?.detail?.formData?.get("OrderContext");
621 updateCartSummary(orderContext);
622
623 @if (offcanvasMiniCartBehaviour == "2" || offcanvasMiniCartBehaviour == "3") {
624 <text>openMiniCartOffcanvas();</text>
625 }
626 });
627 </script>
628
629 if (offcanvasMiniCartBehaviour == "1" || offcanvasMiniCartBehaviour == "3")
630 {
631 @* Open MiniCart when toggle is clicked *@
632 <script type="module">
633 let miniCartToggles = document.querySelectorAll('.mini-cart-quantity');
634 miniCartToggles?.forEach((toggle) => {
635 toggle.parentElement.addEventListener('click', (event) => {
636 event.preventDefault();
637 let orderContext = toggle.dataset?.orderContext;
638 updateCartSummary(orderContext);
639
640 openMiniCartOffcanvas();
641 });
642 });
643 </script>
644 }
645
646 <script>
647
648 const updateCartSummary = (orderContext) => {
649 const dynamicOffcanvas = document.getElementById('DynamicOffcanvas');
650 // SINCE Swift_CartSummary.cshtml IS HARDCODED HERE IN STANDARD SWIFT TEMPLATES AND BOTH THE B2B, PREORDER AND B2C SITES ARE USING Swift_CartSummary_Custom.cshtml I HAVE HARDCODED THAT INSTEAD TO BE USED:
651 swift.PageUpdater.UpdateFromUrlInline(event, '/Default.aspx?ID=@(cartSummaryPageId)&CartType=minicart&RequestPageID=@(Pageview.Page.ID)&OrderContext=' + orderContext + '', 'Swift_CartSummary_Custom.cshtml', dynamicOffcanvas);
652 // HERE IS THE STANDARD LINE FOR REFERENCE:
653 //swift.PageUpdater.UpdateFromUrlInline(event, '/Default.aspx?ID=@(cartSummaryPageId)&CartType=minicart&RequestPageID=@(Pageview.Page.ID)&OrderContext=' + orderContext +'', 'Swift_CartSummary.cshtml', dynamicOffcanvas);
654 };
655
656 const openMiniCartOffcanvas = () => {
657 const dynamicOffcanvas = document.getElementById('DynamicOffcanvas');
658 const miniCartOffcanvas = bootstrap.Offcanvas.getOrCreateInstance(dynamicOffcanvas);
659 dynamicOffcanvas.classList.add('overflow-y-auto');
660
661 if (!miniCartOffcanvas._isShown) {
662 miniCartOffcanvas.show();
663 hideActiveOffcanvases(miniCartOffcanvas);
664 }
665 };
666
667 const hideActiveOffcanvases = (miniCartOffcanvas) => {
668 let activeOffcanvases = document.querySelectorAll('.offcanvas.show');
669 activeOffcanvases?.forEach((offCanvas) => {
670 offCanvas = bootstrap.Offcanvas.getInstance(offCanvas);
671 if (offCanvas !== miniCartOffcanvas) {
672 offCanvas.hide();
673 }
674 });
675 };
676
677 </script>
678 }
679
680 </body>
681
682 </html>
683
684 }
685 else if (Pageview.IsVisualEditorMode)
686 {
687 <head>
688 <title>@Model.Title</title>
689 @* Bootstrap + Swift stylesheet *@
690 <link href="/Files/Templates/Designs/Swift/Assets/css/styles.css" rel="stylesheet" media="all" type="text/css">
691 </head>
692 <body class="p-3">
693 <div class="alert alert-danger" role="alert">
694 @Translate("Basic Swift setup is needed!")
695 </div>
696
697 @if (brandingPage == null)
698 {
699 <div class="alert alert-warning" role="alert">
700 @Translate("Please add a Branding page and reference it in website settings")
701 </div>
702 }
703
704 @if (themesParagraphs == null)
705 {
706 <div class="alert alert-warning" role="alert">
707 @Translate("Please add a Themes collection page and reference it in website settings")
708 </div>
709 }
710 </body>
711 }
712
713
714 @functions {
715 void SetMetaTags()
716 {
717 //Verification Tokens
718 string siteVerificationGoogle = Model.Area.Item.GetString("Google_Site_Verification") != null ? Model.Area.Item.GetString("Google_Site_Verification") : "";
719
720 //Generic Site Values
721 string openGraphFacebookAppID = Model.Area.Item.GetString("Fb_app_id") != null ? Model.Area.Item.GetString("Fb_app_id") : "";
722 string openGraphType = Model.Area.Item.GetString("Open_Graph_Type") != null ? Model.Area.Item.GetString("Open_Graph_Type") : "";
723 string openGraphSiteName = Model.Area.Item.GetString("Open_Graph_Site_Name") != null ? Model.Area.Item.GetString("Open_Graph_Site_Name") : "";
724
725 string twitterCardSite = Model.Area.Item.GetString("Twitter_Site") != null ? Model.Area.Item.GetString("Twitter_Site") : "";
726
727 //Page specific values
728 string openGraphSiteTitle = Model.Area.Item.GetString("Open_Graph_Title") != null ? Model.Area.Item.GetString("Open_Graph_Title") : "";
729 FileViewModel openGraphImage = Model.Area.Item.GetFile("Open_Graph_Image");
730 string openGraphImageALT = Model.Area.Item.GetString("Open_Graph_Image_ALT") != null ? Model.Area.Item.GetString("Open_Graph_Image_ALT") : "";
731 string openGraphDescription = Model.Area.Item.GetString("Open_Graph_Description") != null ? Model.Area.Item.GetString("Open_Graph_Description") : "";
732
733 string twitterCardURL = Model.Area.Item.GetString("Twitter_URL") != null ? Model.Area.Item.GetString("Twitter_URL") : "";
734 string twitterCardTitle = Model.Area.Item.GetString("Twitter_Title") != null ? Model.Area.Item.GetString("Twitter_Title") : "";
735 string twitterCardDescription = Model.Area.Item.GetString("Twitter_Description") != null ? Model.Area.Item.GetString("Twitter_Description") : "";
736 FileViewModel twitterCardImage = Model.Area.Item.GetFile("Twitter_Image");
737 string twitterCardImageALT = Model.Area.Item.GetString("Twitter_Image_ALT") != null ? Model.Area.Item.GetString("Twitter_Image_ALT") : "";
738 string topImage = Pageview.Page.TopImage.StartsWith("/Files", StringComparison.OrdinalIgnoreCase) ? Pageview.Page.TopImage : $"/Files{Pageview.Page.TopImage}";
739
740 if (string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["ProductID"]))
741 {
742 if (!string.IsNullOrEmpty(Model.Description))
743 {
744 Pageview.Meta.AddTag($"<meta property=\"og:description\" content=\"{Model.Description}\">");
745 }
746 else
747 {
748 Pageview.Meta.AddTag($"<meta property=\"og:description\" content=\"{openGraphDescription}\">");
749 }
750
751 if (!string.IsNullOrEmpty(Pageview.Page.TopImage))
752 {
753 Pageview.Meta.AddTag($"<meta property=\"og:image\" content=\"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{topImage}\">");
754 Pageview.Meta.AddTag($"<meta property=\"og:image:secure_url\" content=\"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{topImage}\">");
755 }
756 else if (openGraphImage != null)
757 {
758 Pageview.Meta.AddTag($"<meta property=\"og:image\" content=\"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{openGraphImage.Path}\">");
759 Pageview.Meta.AddTag($"<meta property=\"og:image:secure_url\" content=\"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{openGraphImage.Path}\">");
760 }
761
762 if (!string.IsNullOrEmpty(openGraphImageALT))
763 {
764 Pageview.Meta.AddTag($"<meta property=\"og:image:alt\" content=\"{openGraphImageALT}\">");
765 }
766 if (!string.IsNullOrEmpty(twitterCardDescription))
767 {
768 Pageview.Meta.AddTag("twitter:description", twitterCardDescription);
769 }
770
771 if (!string.IsNullOrEmpty(Pageview.Page.TopImage))
772 {
773 Pageview.Meta.AddTag("twitter:image", $"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{topImage}");
774 }
775 else if (twitterCardImage != null)
776 {
777 Pageview.Meta.AddTag("twitter:image", $"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{openGraphImage.Path}");
778 }
779
780 if (!string.IsNullOrEmpty(twitterCardImageALT))
781 {
782 Pageview.Meta.AddTag("twitter:image:alt", twitterCardImageALT);
783 }
784 }
785
786 if (!string.IsNullOrEmpty(siteVerificationGoogle))
787 {
788 Pageview.Meta.AddTag("google-site-verification", siteVerificationGoogle);
789 }
790
791 if (!string.IsNullOrEmpty(openGraphFacebookAppID))
792 {
793 Pageview.Meta.AddTag($"<meta property=\"fb:app_id\" content=\"{openGraphFacebookAppID}\">");
794 }
795
796 if (!string.IsNullOrEmpty(openGraphType))
797 {
798 Pageview.Meta.AddTag($"<meta property=\"og:type\" content=\"{openGraphType}\">");
799 }
800
801 if (!string.IsNullOrEmpty(openGraphSiteName))
802 {
803 Pageview.Meta.AddTag($"<meta property=\"og:url\" content=\"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{Pageview.SearchFriendlyUrl}\">");
804 }
805
806 if (!string.IsNullOrEmpty(openGraphSiteName))
807 {
808 Pageview.Meta.AddTag($"<meta property=\"og:site_name\" content=\"{openGraphSiteName}\">");
809 }
810
811 if (!string.IsNullOrEmpty(Model.Title))
812 {
813 Pageview.Meta.AddTag($"<meta property=\"og:title\" content=\"{Model.Title}\">");
814 }
815 else
816 {
817 Pageview.Meta.AddTag($"<meta property=\"og:title\" content=\"{openGraphSiteTitle}\">");
818 }
819
820 if (!string.IsNullOrEmpty(twitterCardSite))
821 {
822 Pageview.Meta.AddTag("twitter:site", twitterCardSite);
823 }
824
825 if (!string.IsNullOrEmpty(twitterCardURL))
826 {
827 Pageview.Meta.AddTag("twitter:url", twitterCardURL);
828 }
829
830 if (!string.IsNullOrEmpty(twitterCardTitle))
831 {
832 Pageview.Meta.AddTag("twitter:title", twitterCardTitle);
833 }
834 }
835 }
836