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