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