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