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_536b6bef34d340e5b4fd527215c13ace.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 .image-overlay {
350 position: relative;
351 }
352 .image-overlay:after {
353 content: "";
354 position: absolute;
355 top: 0;
356 left: 0;
357 width: 100%;
358 height: 100%;
359 pointer-events: none;
360 background: #0004;
361 }
362 .image-overlay-left:after {
363 background: radial-gradient(ellipse at bottom left, #0009 0%, #0000 60%);
364 }
365 .image-overlay-right:after {
366 background: radial-gradient(ellipse at bottom left, #0009 0%, #0000 60%);
367 }
368 @@media (min-width: 992px) {
369 .component-sticky {
370 position: sticky;
371 top: var(--header-height);
372 height: auto !important;
373 }
374
375 .modal-open .component-sticky {
376 position: static;
377 }
378 }
379 </style>
380 </head>
381 <body class="brand @(masterTheme)" id="page@(Model.ID)">
382
383 @* Google tag manager *@
384 @if (!string.IsNullOrWhiteSpace(googleTagManagerID) && allowTracking)
385 {
386 <noscript>
387 <iframe src="@googleTagManagerID" height="0" width="0" style="display:none;visibility:hidden"></iframe>
388 </noscript>
389 }
390
391 @if (renderAsResponsive || !renderMobile)
392 {
393 var IsCatalogPage = Pageview.Page.ItemType == "Swift_CatalogPage";
394
395 <header class="page-header @headerCssClass top-0@(responsiveClassDesktop)" id="page-header-desktop">
396 @if (IsCatalogPage)
397 {
398 // Current page path. Page Id and title
399 var PagePath = Pageview.Page.GetPath()
400 .Select(a => new KeyValuePair<int, string>(a.ID, a.MenuText))
401 .ToDictionary(x => x.Key, x => x.Value);
402
403 var CatalogHeader = Dynamicweb.Content.Services.Pages.GetPageByNavigationTag(Pageview.Area.ID, "CatalogHeader");
404 var RootpathBrandname = PagePath.Skip(3).FirstOrDefault().Value;
405 var BrandName = RootpathBrandname?.ToLower() switch
406 {
407 "outwell" => "Swift_styles_3",
408 "robens" => "Swift_styles_4",
409 "easy camp" => "Swift_styles_2",
410 _ => ""
411 };
412 <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">
413 @RenderGrid(CatalogHeader.ID)
414 } else
415 {
416 if (headerDesktopLink != null)
417 {
418 @RenderGrid(headerDesktopLink.PageId)
419 }
420 }
421 </header>
422 }
423
424 @if ((renderAsResponsive || renderMobile))
425 {
426 <header class="page-header @headerCssClass top-0@(responsiveClassMobile)" id="page-header-mobile">
427 @if (headerMobileLink != null)
428 {
429 @RenderGrid(headerMobileLink.PageId)
430 }
431 </header>
432 }
433
434 <div data-intersect></div>
435
436 <main id="content" style="@(Pageview.AreaID != 29 ? "user-select:none;" : "")" @(schemaOrgType)>
437 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.PageViewModel>
438 @using System
439 @using Dynamicweb.Ecommerce.ProductCatalog
440 @using Oase.Backend.Extensions
441
442
443 @{
444 string productIdFromUrl = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("ProductID")) ? Dynamicweb.Context.Current.Request.QueryString.Get("ProductID") : string.Empty;
445 bool isProductDetail = !string.IsNullOrEmpty(productIdFromUrl) && Pageview.Page.NavigationTag.ToLower() == "shop";
446
447 bool isArticlePagePage = Model.ItemType == "Swift_Article";
448 bool isArticleListPage = Model.ItemType == "Swift_ArticleListPage";
449 string schemaOrgProp = string.Empty;
450 if(isArticlePagePage)
451 {
452 schemaOrgProp = "itemprop=\"articleBody\"";
453 }
454
455 string theme = "";
456 string gridContent = "";
457
458 if (Model.PropertyItem != null)
459 {
460 theme = !string.IsNullOrWhiteSpace(Model.PropertyItem.GetRawValueString("Theme")) ? "theme " + Model.PropertyItem.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : "";
461 }
462
463 if (Model.Item != null || Pageview.IsVisualEditorMode)
464 {
465 if (!isProductDetail)
466 {
467 gridContent = Model.Grid("Grid", "Grid", "default:true;sort:1", "Page");
468 }
469 else
470 {
471 var productObject = Dynamicweb.Ecommerce.Services.Products.GetProductById(productIdFromUrl, "", Pageview.Area.EcomLanguageId);
472 var detailPage = Dynamicweb.Ecommerce.Services.ProductGroups.GetGroup(productObject.PrimaryGroupId)?.Meta.PrimaryPage ?? string.Empty;
473 var detailPageId = detailPage != string.Empty ? Convert.ToInt16(detailPage.Substring(detailPage.LastIndexOf('=') + 1)) : GetPageIdByNavigationTag("ProductDetailPage");
474
475 @RenderGrid(detailPageId)
476 }
477 }
478
479 bool doNotRenderPage = false;
480
481 //Check if we are on the poduct detail page, and if there is data to render
482 ProductViewModel product = new ProductViewModel();
483 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails"))
484 {
485 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"];
486 if (string.IsNullOrEmpty(product.Id)) {
487 doNotRenderPage = true;
488 }
489 if (product!.ProductFields.TryGetValue("PublicationStart", out FieldValueViewModel? PublicationStart))
490 {
491 DateTime.TryParse(PublicationStart.Value.ToString(), out DateTime PublicationStartDate);
492 if (PublicationStartDate >= DateTime.Now)
493 {
494 var shopPageId = GetPageIdByNavigationTag("Shop");
495 Dynamicweb.Context.Current.Response.Redirect($"/Default.aspx?ID={shopPageId}&GroupID={product.PrimaryOrDefaultGroup.Id}");
496 }
497 }
498 }
499 if(!string.IsNullOrWhiteSpace(productIdFromUrl))
500 {
501 <script>
502
503 gtag("event", "view_item", {
504 currency: "@product.Price.CurrencyCode",
505 value: @PriceViewModelExtensions.ToStringInvariant(product.Price),
506 items: [
507 {
508 item_id: "@product.Id",
509 item_name: "@Dynamicweb.Core.Encoders.HtmlEncoder.JavaScriptStringEncode(product.Name)",
510 item_brand: "@product.ProductFields.GetValueOrDefault("Collection")?.Value",
511 currency: "@product.Price.CurrencyCode",
512 price: @PriceViewModelExtensions.ToStringInvariant(product.Price)
513 }
514 ]
515 });
516 </script>
517 }
518
519 // Custom authorization
520 var AllowUserWith = Model?.PropertyItem?.GetString("AllowUserWith", "");
521 if (!string.IsNullOrWhiteSpace(AllowUserWith))
522 {
523 var currentUserValue = Dynamicweb.Security.UserManagement.UserContext.Current.User?.GetCustomFieldValue($"{AllowUserWith}");
524 if (currentUserValue == false)
525 {
526 doNotRenderPage = true;
527 }
528 }
529
530 //Render the page
531 if (!doNotRenderPage)
532 {
533 string itemIdentifier = Model?.Item?.SystemName != null ? "item_" + Model.Item.SystemName.ToLower() : "item_Swift_Page";
534
535 if (Pageview.IsVisualEditorMode) {
536 @Model.Placeholder("dwcontent", "content", "default:true;sort:1")
537 }
538 <div class="@theme @itemIdentifier" @schemaOrgProp>
539 @if (isArticleListPage)
540 {
541 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\"";
542
543 <form @hx id="ArticleFacetForm">
544 @gridContent
545 </form>
546 <script type="module" src="/Files/Templates/Designs/Swift/Assets/js/htmx.js"></script>
547 <script type="module">
548 document.addEventListener('htmx:confirm', (event) => {
549 let filters = event.detail.elt.querySelectorAll('select');
550 for (var i = 0; i < filters.length; i++) {
551 let input = filters[i];
552 if (input.name && !input.value) {
553 input.name = '';
554 }
555 }
556 });
557
558 document.addEventListener('htmx:beforeOnLoad', (event) => {
559 swift.Scroll.stopIntersectionObserver();
560 });
561
562 document.addEventListener('htmx:afterOnLoad', () => {
563 swift.Scroll.hideHeadersOnScroll();
564 swift.Scroll.handleAlternativeTheme();
565 });
566 </script>
567 }
568 else
569 {
570 @gridContent
571 }
572 </div>
573 <style>
574 @@media (max-width: 991.98px) {
575 .item_custom_swift_productaddtocart.is-sticky {
576 position: fixed;
577 top: var(--header-height);
578 left: 0;
579 right: 0;
580 max-width: 100vw!important;
581 z-index: 1060;
582 padding: 0.5rem 1rem;
583 background: #fff;
584 box-shadow: 0 2px 10px rgba(0,0,0,0.1);
585 }
586
587 .item_custom_swift_productaddtocart-placeholder {
588 display: none;
589 }
590
591 .item_custom_swift_productaddtocart-placeholder.active {
592 display: block;
593 }
594
595 .offcanvas {
596 z-index: 1070!important;
597 }
598 }
599 </style>
600 } else {
601 <div class="container py-5">
602 <div class="alert alert-info" role="alert">@Translate("Sorry. There is nothing to view here")</div>
603 </div>
604 }
605
606 if (!Model.IsCurrentUserAllowed)
607 {
608 int signInPage = GetPageIdByNavigationTag("SignInPage");
609 int dashboardPage = GetPageIdByNavigationTag("MyAccountDashboardPage");
610
611 if (!Pageview.IsVisualEditorMode)
612 {
613 if (signInPage != 0)
614 {
615 if (signInPage != Model.ID) {
616 Dynamicweb.Context.Current.Response.Redirect("/Default.aspx?ID=" + signInPage);
617 } else {
618 if (dashboardPage != 0) {
619 Dynamicweb.Context.Current.Response.Redirect("/Default.aspx?ID=" + dashboardPage);
620 } else {
621 Dynamicweb.Context.Current.Response.Redirect("/");
622 }
623 }
624 }
625 else
626 {
627 <div class="alert alert-dark m-0" role="alert">
628 <span>@Translate("You do not have access to this page")</span>
629 </div>
630 }
631 }
632 else
633 {
634 <div class="alert alert-dark m-0" role="alert">
635 <span>@Translate("To work on this page, you must be signed in, in the frontend")</span>
636 </div>
637 }
638 }
639 }
640
641 </main>
642
643 <link rel="stylesheet" href="/Files/Templates/Designs/Swift/Assets/css/glightbox.min.css" />
644 <script src="/Files/Templates/Designs/Swift/Assets/js/glightbox.min.js"></script>
645 <script>
646 var lightbox = GLightbox({
647 touchNavigation: true,
648 loop: false,
649 autoplayVideos: true,
650 videosWidth: '80vw',
651 dragAutoSnap: true,
652 plyr: {
653 config: {
654 quality: 720,
655 muted: false,
656 volume: 100,
657 youtube: { noCookie: true, rel: 0, showinfo: 0, iv_load_policy: 3, modestbranding: 1 }
658 }
659 }
660 });
661 </script>
662 <script>
663 document.addEventListener('DOMContentLoaded', () => {
664 document.querySelectorAll('a').forEach(link => {
665 const href = link.getAttribute('href');
666 if (href && href.startsWith('https')) {
667 link.setAttribute('target', '_blank');
668 } else {
669 link.setAttribute('target', '_self');
670 }
671 });
672 });
673 </script>
674
675 @if (renderAsResponsive || !renderMobile)
676 {
677 <footer class="page-footer@(responsiveClassDesktop)" id="page-footer-desktop">
678 @if (footerDesktopLink != null)
679 {
680 @RenderGrid(footerDesktopLink.PageId)
681 }
682 </footer>
683 }
684
685 @if (renderAsResponsive || renderMobile)
686 {
687 <footer class="page-footer@(responsiveClassMobile)" id="page-footer-mobile">
688 @if (footerMobileLink != null)
689 {
690 @RenderGrid(footerMobileLink.PageId)
691 }
692 </footer>
693 }
694
695 @* Render any offcanvas menu here *@
696 @RenderSnippet("offcanvas")
697
698 @{
699 bool isErpConnectionDown = !Dynamicweb.Core.Converter.ToBoolean(Context.Current.Items["IsWebServiceConnectionAvailable"]);
700 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/";
701 }
702
703 @* Language selector modal *@
704 <div class="modal fade" id="PreferencesModal" tabindex="-1" aria-hidden="true">
705 <div class="modal-dialog modal-dialog-centered modal-sm" id="PreferencesModalContent">
706 @* The content here comes from an external request *@
707 </div>
708 </div>
709
710 @* Toast *@
711 <div aria-live="polite" aria-atomic="true">
712 <div class="toast-container position-fixed end-0 p-3" style="top:50px;z-index:1500;">
713 <div id="notificationToast" class="toast rounded-6 overflow-hidden" role="alert" aria-live="assertive" aria-atomic="true" data-bs-delay="1000">
714 <div class="toast-body d-flex align-items-end gap-3 theme theme-gray bg-dark text-light">
715 @ReadFile(iconPath + "check.svg")
716 <div id="notificationToast_Text" class=""></div>
717 </div>
718 </div>
719 </div>
720 </div>
721
722 @* Modal for dynamic content *@
723 <div class="modal fade js-product" id="DynamicModal" tabindex="-1" aria-hidden="true">
724 <div class="modal-dialog modal-dialog-centered modal-md">
725 <div class="modal-content theme light" id="DynamicModalContent">
726 @* The content here comes from an external request *@
727 </div>
728 </div>
729 </div>
730
731 @* Offcanvas for dynamic content *@
732 <div class="offcanvas offcanvas-end theme light" tabindex="-1" id="DynamicOffcanvas">
733 @* The content here comes from an external request *@
734 </div>
735
736 @if (Model.Area.Item.GetBoolean("ShowErpDownMessage") && !Dynamicweb.Core.Converter.ToBoolean(Context.Current.Items["IsWebServiceConnectionAvailable"]))
737 {
738 string erpDownMessageTheme = !string.IsNullOrWhiteSpace(Model.Area.Item.GetRawValueString("ErpDownMessageTheme")) ? " theme " + Model.Area.Item.GetRawValueString("ErpDownMessageTheme").Replace(" ", "").Trim().ToLower() : "theme light";
739
740 <div class="position-fixed bottom-0 end-0 p-3" style="z-index: 1040">
741 <div class="toast fade show border-0 @erpDownMessageTheme" role="alert" aria-live="assertive" aria-atomic="true">
742 <div class="toast-header">
743 <strong class="me-auto">@Translate("Connection down")</strong>
744 <button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
745 </div>
746 <div class="toast-body">
747 @Translate("We are experiencing some connectivity issues. Not all features may be available to you.")
748 </div>
749 </div>
750 </div>
751 }
752
753 @if (miniCartEnabled)
754 {
755 @* Open MiniCart when the cart is updated *@
756 <script type="module">
757 document.addEventListener('updated.swift.cart', (event) => {
758 let orderContext = event?.detail?.formData?.get("OrderContext");
759 updateCartSummary(orderContext);
760
761 @if (offcanvasMiniCartBehaviour == "2" || offcanvasMiniCartBehaviour == "3") {
762 <text>openMiniCartOffcanvas();</text>
763 }
764 });
765 </script>
766
767 if (offcanvasMiniCartBehaviour == "1" || offcanvasMiniCartBehaviour == "3")
768 {
769 @* Open MiniCart when toggle is clicked *@
770 <script type="module">
771 let miniCartToggles = document.querySelectorAll('.mini-cart-quantity');
772 miniCartToggles?.forEach((toggle) => {
773 toggle.parentElement.addEventListener('click', (event) => {
774 event.preventDefault();
775 let orderContext = toggle.dataset?.orderContext;
776 updateCartSummary(orderContext);
777
778 openMiniCartOffcanvas();
779 });
780 });
781 </script>
782 }
783
784 <script>
785
786 const updateCartSummary = (orderContext) => {
787 const dynamicOffcanvas = document.getElementById('DynamicOffcanvas');
788 swift.PageUpdater.UpdateFromUrlInline(event, '/Default.aspx?ID=@(cartSummaryPageId)&CartType=minicart&RequestPageID=@(Pageview.Page.ID)&OrderContext=' + orderContext +'', 'Swift_CartSummary.cshtml', dynamicOffcanvas);
789 };
790
791 const openMiniCartOffcanvas = () => {
792 const dynamicOffcanvas = document.getElementById('DynamicOffcanvas');
793 const miniCartOffcanvas = bootstrap.Offcanvas.getOrCreateInstance(dynamicOffcanvas);
794 dynamicOffcanvas.classList.add('overflow-y-auto');
795
796 if (!miniCartOffcanvas._isShown) {
797 miniCartOffcanvas.show();
798 hideActiveOffcanvases(miniCartOffcanvas);
799 }
800 };
801
802 const hideActiveOffcanvases = (miniCartOffcanvas) => {
803 let activeOffcanvases = document.querySelectorAll('.offcanvas.show');
804 activeOffcanvases?.forEach((offCanvas) => {
805 offCanvas = bootstrap.Offcanvas.getInstance(offCanvas);
806 if (offCanvas !== miniCartOffcanvas) {
807 offCanvas.hide();
808 }
809 });
810 };
811
812 </script>
813 }
814 @if (Pageview.Area.Name?.StartsWith("Xhelter") == false && Pageview.Area.Name?.StartsWith("Oase") == false)
815 {
816 <p class="d-none">@Pageview.Area.Name</p>
817 @RenderPartial("Components/Custom/CustomLanguageModal.cshtml")
818 }
819 </body>
820
821 </html>
822
823 }
824 else if (Pageview.IsVisualEditorMode)
825 {
826 <head>
827 <title>@Model.Title</title>
828 @* Bootstrap + Swift stylesheet *@
829 <link href="/Files/Templates/Designs/Swift/Assets/css/styles.css" rel="stylesheet" media="all" type="text/css">
830 </head>
831 <body class="p-3">
832 <div class="alert alert-danger" role="alert">
833 @Translate("Basic Swift setup is needed!")
834 </div>
835
836 @if (brandingPage == null)
837 {
838 <div class="alert alert-warning" role="alert">
839 @Translate("Please add a Branding page and reference it in website settings")
840 </div>
841 }
842
843 @if (themesParagraphs == null)
844 {
845 <div class="alert alert-warning" role="alert">
846 @Translate("Please add a Themes collection page and reference it in website settings")
847 </div>
848 }
849 </body>
850 }
851
852
853 @functions {
854 void SetMetaTags()
855 {
856 //Verification Tokens
857 string siteVerificationGoogle = Model.Area.Item.GetString("Google_Site_Verification") != null ? Model.Area.Item.GetString("Google_Site_Verification") : "";
858 string siteVerificationFacebook = Model.Area.Item.GetString("Facebook_Site_Verification") != null ? Model.Area.Item.GetString("Facebook_Site_Verification") : "";
859
860 //Generic Site Values
861 string openGraphFacebookAppID = Model.Area.Item.GetString("Fb_app_id") != null ? Model.Area.Item.GetString("Fb_app_id") : "";
862 string openGraphType = Model.Area.Item.GetString("Open_Graph_Type") != null ? Model.Area.Item.GetString("Open_Graph_Type") : "";
863 string openGraphSiteName = Model.Area.Item.GetString("Open_Graph_Site_Name") != null ? Model.Area.Item.GetString("Open_Graph_Site_Name") : "";
864
865 string twitterCardSite = Model.Area.Item.GetString("Twitter_Site") != null ? Model.Area.Item.GetString("Twitter_Site") : "";
866
867 //Page specific values
868 string openGraphSiteTitle = Model.Area.Item.GetString("Open_Graph_Title") != null ? Model.Area.Item.GetString("Open_Graph_Title") : "";
869 FileViewModel openGraphImage = Model.Area.Item.GetFile("Open_Graph_Image");
870 string openGraphImageALT = Model.Area.Item.GetString("Open_Graph_Image_ALT") != null ? Model.Area.Item.GetString("Open_Graph_Image_ALT") : "";
871 string openGraphDescription = Model.Area.Item.GetString("Open_Graph_Description") != null ? Model.Area.Item.GetString("Open_Graph_Description") : "";
872
873 string twitterCardURL = Model.Area.Item.GetString("Twitter_URL") != null ? Model.Area.Item.GetString("Twitter_URL") : "";
874 string twitterCardTitle = Model.Area.Item.GetString("Twitter_Title") != null ? Model.Area.Item.GetString("Twitter_Title") : "";
875 string twitterCardDescription = Model.Area.Item.GetString("Twitter_Description") != null ? Model.Area.Item.GetString("Twitter_Description") : "";
876 FileViewModel twitterCardImage = Model.Area.Item.GetFile("Twitter_Image");
877 string twitterCardImageALT = Model.Area.Item.GetString("Twitter_Image_ALT") != null ? Model.Area.Item.GetString("Twitter_Image_ALT") : "";
878 string topImage = Pageview.Page.TopImage.StartsWith("/Files", StringComparison.OrdinalIgnoreCase) ? Pageview.Page.TopImage : $"/Files{Pageview.Page.TopImage}";
879
880 if (string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["ProductID"]))
881 {
882 if (!string.IsNullOrEmpty(Model.Description))
883 {
884 Pageview.Meta.AddTag($"<meta property=\"og:description\" content=\"{Model.Description}\">");
885 }
886 else
887 {
888 Pageview.Meta.AddTag($"<meta property=\"og:description\" content=\"{openGraphDescription}\">");
889 }
890
891 if (!string.IsNullOrEmpty(Pageview.Page.TopImage))
892 {
893 Pageview.Meta.AddTag($"<meta property=\"og:image\" content=\"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{topImage}\">");
894 Pageview.Meta.AddTag($"<meta property=\"og:image:secure_url\" content=\"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{topImage}\">");
895 }
896 else if (openGraphImage != null)
897 {
898 Pageview.Meta.AddTag($"<meta property=\"og:image\" content=\"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{openGraphImage.Path}\">");
899 Pageview.Meta.AddTag($"<meta property=\"og:image:secure_url\" content=\"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{openGraphImage.Path}\">");
900 }
901
902 if (!string.IsNullOrEmpty(openGraphImageALT))
903 {
904 Pageview.Meta.AddTag($"<meta property=\"og:image:alt\" content=\"{openGraphImageALT}\">");
905 }
906 if (!string.IsNullOrEmpty(twitterCardDescription))
907 {
908 Pageview.Meta.AddTag("twitter:description", twitterCardDescription);
909 }
910
911 if (!string.IsNullOrEmpty(Pageview.Page.TopImage))
912 {
913 Pageview.Meta.AddTag("twitter:image", $"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{topImage}");
914 }
915 else if (twitterCardImage != null)
916 {
917 Pageview.Meta.AddTag("twitter:image", $"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{openGraphImage.Path}");
918 }
919
920 if (!string.IsNullOrEmpty(twitterCardImageALT))
921 {
922 Pageview.Meta.AddTag("twitter:image:alt", twitterCardImageALT);
923 }
924 }
925
926 if (!string.IsNullOrEmpty(siteVerificationGoogle))
927 {
928 Pageview.Meta.AddTag("google-site-verification", siteVerificationGoogle);
929 }
930
931 if (!string.IsNullOrEmpty(siteVerificationFacebook))
932 {
933 Pageview.Meta.AddTag("facebook-domain-verification", siteVerificationFacebook);
934 }
935
936 if (!string.IsNullOrEmpty(openGraphFacebookAppID))
937 {
938 Pageview.Meta.AddTag($"<meta property=\"fb:app_id\" content=\"{openGraphFacebookAppID}\">");
939 }
940
941 if (!string.IsNullOrEmpty(openGraphType))
942 {
943 Pageview.Meta.AddTag($"<meta property=\"og:type\" content=\"{openGraphType}\">");
944 }
945
946 if (!string.IsNullOrEmpty(openGraphSiteName))
947 {
948 Pageview.Meta.AddTag($"<meta property=\"og:url\" content=\"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{Pageview.SearchFriendlyUrl}\">");
949 }
950
951 if (!string.IsNullOrEmpty(openGraphSiteName))
952 {
953 Pageview.Meta.AddTag($"<meta property=\"og:site_name\" content=\"{openGraphSiteName}\">");
954 }
955
956 if (!string.IsNullOrEmpty(Model.Title))
957 {
958 Pageview.Meta.AddTag($"<meta property=\"og:title\" content=\"{Model.Title}\">");
959 }
960 else
961 {
962 Pageview.Meta.AddTag($"<meta property=\"og:title\" content=\"{openGraphSiteTitle}\">");
963 }
964
965 if (!string.IsNullOrEmpty(twitterCardSite))
966 {
967 Pageview.Meta.AddTag("twitter:site", twitterCardSite);
968 }
969
970 if (!string.IsNullOrEmpty(twitterCardURL))
971 {
972 Pageview.Meta.AddTag("twitter:url", twitterCardURL);
973 }
974
975 if (!string.IsNullOrEmpty(twitterCardTitle))
976 {
977 Pageview.Meta.AddTag("twitter:title", twitterCardTitle);
978 }
979 }
980 }
981