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 (64): The specified network name is no longer available.
at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)
at System.Data.ProviderBase.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)
at CompiledRazorTemplates.Dynamic.RazorEngine_e0e42a14a9b848719239fe1fa630c49c.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:64,State:0,Class:20
1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.PageViewModel>
2 @using System
3 @using Dynamicweb
4 @using Dynamicweb.Environment
5 @using Dynamicweb.Frontend
6
7 @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 // Has ContactID? - used with ActiveCampaign
132 var ContactId = Dynamicweb.Context.Current.Request.QueryString["ContactId"];
133 if (!string.IsNullOrWhiteSpace(ContactId))
134 {
135 Dynamicweb.Environment.CookieManager.SetCookie("ContactId", ContactId, DateTime.Now.AddYears(1));
136 }
137
138 var cssStyleFileInfo = new System.IO.FileInfo(Dynamicweb.Core.SystemInformation.MapPath("/Files/Templates/Designs/Swift/Assets/css/styles.css"));
139 var jsFileInfo = new System.IO.FileInfo(Dynamicweb.Core.SystemInformation.MapPath("/Files/Templates/Designs/Swift/Assets/js/scripts.js"));
140 var jsMainFileInfo = new System.IO.FileInfo(Dynamicweb.Core.SystemInformation.MapPath("/Files/Templates/Designs/Swift/Assets/js/main.js"));
141
142 string masterTheme = !string.IsNullOrWhiteSpace(Model.Area.Item.GetRawValueString("Theme")) ? " theme " + Model.Area.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : "";
143
144 string favicon = Model.Area.Item.GetRawValueString("Favicon", "/Files/Templates/Designs/Swift/Assets/Images/favicon.png");
145 string appleTouchIcon = Model.Area.Item.GetRawValueString("AppleTouchIcon", "/Files/Templates/Designs/Swift/Assets/Images/apple-touch-icon.png");
146
147 string headerCssClass = "sticky-top";
148 bool movePageBehind = false;
149
150 if (Model.PropertyItem != null)
151 {
152 headerCssClass = Model.PropertyItem.GetRawValueString("MoveThisPageBehindTheHeader", "sticky-top");
153 movePageBehind = headerCssClass == "fixed-top" && !Pageview.IsVisualEditorMode ? true : false;
154 }
155
156 headerCssClass = headerCssClass == "" ? "sticky-top" : headerCssClass;
157 headerCssClass = Pageview.IsVisualEditorMode ? "" : headerCssClass;
158
159 string googleTagManagerID = Model.Area.Item.GetString("GoogleTagManagerID").Trim();
160 string googleAnalyticsMeasurementID = Model.Area.Item.GetString("GoogleAnalyticsMeasurementID").Trim();
161
162 bool allowTracking = AllowTracking();
163
164 Dynamicweb.Context.Current.Response.AddHeader("link", $"</Files/Templates/Designs/Swift/Assets/css/styles.css?{cssStyleFileInfo.LastWriteTime.Ticks}>; rel=preload; as=style;");
165 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;");
166 Dynamicweb.Context.Current.Response.AddHeader("link", $"</Files/Templates/Designs/Swift/Assets/js/scripts.js?{jsFileInfo.LastWriteTime.Ticks}>; rel=preload; as=script;");
167 Dynamicweb.Context.Current.Response.AddHeader("link", $"</Files/Templates/Designs/Swift/Assets/js/main.js?{jsMainFileInfo.LastWriteTime.Ticks}>; rel=preload; as=script;");
168
169
170 SetMetaTags();
171
172 List<Dynamicweb.Content.Page> languages = new List<Dynamicweb.Content.Page>();
173
174 var masterPage = Pageview.Area.IsMaster ? Pageview.Page : Pageview.Page.MasterPage;
175 languages.Add(masterPage);
176 if (masterPage?.Languages != null)
177 {
178 foreach (var language in masterPage.Languages)
179 {
180 languages.Add(language);
181 }
182 }
183
184 Uri url = Dynamicweb.Context.Current.Request.Url;
185 string hostName = url.Host;
186
187 <!doctype html>
188 <html lang="@Pageview.Area.CultureInfo.TwoLetterISOLanguageName">
189 <head>
190 <!-- @swiftVersion -->
191 @* Required meta tags *@
192 <meta charset="utf-8">
193 <meta name="viewport" content="height=device-height, width=device-width, initial-scale=1.0">
194 <link rel="shortcut icon" href="@favicon">
195 <link rel="apple-touch-icon" href="@appleTouchIcon">
196
197 @Model.MetaTags
198
199 @{
200 var alreadyWrittenTwoletterIsos = new List<string>();
201 @* Languages meta data *@
202 foreach (var language in languages)
203 {
204 hostName = url.Host;
205 if (language?.Area != null)
206 {
207 if (language.Area?.MasterArea != null && !string.IsNullOrEmpty(language.Area.MasterArea.DomainLock))
208 {
209 hostName = language.Area.MasterArea.DomainLock; //dk.domain.com or dk-domain.dk
210 }
211 if (language != null && language.Area != null && language.Published && language.Area.Active && language.Area.Published)
212 {
213 if (!string.IsNullOrEmpty(language.Area.DomainLock))
214 {
215 hostName = language.Area.DomainLock; //dk.domain.com or dk-domain.dk
216 }
217 string querystring = $"Default.aspx?ID={language.ID}";
218 if (!string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["GroupID"]))
219 {
220 querystring += $"&GroupID={Dynamicweb.Context.Current.Request.QueryString["GroupID"]}";
221 }
222 if (!string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["ProductID"]))
223 {
224 querystring += $"&ProductID={Dynamicweb.Context.Current.Request.QueryString["ProductID"]}";
225 }
226 if (!string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["VariantID"]))
227 {
228 querystring += $"&VariantID={Dynamicweb.Context.Current.Request.QueryString["VariantID"]}";
229 }
230
231 string friendlyUrl = Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl(querystring);
232 if (language.Area.RedirectFirstPage && language.ParentPageId == 0 && language.Sort == 1)
233 {
234 friendlyUrl = "/";
235 }
236 string href = $"{url.Scheme}://{hostName}{friendlyUrl}";
237
238 var hreflang = language.Area.CultureInfo.Name.ToLower();
239 if(language.Area.EcomLanguageId == "LANG7")
240 {
241 hreflang = "en-150";
242 }
243
244 if (!alreadyWrittenTwoletterIsos.Contains(hreflang))
245 {
246 alreadyWrittenTwoletterIsos.Add(hreflang);
247 <link rel="alternate" hreflang="@hreflang" href="@href.TrimEnd('/')">
248 }
249 }
250 }
251 }
252 }
253
254 <title>@Model.Title</title>
255 @* Bootstrap + Swift stylesheet *@
256 <link href="/Files/Templates/Designs/Swift/Assets/css/styles.css?@cssStyleFileInfo.LastWriteTime.Ticks" rel="stylesheet" media="all" type="text/css">
257 @* <style>pre.dw-error { display:none; }</style> *@
258 @if (disableWideBreakpoints != "disableBoth")
259 {
260 <style>
261 @@media ( min-width: 1600px ) {
262 .container-xxl,
263 .container-xl,
264 .container-lg,
265 .container-md,
266 .container-sm,
267 .container {
268 max-width: 1520px;
269 }
270 }
271 </style>
272
273
274
275 if (disableWideBreakpoints != "disableUltraWideOnly")
276 {
277 <style>
278 @@media ( min-width: 1920px ) {
279 .container-xxl,
280 .container-xl,
281 .container-lg,
282 .container-md,
283 .container-sm,
284 .container {
285 max-width: 1820px;
286 }
287 }
288 </style>
289 }
290 }
291
292 @* Branding and Themes min stylesheet *@
293 <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">
294 <script src="/Files/Templates/Designs/Swift/Assets/js/scripts.js?@jsFileInfo.LastWriteTime.Ticks"></script>
295 <script src="/Files/Templates/Designs/Swift/Assets/js/main.js?@jsMainFileInfo.LastWriteTime.Ticks"></script>
296 <script type="module">
297 swift.Scroll.hideHeadersOnScroll();
298 swift.Scroll.handleAlternativeTheme();
299
300 //Only load if AOS
301 const aosColumns = document.querySelectorAll('[data-aos]');
302 if (aosColumns.length > 0) {
303 swift.AssetLoader.Load('/Files/Templates/Designs/Swift/Assets/js/aos.js?@jsFileInfo.LastWriteTime.Ticks', 'js');
304 document.addEventListener('load.swift.assetloader', function () {
305 AOS.init({ duration: 400, delay: 100, easing: 'ease-in-out', mirror: false, disable: window.matchMedia('(prefers-reduced-motion: reduce)') });
306 });
307 }
308 </script>
309
310 @* Google gtag method - always include even if it is not used for anything *@
311 <script>
312 window.dataLayer = window.dataLayer || [];
313 function gtag() {
314 //dataLayer.push(arguments);
315 if(arguments[2] && arguments[2]?.items) {
316 // try update items
317 // arguments[2]?.items?.forEach(itm => {
318 // const itm_brand = document.querySelector('[data-gtagbrand="'+itm.item_id+'"]')?.innerText;
319 // if(itm_brand) {
320 // itm['item_brand'] = itm_brand;
321 // }
322 // });
323
324 dataLayer.push({ ecommerce: null });
325 dataLayer.push({
326 event: arguments[1],
327 ecommerce: arguments[2]
328 });
329 } else {
330 dataLayer.push(arguments);
331 }
332 }
333 </script>
334
335 @if (!string.IsNullOrWhiteSpace(customHeaderInclude))
336 {
337 @RenderPartial($"Components/Custom/{customHeaderInclude}")
338 }
339 <style>
340 .item_swift_productlistitemrepeater {
341 font-size: 0.85rem;
342 }
343 .grid > * {
344 min-width: 0 !important;
345 }
346 article.product:hover .border-hover-primary {
347 border: 1px solid var(--swift-button-primary-background-color)!important;
348 }
349 @@media (min-width: 992px) {
350 .component-sticky {
351 position: sticky;
352 top: var(--header-height);
353 height: auto !important;
354 }
355
356 .modal-open .component-sticky {
357 position: static;
358 }
359 }
360 </style>
361 </head>
362 <body class="brand @(masterTheme)" id="page@(Model.ID)">
363
364 @* Google tag manager *@
365 @if (!string.IsNullOrWhiteSpace(googleTagManagerID) && allowTracking)
366 {
367 <noscript>
368 <iframe src="@googleTagManagerID" height="0" width="0" style="display:none;visibility:hidden"></iframe>
369 </noscript>
370 }
371
372 @if (renderAsResponsive || !renderMobile)
373 {
374 var IsCatalogPage = Pageview.Page.ItemType == "Swift_CatalogPage";
375
376 <header class="page-header @headerCssClass top-0@(responsiveClassDesktop)" id="page-header-desktop">
377 @if (IsCatalogPage)
378 {
379 // Current page path. Page Id and title
380 var PagePath = Pageview.Page.GetPath()
381 .Select(a => new KeyValuePair<int, string>(a.ID, a.MenuText))
382 .ToDictionary(x => x.Key, x => x.Value);
383
384 var CatalogHeader = Dynamicweb.Content.Services.Pages.GetPageByNavigationTag(Pageview.Area.ID, "CatalogHeader");
385 var RootpathBrandname = PagePath.Skip(3).FirstOrDefault().Value;
386 var BrandName = RootpathBrandname?.ToLower() switch
387 {
388 "outwell" => "Swift_styles_3",
389 "robens" => "Swift_styles_4",
390 "easy camp" => "Swift_styles_2",
391 _ => ""
392 };
393 <link href="/Files/Templates/Designs/Swift/_parsed/Swift_css/@(BrandName).min.css?@cssLastModified.Ticks" rel="stylesheet" media="all" type="text/css" data-last-modified-content="@cssLastModified">
394 @RenderGrid(CatalogHeader.ID)
395 } else
396 {
397 if (headerDesktopLink != null)
398 {
399 @RenderGrid(headerDesktopLink.PageId)
400 }
401 }
402 </header>
403 }
404
405 @if ((renderAsResponsive || renderMobile))
406 {
407 <header class="page-header @headerCssClass top-0@(responsiveClassMobile)" id="page-header-mobile">
408 @if (headerMobileLink != null)
409 {
410 @RenderGrid(headerMobileLink.PageId)
411 }
412 </header>
413 }
414
415 <div data-intersect></div>
416
417 <main id="content" style="user-select:none;" @(schemaOrgType)>
418 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.PageViewModel>
419 @using System
420 @using Dynamicweb.Ecommerce.ProductCatalog
421 @using Oase.Backend.Extensions
422
423
424 @{
425 string productIdFromUrl = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("ProductID")) ? Dynamicweb.Context.Current.Request.QueryString.Get("ProductID") : string.Empty;
426 bool isProductDetail = !string.IsNullOrEmpty(productIdFromUrl) && Pageview.Page.NavigationTag.ToLower() == "shop";
427
428 bool isArticlePagePage = Model.ItemType == "Swift_Article";
429 bool isArticleListPage = Model.ItemType == "Swift_ArticleListPage";
430 string schemaOrgProp = string.Empty;
431 if(isArticlePagePage)
432 {
433 schemaOrgProp = "itemprop=\"articleBody\"";
434 }
435
436 string theme = "";
437 string gridContent = "";
438
439 if (Model.PropertyItem != null)
440 {
441 theme = !string.IsNullOrWhiteSpace(Model.PropertyItem.GetRawValueString("Theme")) ? "theme " + Model.PropertyItem.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : "";
442 }
443
444 if (Model.Item != null || Pageview.IsVisualEditorMode)
445 {
446 if (!isProductDetail)
447 {
448 gridContent = Model.Grid("Grid", "Grid", "default:true;sort:1", "Page");
449 }
450 else
451 {
452 var productObject = Dynamicweb.Ecommerce.Services.Products.GetProductById(productIdFromUrl, "", Pageview.Area.EcomLanguageId);
453 var detailPage = Dynamicweb.Ecommerce.Services.ProductGroups.GetGroup(productObject.PrimaryGroupId)?.Meta.PrimaryPage ?? string.Empty;
454 var detailPageId = detailPage != string.Empty ? Convert.ToInt16(detailPage.Substring(detailPage.LastIndexOf('=') + 1)) : GetPageIdByNavigationTag("ProductDetailPage");
455
456 @RenderGrid(detailPageId)
457 }
458 }
459
460 bool doNotRenderPage = false;
461
462 //Check if we are on the poduct detail page, and if there is data to render
463 ProductViewModel product = new ProductViewModel();
464 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails"))
465 {
466 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"];
467 if (string.IsNullOrEmpty(product.Id)) {
468 doNotRenderPage = true;
469 }
470 }
471 if(!string.IsNullOrWhiteSpace(productIdFromUrl))
472 {
473 <script>
474
475 gtag("event", "view_item", {
476 currency: "@product.Price.CurrencyCode",
477 value: @PriceViewModelExtensions.ToStringInvariant(product.Price),
478 items: [
479 {
480 item_id: "@product.Id",
481 item_name: "@Dynamicweb.Core.Encoders.HtmlEncoder.JavaScriptStringEncode(product.Name)",
482 item_brand: "@product.ProductFields.GetValueOrDefault("Collection")?.Value",
483 currency: "@product.Price.CurrencyCode",
484 price: @PriceViewModelExtensions.ToStringInvariant(product.Price)
485 }
486 ]
487 });
488 </script>
489 }
490
491 // Custom authorization
492 var AllowUserWith = Model?.PropertyItem?.GetString("AllowUserWith", "");
493 if (!string.IsNullOrWhiteSpace(AllowUserWith))
494 {
495 var currentUserValue = Dynamicweb.Security.UserManagement.UserContext.Current.User?.GetCustomFieldValue($"{AllowUserWith}");
496 if (currentUserValue == false)
497 {
498 doNotRenderPage = true;
499 }
500 }
501
502 //Render the page
503 if (!doNotRenderPage)
504 {
505 string itemIdentifier = Model?.Item?.SystemName != null ? "item_" + Model.Item.SystemName.ToLower() : "item_Swift_Page";
506
507 if (Pageview.IsVisualEditorMode) {
508 @Model.Placeholder("dwcontent", "content", "default:true;sort:1")
509 }
510 <div class="@theme @itemIdentifier" @schemaOrgProp>
511 @if (isArticleListPage)
512 {
513 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\"";
514
515 <form @hx id="ArticleFacetForm">
516 @gridContent
517 </form>
518 <script type="module" src="/Files/Templates/Designs/Swift/Assets/js/htmx.js"></script>
519 <script type="module">
520 document.addEventListener('htmx:confirm', (event) => {
521 let filters = event.detail.elt.querySelectorAll('select');
522 for (var i = 0; i < filters.length; i++) {
523 let input = filters[i];
524 if (input.name && !input.value) {
525 input.name = '';
526 }
527 }
528 });
529
530 document.addEventListener('htmx:beforeOnLoad', (event) => {
531 swift.Scroll.stopIntersectionObserver();
532 });
533
534 document.addEventListener('htmx:afterOnLoad', () => {
535 swift.Scroll.hideHeadersOnScroll();
536 swift.Scroll.handleAlternativeTheme();
537 });
538 </script>
539 }
540 else
541 {
542 @gridContent
543 }
544 </div>
545 <style>
546 @@media (max-width: 991.98px) {
547 .item_custom_swift_productaddtocart.is-sticky {
548 position: fixed;
549 top: var(--header-height);
550 left: 0;
551 right: 0;
552 max-width: 100vw!important;
553 z-index: 1060;
554 padding: 0.5rem 1rem;
555 background: #fff;
556 box-shadow: 0 2px 10px rgba(0,0,0,0.1);
557 }
558
559 .item_custom_swift_productaddtocart-placeholder {
560 display: none;
561 }
562
563 .item_custom_swift_productaddtocart-placeholder.active {
564 display: block;
565 }
566
567 .offcanvas {
568 z-index: 1070!important;
569 }
570 }
571 </style>
572 } else {
573 <div class="container py-5">
574 <div class="alert alert-info" role="alert">@Translate("Sorry. There is nothing to view here")</div>
575 </div>
576 }
577
578 if (!Model.IsCurrentUserAllowed)
579 {
580 int signInPage = GetPageIdByNavigationTag("SignInPage");
581 int dashboardPage = GetPageIdByNavigationTag("MyAccountDashboardPage");
582
583 if (!Pageview.IsVisualEditorMode)
584 {
585 if (signInPage != 0)
586 {
587 if (signInPage != Model.ID) {
588 Dynamicweb.Context.Current.Response.Redirect("/Default.aspx?ID=" + signInPage);
589 } else {
590 if (dashboardPage != 0) {
591 Dynamicweb.Context.Current.Response.Redirect("/Default.aspx?ID=" + dashboardPage);
592 } else {
593 Dynamicweb.Context.Current.Response.Redirect("/");
594 }
595 }
596 }
597 else
598 {
599 <div class="alert alert-dark m-0" role="alert">
600 <span>@Translate("You do not have access to this page")</span>
601 </div>
602 }
603 }
604 else
605 {
606 <div class="alert alert-dark m-0" role="alert">
607 <span>@Translate("To work on this page, you must be signed in, in the frontend")</span>
608 </div>
609 }
610 }
611 }
612
613 </main>
614
615 <link rel="stylesheet" href="/Files/Templates/Designs/Swift/Assets/css/glightbox.min.css" />
616 <script src="/Files/Templates/Designs/Swift/Assets/js/glightbox.min.js"></script>
617 <script>
618 var lightbox = GLightbox({
619 touchNavigation: true,
620 loop: false,
621 autoplayVideos: true,
622 videosWidth: '80vw',
623 dragAutoSnap: true,
624 plyr: {
625 config: {
626 quality: 720,
627 muted: false,
628 volume: 100,
629 youtube: { noCookie: true, rel: 0, showinfo: 0, iv_load_policy: 3, modestbranding: 1 }
630 }
631 }
632 });
633 </script>
634 <script>
635 document.addEventListener('DOMContentLoaded', () => {
636 document.querySelectorAll('a').forEach(link => {
637 const href = link.getAttribute('href');
638 if (href && href.startsWith('https')) {
639 link.setAttribute('target', '_blank');
640 } else {
641 link.setAttribute('target', '_self');
642 }
643 });
644 });
645 </script>
646
647 @if (renderAsResponsive || !renderMobile)
648 {
649 <footer class="page-footer@(responsiveClassDesktop)" id="page-footer-desktop">
650 @if (footerDesktopLink != null)
651 {
652 @RenderGrid(footerDesktopLink.PageId)
653 }
654 </footer>
655 }
656
657 @if (renderAsResponsive || renderMobile)
658 {
659 <footer class="page-footer@(responsiveClassMobile)" id="page-footer-mobile">
660 @if (footerMobileLink != null)
661 {
662 @RenderGrid(footerMobileLink.PageId)
663 }
664 </footer>
665 }
666
667 @* Render any offcanvas menu here *@
668 @RenderSnippet("offcanvas")
669
670 @{
671 bool isErpConnectionDown = !Dynamicweb.Core.Converter.ToBoolean(Context.Current.Items["IsWebServiceConnectionAvailable"]);
672 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/";
673 }
674
675 @* Language selector modal *@
676 <div class="modal fade" id="PreferencesModal" tabindex="-1" aria-hidden="true">
677 <div class="modal-dialog modal-dialog-centered modal-sm" id="PreferencesModalContent">
678 @* The content here comes from an external request *@
679 </div>
680 </div>
681
682 @* Toast *@
683 <div aria-live="polite" aria-atomic="true">
684 <div class="toast-container position-fixed end-0 p-3" style="top:50px;z-index:1500;">
685 <div id="notificationToast" class="toast rounded-6 overflow-hidden" role="alert" aria-live="assertive" aria-atomic="true" data-bs-delay="1000">
686 <div class="toast-body d-flex align-items-end gap-3 theme theme-gray bg-dark text-light">
687 @ReadFile(iconPath + "check.svg")
688 <div id="notificationToast_Text" class=""></div>
689 </div>
690 </div>
691 </div>
692 </div>
693
694 @* Modal for dynamic content *@
695 <div class="modal fade js-product" id="DynamicModal" tabindex="-1" aria-hidden="true">
696 <div class="modal-dialog modal-dialog-centered modal-md">
697 <div class="modal-content theme light" id="DynamicModalContent">
698 @* The content here comes from an external request *@
699 </div>
700 </div>
701 </div>
702
703 @* Offcanvas for dynamic content *@
704 <div class="offcanvas offcanvas-end theme light" tabindex="-1" id="DynamicOffcanvas">
705 @* The content here comes from an external request *@
706 </div>
707
708 @if (Model.Area.Item.GetBoolean("ShowErpDownMessage") && !Dynamicweb.Core.Converter.ToBoolean(Context.Current.Items["IsWebServiceConnectionAvailable"]))
709 {
710 string erpDownMessageTheme = !string.IsNullOrWhiteSpace(Model.Area.Item.GetRawValueString("ErpDownMessageTheme")) ? " theme " + Model.Area.Item.GetRawValueString("ErpDownMessageTheme").Replace(" ", "").Trim().ToLower() : "theme light";
711
712 <div class="position-fixed bottom-0 end-0 p-3" style="z-index: 1040">
713 <div class="toast fade show border-0 @erpDownMessageTheme" role="alert" aria-live="assertive" aria-atomic="true">
714 <div class="toast-header">
715 <strong class="me-auto">@Translate("Connection down")</strong>
716 <button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
717 </div>
718 <div class="toast-body">
719 @Translate("We are experiencing some connectivity issues. Not all features may be available to you.")
720 </div>
721 </div>
722 </div>
723 }
724
725 @if (miniCartEnabled)
726 {
727 @* Open MiniCart when the cart is updated *@
728 <script type="module">
729 document.addEventListener('updated.swift.cart', (event) => {
730 let orderContext = event?.detail?.formData?.get("OrderContext");
731 updateCartSummary(orderContext);
732
733 @if (offcanvasMiniCartBehaviour == "2" || offcanvasMiniCartBehaviour == "3") {
734 <text>openMiniCartOffcanvas();</text>
735 }
736 });
737 </script>
738
739 if (offcanvasMiniCartBehaviour == "1" || offcanvasMiniCartBehaviour == "3")
740 {
741 @* Open MiniCart when toggle is clicked *@
742 <script type="module">
743 let miniCartToggles = document.querySelectorAll('.mini-cart-quantity');
744 miniCartToggles?.forEach((toggle) => {
745 toggle.parentElement.addEventListener('click', (event) => {
746 event.preventDefault();
747 let orderContext = toggle.dataset?.orderContext;
748 updateCartSummary(orderContext);
749
750 openMiniCartOffcanvas();
751 });
752 });
753 </script>
754 }
755
756 <script>
757
758 const updateCartSummary = (orderContext) => {
759 const dynamicOffcanvas = document.getElementById('DynamicOffcanvas');
760 swift.PageUpdater.UpdateFromUrlInline(event, '/Default.aspx?ID=@(cartSummaryPageId)&CartType=minicart&RequestPageID=@(Pageview.Page.ID)&OrderContext=' + orderContext +'', 'Swift_CartSummary.cshtml', dynamicOffcanvas);
761 };
762
763 const openMiniCartOffcanvas = () => {
764 const dynamicOffcanvas = document.getElementById('DynamicOffcanvas');
765 const miniCartOffcanvas = bootstrap.Offcanvas.getOrCreateInstance(dynamicOffcanvas);
766 dynamicOffcanvas.classList.add('overflow-y-auto');
767
768 if (!miniCartOffcanvas._isShown) {
769 miniCartOffcanvas.show();
770 hideActiveOffcanvases(miniCartOffcanvas);
771 }
772 };
773
774 const hideActiveOffcanvases = (miniCartOffcanvas) => {
775 let activeOffcanvases = document.querySelectorAll('.offcanvas.show');
776 activeOffcanvases?.forEach((offCanvas) => {
777 offCanvas = bootstrap.Offcanvas.getInstance(offCanvas);
778 if (offCanvas !== miniCartOffcanvas) {
779 offCanvas.hide();
780 }
781 });
782 };
783
784 </script>
785 }
786 @if (Pageview.Area.Name?.StartsWith("Xhelter") == false && Pageview.Area.Name?.StartsWith("Oase") == false)
787 {
788 <p class="d-none">@Pageview.Area.Name</p>
789 @RenderPartial("Components/Custom/CustomLanguageModal.cshtml")
790 }
791 </body>
792
793 </html>
794
795 }
796 else if (Pageview.IsVisualEditorMode)
797 {
798 <head>
799 <title>@Model.Title</title>
800 @* Bootstrap + Swift stylesheet *@
801 <link href="/Files/Templates/Designs/Swift/Assets/css/styles.css" rel="stylesheet" media="all" type="text/css">
802 </head>
803 <body class="p-3">
804 <div class="alert alert-danger" role="alert">
805 @Translate("Basic Swift setup is needed!")
806 </div>
807
808 @if (brandingPage == null)
809 {
810 <div class="alert alert-warning" role="alert">
811 @Translate("Please add a Branding page and reference it in website settings")
812 </div>
813 }
814
815 @if (themesParagraphs == null)
816 {
817 <div class="alert alert-warning" role="alert">
818 @Translate("Please add a Themes collection page and reference it in website settings")
819 </div>
820 }
821 </body>
822 }
823
824
825 @functions {
826 void SetMetaTags()
827 {
828 //Verification Tokens
829 string siteVerificationGoogle = Model.Area.Item.GetString("Google_Site_Verification") != null ? Model.Area.Item.GetString("Google_Site_Verification") : "";
830 string siteVerificationFacebook = Model.Area.Item.GetString("Facebook_Site_Verification") != null ? Model.Area.Item.GetString("Facebook_Site_Verification") : "";
831
832 //Generic Site Values
833 string openGraphFacebookAppID = Model.Area.Item.GetString("Fb_app_id") != null ? Model.Area.Item.GetString("Fb_app_id") : "";
834 string openGraphType = Model.Area.Item.GetString("Open_Graph_Type") != null ? Model.Area.Item.GetString("Open_Graph_Type") : "";
835 string openGraphSiteName = Model.Area.Item.GetString("Open_Graph_Site_Name") != null ? Model.Area.Item.GetString("Open_Graph_Site_Name") : "";
836
837 string twitterCardSite = Model.Area.Item.GetString("Twitter_Site") != null ? Model.Area.Item.GetString("Twitter_Site") : "";
838
839 //Page specific values
840 string openGraphSiteTitle = Model.Area.Item.GetString("Open_Graph_Title") != null ? Model.Area.Item.GetString("Open_Graph_Title") : "";
841 FileViewModel openGraphImage = Model.Area.Item.GetFile("Open_Graph_Image");
842 string openGraphImageALT = Model.Area.Item.GetString("Open_Graph_Image_ALT") != null ? Model.Area.Item.GetString("Open_Graph_Image_ALT") : "";
843 string openGraphDescription = Model.Area.Item.GetString("Open_Graph_Description") != null ? Model.Area.Item.GetString("Open_Graph_Description") : "";
844
845 string twitterCardURL = Model.Area.Item.GetString("Twitter_URL") != null ? Model.Area.Item.GetString("Twitter_URL") : "";
846 string twitterCardTitle = Model.Area.Item.GetString("Twitter_Title") != null ? Model.Area.Item.GetString("Twitter_Title") : "";
847 string twitterCardDescription = Model.Area.Item.GetString("Twitter_Description") != null ? Model.Area.Item.GetString("Twitter_Description") : "";
848 FileViewModel twitterCardImage = Model.Area.Item.GetFile("Twitter_Image");
849 string twitterCardImageALT = Model.Area.Item.GetString("Twitter_Image_ALT") != null ? Model.Area.Item.GetString("Twitter_Image_ALT") : "";
850 string topImage = Pageview.Page.TopImage.StartsWith("/Files", StringComparison.OrdinalIgnoreCase) ? Pageview.Page.TopImage : $"/Files{Pageview.Page.TopImage}";
851
852 if (string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["ProductID"]))
853 {
854 if (!string.IsNullOrEmpty(Model.Description))
855 {
856 Pageview.Meta.AddTag($"<meta property=\"og:description\" content=\"{Model.Description}\">");
857 }
858 else
859 {
860 Pageview.Meta.AddTag($"<meta property=\"og:description\" content=\"{openGraphDescription}\">");
861 }
862
863 if (!string.IsNullOrEmpty(Pageview.Page.TopImage))
864 {
865 Pageview.Meta.AddTag($"<meta property=\"og:image\" content=\"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{topImage}\">");
866 Pageview.Meta.AddTag($"<meta property=\"og:image:secure_url\" content=\"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{topImage}\">");
867 }
868 else if (openGraphImage != null)
869 {
870 Pageview.Meta.AddTag($"<meta property=\"og:image\" content=\"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{openGraphImage.Path}\">");
871 Pageview.Meta.AddTag($"<meta property=\"og:image:secure_url\" content=\"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{openGraphImage.Path}\">");
872 }
873
874 if (!string.IsNullOrEmpty(openGraphImageALT))
875 {
876 Pageview.Meta.AddTag($"<meta property=\"og:image:alt\" content=\"{openGraphImageALT}\">");
877 }
878 if (!string.IsNullOrEmpty(twitterCardDescription))
879 {
880 Pageview.Meta.AddTag("twitter:description", twitterCardDescription);
881 }
882
883 if (!string.IsNullOrEmpty(Pageview.Page.TopImage))
884 {
885 Pageview.Meta.AddTag("twitter:image", $"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{topImage}");
886 }
887 else if (twitterCardImage != null)
888 {
889 Pageview.Meta.AddTag("twitter:image", $"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{openGraphImage.Path}");
890 }
891
892 if (!string.IsNullOrEmpty(twitterCardImageALT))
893 {
894 Pageview.Meta.AddTag("twitter:image:alt", twitterCardImageALT);
895 }
896 }
897
898 if (!string.IsNullOrEmpty(siteVerificationGoogle))
899 {
900 Pageview.Meta.AddTag("google-site-verification", siteVerificationGoogle);
901 }
902
903 if (!string.IsNullOrEmpty(siteVerificationFacebook))
904 {
905 Pageview.Meta.AddTag("facebook-domain-verification", siteVerificationFacebook);
906 }
907
908 if (!string.IsNullOrEmpty(openGraphFacebookAppID))
909 {
910 Pageview.Meta.AddTag($"<meta property=\"fb:app_id\" content=\"{openGraphFacebookAppID}\">");
911 }
912
913 if (!string.IsNullOrEmpty(openGraphType))
914 {
915 Pageview.Meta.AddTag($"<meta property=\"og:type\" content=\"{openGraphType}\">");
916 }
917
918 if (!string.IsNullOrEmpty(openGraphSiteName))
919 {
920 Pageview.Meta.AddTag($"<meta property=\"og:url\" content=\"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{Pageview.SearchFriendlyUrl}\">");
921 }
922
923 if (!string.IsNullOrEmpty(openGraphSiteName))
924 {
925 Pageview.Meta.AddTag($"<meta property=\"og:site_name\" content=\"{openGraphSiteName}\">");
926 }
927
928 if (!string.IsNullOrEmpty(Model.Title))
929 {
930 Pageview.Meta.AddTag($"<meta property=\"og:title\" content=\"{Model.Title}\">");
931 }
932 else
933 {
934 Pageview.Meta.AddTag($"<meta property=\"og:title\" content=\"{openGraphSiteTitle}\">");
935 }
936
937 if (!string.IsNullOrEmpty(twitterCardSite))
938 {
939 Pageview.Meta.AddTag("twitter:site", twitterCardSite);
940 }
941
942 if (!string.IsNullOrEmpty(twitterCardURL))
943 {
944 Pageview.Meta.AddTag("twitter:url", twitterCardURL);
945 }
946
947 if (!string.IsNullOrEmpty(twitterCardTitle))
948 {
949 Pageview.Meta.AddTag("twitter:title", twitterCardTitle);
950 }
951 }
952 }
953