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