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