Error executing template "Designs/Swift/_parsed/Swift_Page.parsed.cshtml"
System.Data.SqlClient.SqlException (0x80131904): A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: Named Pipes Provider, error: 40 - Could not open a connection to SQL Server) ---> System.ComponentModel.Win32Exception (0x80004005): The specified network name is no longer available
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_fef883997c6b4b6aa97dae1ba704778e.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:00000000-0000-0000-0000-000000000000
Error Number:64,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