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