Error executing template "Designs/Swift/_parsed/Swift_CustomerCenter.parsed.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_35fe5e1d412f4b9db4b5ea6e43a281f8.Execute() in C:\inetpub\wwwroot\directions2023_dev\Files\Templates\Designs\Swift\_parsed\Swift_CustomerCenter.parsed.cshtml:line 570
   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()

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 42 //CUSTOM CODE 43 //handle Maintenance mode 44 var maintenancePage = Model.Area.Item?.GetLink("MaintenancePage") ?? null; 45 bool maintenanceMode = Model.Area.Item.GetBoolean("MaintenanceMode"); 46 DateTime maintenanceStartDate = Model.Area.Item.GetDateTime("MaintenanceStartDate"); 47 DateTime maintenanceEndDate = Model.Area.Item.GetDateTime("MaintenanceEndDate"); 48 DateTime currentDate = System.DateTime.Now; 49 maintenanceMode = maintenanceMode && maintenanceStartDate <= currentDate && maintenanceEndDate >= currentDate; 50 var adminUser = Dynamicweb.Security.UserManagement.User.GetCurrentBackendUser(); 51 int currentPage = Dynamicweb.Frontend.PageView.Current().Page.ID; 52 53 if(maintenanceMode && maintenancePage != null && adminUser == null && maintenancePage.PageId != currentPage) 54 { 55 string redirectLink = "/" + maintenancePage; 56 Dynamicweb.Context.Current.Response.Redirect(redirectLink); 57 } 58 //CUSTOM CODE 59 60 var cartSummaryPageId = Dynamicweb.Content.Services.Pages.GetPageByNavigationTag(Model.Area.ID, "CartSummary")?.ID; 61 bool enableMiniCart = Model.Area.Item?.GetBoolean("EnableOffcanvasMiniCart") ?? false; 62 var offcanvasMiniCartBehaviour = Model.Area.Item?.GetRawValueString("OffcanvasMinicartBehaviour", "3") ?? "3"; 63 bool miniCartEnabled = cartSummaryPageId != null && enableMiniCart; 64 var brandingPageId = Model.Area.Item?.GetInt32("BrandingPage") ?? 0; 65 var themePageId = Model.Area.Item?.GetInt32("ThemesPage") ?? 0; 66 var cssPageId = Model.Area.Item?.GetInt32("CssPage") ?? 0; 67 var brandingPage = brandingPageId != 0 ? Dynamicweb.Content.Services.Pages?.GetPage(brandingPageId) ?? null : null; 68 var themesParagraphs = themePageId != 0 ? Dynamicweb.Content.Services.Paragraphs?.GetParagraphsByPageId(themePageId) ?? null : null; 69 var cssParagraphs = cssPageId != 0 ? Dynamicweb.Content.Services.Paragraphs?.GetParagraphsByPageId(cssPageId) ?? null : null; 70 71 //CUSTOM CODE 72 var backgroundPageId = Model.Area.Item?.GetInt32("BackgroundsPage") ?? 0; 73 var backgroundParagraphs = backgroundPageId != 0 ? Dynamicweb.Content.Services.Paragraphs?.GetParagraphsByPageId(backgroundPageId) ?? null : null; 74 //CUSTOM CODE 75 } 76 77 @if (themesParagraphs != null || brandingPage != null) 78 { 79 string swiftVersion = ReadFile("/Files/Templates/Designs/Swift/swift_version.txt"); 80 bool renderAsResponsive = Model.Area.Item.GetString("DeviceRendering", "responsive").Equals("responsive", StringComparison.OrdinalIgnoreCase); 81 bool renderMobile = Pageview.Device == Dynamicweb.Frontend.Devices.DeviceType.Mobile || Pageview.Device == Dynamicweb.Frontend.Devices.DeviceType.Tablet; 82 string responsiveClassDesktop = string.Empty; 83 string responsiveClassMobile = string.Empty; 84 if (renderAsResponsive) 85 { 86 responsiveClassDesktop = " d-none d-xl-block"; 87 responsiveClassMobile = " d-block d-xl-none"; 88 } 89 90 var headerDesktopLink = Model.Area.Item?.GetLink("HeaderDesktop") ?? null; 91 var headerMobileLink = Model.Area.Item?.GetLink("HeaderMobile") ?? null; 92 93 var footerDesktopLink = Model.Area.Item?.GetLink("FooterDesktop") ?? null; 94 var footerMobileLink = Model.Area.Item?.GetLink("FooterMobile") ?? null; 95 96 var disableWideBreakpoints = Model.Area?.Item?.GetRawValueString("DisableWideBreakpoints", "default"); 97 98 string customHeaderInclude = !string.IsNullOrEmpty(Model.Area.Item.GetRawValueString("CustomHeaderInclude")) ? Model.Area.Item.GetFile("CustomHeaderInclude").Name : string.Empty; 99 100 var themesParagraphLastChanged = Dynamicweb.Content.Services.Paragraphs.GetParagraphsByPageId(themePageId).OrderByDescending(p => p.Audit.LastModifiedAt).FirstOrDefault(); 101 var cssLastModified = brandingPage.Audit.LastModifiedAt > themesParagraphLastChanged.Audit.LastModifiedAt ? brandingPage.Audit.LastModifiedAt : themesParagraphLastChanged.Audit.LastModifiedAt; 102 103 var cssThemeAndBrandingStyleFileInfo = new System.IO.FileInfo(Dynamicweb.Core.SystemInformation.MapPath($"/Files/Templates/Designs/Swift/_parsed/Swift_css/Swift_styles_{Model.Area.ID}.min.css")); 104 105 //CUSTOM CODE 106 //Create a collection with all last modified dates 107 List<DateTime> filesAudit = new List<DateTime>(); 108 if(brandingPage != null) 109 { 110 filesAudit.Add(brandingPage.Audit.LastModifiedAt); 111 } 112 if(backgroundParagraphs != null) 113 { 114 var backgroundParagraphLastChanged = backgroundParagraphs.OrderByDescending(p => p.Audit.LastModifiedAt).FirstOrDefault(); 115 filesAudit.Add(backgroundParagraphLastChanged.Audit.LastModifiedAt); 116 } 117 118 cssLastModified = filesAudit.Max(); 119 120 if (backgroundPageId != 0) 121 { 122 var backgroundFileInfo = new System.IO.FileInfo(Dynamicweb.Core.SystemInformation.MapPath($"/Files/Templates/Designs/Swift/_parsed/Swift_css/Swift_backgrounds_styles_{Model.Area.ID}.css")); 123 124 if(backgroundParagraphs.Any() && !backgroundFileInfo.Exists) 125 { 126 127 var backgroundParagraphLastChanged = backgroundParagraphs.OrderByDescending(p => p.Audit.LastModifiedAt).FirstOrDefault(); 128 if (!cssThemeAndBrandingStyleFileInfo.Exists || cssThemeAndBrandingStyleFileInfo.LastWriteTime < backgroundParagraphLastChanged.Audit.LastModifiedAt) 129 { 130 131 var backgroundPageview = Dynamicweb.Frontend.PageView.GetPageviewByPageID(backgroundPageId); 132 backgroundPageview.Redirect = false; 133 backgroundPageview.Output(); 134 } 135 } 136 } 137 138 //CUSTOM CODE 139 140 if (cssPageId != 0) 141 { 142 var cssFileInfo = new System.IO.FileInfo(Dynamicweb.Core.SystemInformation.MapPath($"/Files/Templates/Designs/Swift/_parsed/Swift_css/Swift_css_styles_{Model.Area.ID}.css")); 143 var cssParagraphLastChanged = Dynamicweb.Content.Services.Paragraphs.GetParagraphsByPageId(cssPageId).OrderByDescending(p => p.Audit.LastModifiedAt).FirstOrDefault(); 144 if (!cssThemeAndBrandingStyleFileInfo.Exists || cssThemeAndBrandingStyleFileInfo.LastWriteTime < cssParagraphLastChanged.Audit.LastModifiedAt) 145 { 146 var cssPageview = Dynamicweb.Frontend.PageView.GetPageviewByPageID(cssPageId); 147 cssPageview.Redirect = false; 148 cssPageview.Output(); 149 150 var backgroundPageview = Dynamicweb.Frontend.PageView.GetPageviewByPageID(backgroundPageId); 151 backgroundPageview.Redirect = false; 152 backgroundPageview.Output(); 153 } 154 } 155 156 if (!cssThemeAndBrandingStyleFileInfo.Exists || cssThemeAndBrandingStyleFileInfo.LastWriteTime < brandingPage.Audit.LastModifiedAt) 157 { 158 //Branding page has been saved or the file is missing. Rewrite the file to disc. 159 if (brandingPageId > 0) 160 { 161 var brandingPageview = Dynamicweb.Frontend.PageView.GetPageviewByPageID(brandingPageId); 162 brandingPageview.Redirect = false; 163 brandingPageview.Output(); 164 } 165 } 166 167 if (!cssThemeAndBrandingStyleFileInfo.Exists || cssThemeAndBrandingStyleFileInfo.LastWriteTime < themesParagraphLastChanged.Audit.LastModifiedAt) 168 { 169 //Branding page has been saved or the file is missing. Rewrite the file to disc. 170 if (themePageId > 0) 171 { 172 var themePageview = Dynamicweb.Frontend.PageView.GetPageviewByPageID(themePageId); 173 themePageview.Redirect = false; 174 themePageview.Output(); 175 } 176 } 177 178 // Schema.org details for PDP 179 bool isProductDetailsPage = Dynamicweb.Context.Current.Request.QueryString.AllKeys.Contains("ProductID"); 180 bool isArticlePage = Model.ItemType == "Swift_Article"; 181 string schemaOrgType = string.Empty; 182 183 if (isProductDetailsPage) 184 { 185 schemaOrgType = "itemscope=\"\" itemtype=\"https://schema.org/Product\""; 186 } 187 188 if (isArticlePage) 189 { 190 schemaOrgType = "itemscope=\"\" itemtype=\"https://schema.org/Article\""; 191 } 192 193 194 var cssStyleFileInfo = new System.IO.FileInfo(Dynamicweb.Core.SystemInformation.MapPath("/Files/Templates/Designs/Swift/Assets/css/styles.css")); 195 var jsFileInfo = new System.IO.FileInfo(Dynamicweb.Core.SystemInformation.MapPath("/Files/Templates/Designs/Swift/Assets/js/scripts.js")); 196 //CUSTOM CODE// 197 var customJsFileInfo = new System.IO.FileInfo(Dynamicweb.Core.SystemInformation.MapPath("Files/Templates/Designs/Swift/Assets/js/custom-scripts.js")); 198 //CUSTOM CODE// 199 string masterTheme = !string.IsNullOrWhiteSpace(Model.Area.Item.GetRawValueString("Theme")) ? " theme " + Model.Area.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 200 201 string favicon = Model.Area.Item.GetRawValueString("Favicon", "/Files/Templates/Designs/Swift/Assets/Images/favicon.png"); 202 string appleTouchIcon = Model.Area.Item.GetRawValueString("AppleTouchIcon", "/Files/Templates/Designs/Swift/Assets/Images/apple-touch-icon.png"); 203 204 string headerCssClass = "sticky-top"; 205 bool movePageBehind = false; 206 207 if (Model.PropertyItem != null) 208 { 209 headerCssClass = Model.PropertyItem.GetRawValueString("MoveThisPageBehindTheHeader", "sticky-top"); 210 movePageBehind = headerCssClass == "fixed-top" && !Pageview.IsVisualEditorMode ? true : false; 211 } 212 213 headerCssClass = headerCssClass == "" ? "sticky-top" : headerCssClass; 214 headerCssClass = Pageview.IsVisualEditorMode ? "" : headerCssClass; 215 216 string googleTagManagerID = Model.Area.Item.GetString("GoogleTagManagerID").Trim(); 217 string googleAnalyticsMeasurementID = Model.Area.Item.GetString("GoogleAnalyticsMeasurementID").Trim(); 218 219 bool allowTracking = AllowTracking(); 220 221 Dynamicweb.Context.Current.Response.AddHeader("link", $"</Files/Templates/Designs/Swift/Assets/css/styles.css?{cssStyleFileInfo.LastWriteTime.Ticks}>; rel=preload; as=style;"); 222 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;"); 223 Dynamicweb.Context.Current.Response.AddHeader("link", $"</Files/Templates/Designs/Swift/Assets/js/scripts.js?{jsFileInfo.LastWriteTime.Ticks}>; rel=preload; as=script;"); 224 //CUSTOM CODE 225 Dynamicweb.Context.Current.Response.AddHeader("link", $"</Files/Templates/Designs/Swift/Assets/js/custom-scripts.js?{customJsFileInfo.LastWriteTime.Ticks}>; rel=preload; as=script;"); 226 //CUSTOM CODE 227 228 SetMetaTags(); 229 230 List<Dynamicweb.Content.Page> languages = new List<Dynamicweb.Content.Page>(); 231 232 var masterPage = Pageview.Area.IsMaster ? Pageview.Page : Pageview.Page.MasterPage; 233 languages.Add(masterPage); 234 if (masterPage?.Languages != null) 235 { 236 foreach (var language in masterPage.Languages) 237 { 238 languages.Add(language); 239 } 240 } 241 242 Uri url = Dynamicweb.Context.Current.Request.Url; 243 string hostName = url.Host; 244 245 <!doctype html> 246 <html lang="@Pageview.Area.CultureInfo.TwoLetterISOLanguageName"> 247 <head> 248 <!-- @swiftVersion --> 249 @* Required meta tags *@ 250 <meta charset="utf-8"> 251 <meta name="viewport" content="height=device-height, width=device-width, initial-scale=1.0"> 252 <link rel="shortcut icon" href="@favicon"> 253 <link rel="apple-touch-icon" href="@appleTouchIcon"> 254 255 @Model.MetaTags 256 257 @{ 258 var alreadyWrittenTwoletterIsos = new List<string>(); 259 @* Languages meta data *@ 260 foreach (var language in languages) 261 { 262 hostName = url.Host; 263 if (language?.Area != null) 264 { 265 if (language.Area?.MasterArea != null && !string.IsNullOrEmpty(language.Area.MasterArea.DomainLock)) 266 { 267 hostName = language.Area.MasterArea.DomainLock; //dk.domain.com or dk-domain.dk 268 } 269 if (language != null && language.Area != null && language.Published && language.Area.Active && language.Area.Published) 270 { 271 if (!string.IsNullOrEmpty(language.Area.DomainLock)) 272 { 273 hostName = language.Area.DomainLock; //dk.domain.com or dk-domain.dk 274 } 275 string querystring = $"Default.aspx?ID={language.ID}"; 276 if (!string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["GroupID"])) 277 { 278 querystring += $"&GroupID={Dynamicweb.Context.Current.Request.QueryString["GroupID"]}"; 279 } 280 if (!string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["ProductID"])) 281 { 282 querystring += $"&ProductID={Dynamicweb.Context.Current.Request.QueryString["ProductID"]}"; 283 } 284 if (!string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["VariantID"])) 285 { 286 querystring += $"&VariantID={Dynamicweb.Context.Current.Request.QueryString["VariantID"]}"; 287 } 288 289 string friendlyUrl = Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl(querystring); 290 if (language.Area.RedirectFirstPage && language.ParentPageId == 0 && language.Sort == 1) 291 { 292 friendlyUrl = "/"; 293 } 294 string href = $"{url.Scheme}://{hostName}{friendlyUrl}"; 295 296 297 <link rel="alternate" hreflang="@language.Area.CultureInfo.Name.ToLower()" href="@href"> 298 if (!alreadyWrittenTwoletterIsos.Contains(language.Area.CultureInfo.TwoLetterISOLanguageName)) 299 { 300 alreadyWrittenTwoletterIsos.Add(language.Area.CultureInfo.TwoLetterISOLanguageName); 301 <link rel="alternate" hreflang="@language.Area.CultureInfo.TwoLetterISOLanguageName.ToLower()" href="@href"> 302 } 303 } 304 } 305 } 306 } 307 308 <title>@Model.Title</title> 309 @* Bootstrap + Swift stylesheet *@ 310 <link href="/Files/Templates/Designs/Swift/Assets/css/styles.css?@cssStyleFileInfo.LastWriteTime.Ticks" rel="stylesheet" media="all" type="text/css"> 311 312 @if (disableWideBreakpoints != "disableBoth") 313 { 314 <style> 315 @@media ( min-width: 1600px ) { 316 .container-xxl, 317 .container-xl, 318 .container-lg, 319 .container-md, 320 .container-sm, 321 .container { 322 max-width: 1520px; 323 } 324 } 325 </style> 326 327 328 329 if (disableWideBreakpoints != "disableUltraWideOnly") 330 { 331 <style> 332 @@media ( min-width: 1920px ) { 333 .container-xxl, 334 .container-xl, 335 .container-lg, 336 .container-md, 337 .container-sm, 338 .container { 339 max-width: 1820px; 340 } 341 } 342 </style> 343 } 344 } 345 346 @* Branding and Themes min stylesheet *@ 347 <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"> 348 <script src="/Files/Templates/Designs/Swift/Assets/js/scripts.js?@jsFileInfo.LastWriteTime.Ticks"></script> 349 @*CUSTOM CODE*@ 350 <script src="/Files/Templates/Designs/Swift/Assets/js/custom-scripts.js?@customJsFileInfo.LastWriteTime.Ticks" defer></script> 351 @*CUSTOM CODE*@ 352 <script type="module"> 353 swift.Scroll.hideHeadersOnScroll(); 354 swift.Scroll.handleAlternativeTheme(); 355 356 //Only load if AOS 357 const aosColumns = document.querySelectorAll('[data-aos]'); 358 if (aosColumns.length > 0) { 359 swift.AssetLoader.Load('/Files/Templates/Designs/Swift/Assets/js/aos.js?@jsFileInfo.LastWriteTime.Ticks', 'js'); 360 document.addEventListener('load.swift.assetloader', function () { 361 AOS.init({ duration: 400, delay: 100, easing: 'ease-in-out', mirror: false, disable: window.matchMedia('(prefers-reduced-motion: reduce)') }); 362 }); 363 } 364 </script> 365 366 @* Google gtag method - always include even if it is not used for anything *@ 367 <script> 368 window.dataLayer = window.dataLayer || []; 369 function gtag() { dataLayer.push(arguments); } 370 </script> 371 @* Google tag manager *@ 372 @if (!string.IsNullOrWhiteSpace(googleTagManagerID)) 373 { 374 <script> 375 gtag('consent', 'default', { 376 'ad_storage': 'denied', 377 'ad_user_data': 'denied', 378 'ad_personalization': 'denied', 379 'analytics_storage': 'denied' 380 }); 381 </script> 382 <script> 383 (function (w, d, s, l, i) { 384 w[l] = w[l] || []; w[l].push({ 385 'gtm.start': 386 new Date().getTime(), event: 'gtm.js' 387 }); var f = d.getElementsByTagName(s)[0], 388 j = d.createElement(s), dl = l != 'dataLayer' ? '&l=' + l : ''; j.async = true; j.src = 389 'https://www.googletagmanager.com/gtm.js?id=' + i + dl; f.parentNode.insertBefore(j, f); 390 })(window, document, 'script', 'dataLayer', '@(googleTagManagerID)'); 391 </script> 392 if (allowTracking) 393 { 394 string adConsent = GetCookieOptInPermission("Marketing"); 395 string analyticsConsent = GetCookieOptInPermission("Statistical"); 396 <script> 397 gtag('consent', 'update', { 398 'ad_storage': '@adConsent', 399 'ad_user_data': '@adConsent', 400 'ad_personalization': '@adConsent', 401 'analytics_storage': '@analyticsConsent' 402 }); 403 </script> 404 } 405 } 406 407 @if (!string.IsNullOrWhiteSpace(googleAnalyticsMeasurementID) && allowTracking) 408 { 409 var GoogleAnalyticsDebugMode = ""; 410 411 if (Model.Area.Item.GetBoolean("EnableGoogleAnalyticsDebugMode")) 412 { 413 GoogleAnalyticsDebugMode = ", {'debug_mode': true}"; 414 } 415 416 <script async src="https://www.googletagmanager.com/gtag/js?id=@googleAnalyticsMeasurementID"></script> 417 <script> 418 gtag('js', new Date()); 419 gtag('config', '@googleAnalyticsMeasurementID'@GoogleAnalyticsDebugMode); 420 </script> 421 } 422 423 @if (!string.IsNullOrWhiteSpace(customHeaderInclude)) 424 { 425 @RenderPartial($"Components/Custom/{customHeaderInclude}") 426 } 427 </head> 428 <body class="brand @(masterTheme)" id="page@(Model.ID)"> 429 430 @* Google tag manager *@ 431 @if (!string.IsNullOrWhiteSpace(googleTagManagerID) && allowTracking) 432 { 433 <noscript> 434 <iframe src="https://www.googletagmanager.com/ns.html?id=@(googleTagManagerID)" 435 height="0" width="0" style="display:none;visibility:hidden"></iframe> 436 </noscript> 437 } 438 439 @if (renderAsResponsive || !renderMobile) 440 { 441 <header class="page-header @headerCssClass top-0@(responsiveClassDesktop)" id="page-header-desktop"> 442 @if (headerDesktopLink != null) 443 { 444 @RenderGrid(headerDesktopLink.PageId) 445 } 446 </header> 447 } 448 449 @if ((renderAsResponsive || renderMobile)) 450 { 451 <header class="page-header @headerCssClass top-0@(responsiveClassMobile)" id="page-header-mobile"> 452 @if (headerMobileLink != null) 453 { 454 @RenderGrid(headerMobileLink.PageId) 455 } 456 </header> 457 } 458 459 <div data-intersect></div> 460 461 <main id="content" @(schemaOrgType)> 462 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.PageViewModel> 463 @using System 464 @using Dynamicweb.Ecommerce.ProductCatalog 465 @using Dynamicweb.Frontend.Navigation 466 467 468 @{ 469 string productIdFromUrl = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("ProductID")) ? Dynamicweb.Context.Current.Request.QueryString.Get("ProductID") : string.Empty; 470 bool isProductDetail = !string.IsNullOrEmpty(productIdFromUrl) && Pageview.Page.NavigationTag.ToLower() == "shop"; 471 472 bool isArticlePagePage = Model.ItemType == "Swift_Article"; 473 bool isArticleListPage = Model.ItemType == "Swift_ArticleListPage"; 474 string schemaOrgProp = string.Empty; 475 if(isArticlePagePage) 476 { 477 schemaOrgProp = "itemprop=\"articleBody\""; 478 } 479 480 string theme = ""; 481 string gridContent = ""; 482 483 if (Model.PropertyItem != null) 484 { 485 theme = !string.IsNullOrWhiteSpace(Model.PropertyItem.GetRawValueString("Theme")) ? "theme " + Model.PropertyItem.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 486 } 487 488 if (Model.Item != null || Pageview.IsVisualEditorMode) 489 { 490 if (!isProductDetail) 491 { 492 gridContent = Model.Grid("Grid", "Grid", "default:true;sort:1", "Page"); 493 } 494 else 495 { 496 var productObject = Dynamicweb.Ecommerce.Services.Products.GetProductById(productIdFromUrl, "", Pageview.Area.EcomLanguageId); 497 var detailPage = Dynamicweb.Ecommerce.Services.ProductGroups.GetGroup(productObject.PrimaryGroupId)?.Meta.PrimaryPage ?? string.Empty; 498 var detailPageId = detailPage != string.Empty ? Convert.ToInt16(detailPage.Substring(detailPage.LastIndexOf('=') + 1)) : GetPageIdByNavigationTag("ProductDetailPage"); 499 500 @RenderGrid(detailPageId) 501 } 502 } 503 504 //Navigation settings 505 string navAlignment = "justify-content-start text-start"; 506 string navOrientation = "flex-column"; 507 string layout = "lines"; 508 string linkFontWeight = "fw-normal"; 509 string navTitleFontWeight = "fw-bold"; 510 string NavTitleCasing = "text-capitalize"; 511 string linkCasing = ""; 512 string linkFontSize = "fs-6"; 513 string navTitleFontSize = "h6"; 514 string navigationRoot = "112"; 515 bool showOnlyFirstLevel = true; 516 string contentPadding = " px-3 py-3"; 517 518 var navigationSettings = new NavigationSettings(); 519 navigationSettings.StartLevel = 1; 520 navigationSettings.StopLevel = 10; 521 navigationSettings.ExpandMode = ExpandMode.All; 522 523 navigationSettings.Parameters.Add("Layout", layout); 524 navigationSettings.Parameters.Add("LinkFontSize", linkFontSize); 525 navigationSettings.Parameters.Add("NavOrientation", navOrientation); 526 navigationSettings.Parameters.Add("LinkFontWeight", linkFontWeight); 527 navigationSettings.Parameters.Add("NavAlignment", navAlignment); 528 navigationSettings.Parameters.Add("LinkCasing", linkCasing); 529 navigationSettings.Parameters.Add("Theme", theme); 530 navigationSettings.Parameters.Add("ShowOnlyFirstNavLevel", showOnlyFirstLevel); 531 532 if (!string.IsNullOrEmpty(navigationRoot)) 533 { 534 int rootPageId = Convert.ToInt32(navigationRoot); 535 navigationSettings.RootPageId = rootPageId; 536 537 var page = Dynamicweb.Content.Services.Pages.GetPage(rootPageId); 538 if (page != null && !string.IsNullOrEmpty(page.NavigationTag)) 539 { 540 navigationSettings.Parameters.Add("menu-id", page.NavigationTag.ToLower()); 541 } 542 543 } 544 else 545 { 546 navigationSettings.Parameters.Add("menu-id", "root"); 547 } 548 549 550 bool doNotRenderPage = false; 551 552 //Check if we are on the poduct detail page, and if there is data to render 553 ProductViewModel product = new ProductViewModel(); 554 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 555 { 556 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 557 if (string.IsNullOrEmpty(product.Id)) { 558 doNotRenderPage = true; 559 } 560 } 561 562 //Render the page 563 if (!doNotRenderPage) { 564 string itemIdentifier = Model?.Item?.SystemName != null ? "item_" + Model.Item.SystemName.ToLower() : "item_Swift_Page"; 565 string sectionClass = "py-3 py-lg-3"; 566 string containerClass = "container-xl"; 567 string mobileColumnSize = "1"; 568 string rowClass = ""; 569 570 <div class="@theme @itemIdentifier" @schemaOrgProp> 571 <div class="@(sectionClass)@(theme)"> 572 <div class="@containerClass"> 573 <div class="grid grid-@(mobileColumnSize) grid-lg-4@(rowClass)"> 574 <div class="g-col order-first order-lg-0" data-col-size="3" style="--bs-columns:12"> 575 <div class="h-100@(theme) item_@Model.Item.SystemName.ToLower()"> 576 @if (!string.IsNullOrEmpty(Model.Item.GetString("Title")) && !Model.Item.GetBoolean("HideTitle")) 577 { 578 <h3 class="@contentPadding">@Model.Item.GetString("Title")</h3> 579 } 580 581 @Navigation.RenderNavigation("Navigation/Vertical.cshtml", navigationSettings) 582 </div> 583 </div> 584 <div class="g-col g-col-lg-3" data-col-size="9" style="--bs-columns:12"> 585 @gridContent 586 </div> 587 </div> 588 </div> 589 </div> 590 591 </div> 592 593 } else { 594 <div class="container"> 595 <div class="alert alert-info" role="alert">@Translate("Sorry. There is nothing to view here")</div> 596 </div> 597 } 598 599 if (!Model.IsCurrentUserAllowed) 600 { 601 int signInPage = GetPageIdByNavigationTag("SignInPage"); 602 int dashboardPage = GetPageIdByNavigationTag("MyAccountDashboardPage"); 603 604 if (!Pageview.IsVisualEditorMode) 605 { 606 if (signInPage != 0) 607 { 608 if (signInPage != Model.ID) { 609 Dynamicweb.Context.Current.Response.Redirect("/Default.aspx?ID=" + signInPage); 610 } else { 611 if (dashboardPage != 0) { 612 Dynamicweb.Context.Current.Response.Redirect("/Default.aspx?ID=" + dashboardPage); 613 } else { 614 Dynamicweb.Context.Current.Response.Redirect("/"); 615 } 616 } 617 } 618 else 619 { 620 <div class="alert alert-dark m-0" role="alert"> 621 <span>@Translate("You do not have access to this page") @Pageview.Page.ID</span> 622 </div> 623 } 624 } 625 else 626 { 627 <div class="alert alert-dark m-0" role="alert"> 628 <span>@Translate("To work on this page, you must be signed in, in the frontend")</span> 629 </div> 630 } 631 } 632 } 633 634 </main> 635 636 @if (renderAsResponsive || !renderMobile) 637 { 638 <footer class="page-footer@(responsiveClassDesktop)" id="page-footer-desktop"> 639 @if (footerDesktopLink != null) 640 { 641 @RenderGrid(footerDesktopLink.PageId) 642 } 643 </footer> 644 } 645 646 @if (renderAsResponsive || renderMobile) 647 { 648 <footer class="page-footer@(responsiveClassMobile)" id="page-footer-mobile"> 649 @if (footerMobileLink != null) 650 { 651 @RenderGrid(footerMobileLink.PageId) 652 } 653 </footer> 654 } 655 656 @* Render any offcanvas menu here *@ 657 @RenderSnippet("offcanvas") 658 659 @{ 660 bool isErpConnectionDown = !Dynamicweb.Core.Converter.ToBoolean(Context.Current.Items["IsWebServiceConnectionAvailable"]); 661 } 662 663 @* Language selector modal *@ 664 <div class="modal fade" id="PreferencesModal" tabindex="-1" aria-hidden="true"> 665 <div class="modal-dialog modal-dialog-centered modal-sm" id="PreferencesModalContent"> 666 @* The content here comes from an external request *@ 667 </div> 668 </div> 669 670 @* Favorite toast *@ 671 <div aria-live="polite" aria-atomic="true"> 672 <div class="position-fixed bottom-0 end-0 p-3" style="z-index: 11"> 673 <div id="favoriteNotificationToast" class="toast" role="alert" aria-live="assertive" aria-atomic="true"> 674 <div class="toast-header"> 675 <strong class="me-auto">@Translate("Favorite list updated")</strong> 676 <button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button> 677 </div> 678 <div class="toast-body d-flex gap-3"> 679 <div id="favoriteNotificationToast_Image"></div> 680 <div id="favoriteNotificationToast_Text"></div> 681 </div> 682 </div> 683 </div> 684 </div> 685 686 @* Modal for dynamic content *@ 687 <div class="modal fade js-product" id="DynamicModal" tabindex="-1" aria-hidden="true"> 688 <div class="modal-dialog modal-dialog-centered modal-md"> 689 <div class="modal-content theme light" id="DynamicModalContent"> 690 @* The content here comes from an external request *@ 691 </div> 692 </div> 693 </div> 694 695 @* Offcanvas for dynamic content *@ 696 <div class="offcanvas offcanvas-end theme light" tabindex="-1" id="DynamicOffcanvas"> 697 @* The content here comes from an external request *@ 698 </div> 699 700 @if (Model.Area.Item.GetBoolean("ShowErpDownMessage") && !Dynamicweb.Core.Converter.ToBoolean(Context.Current.Items["IsWebServiceConnectionAvailable"])) 701 { 702 string erpDownMessageTheme = !string.IsNullOrWhiteSpace(Model.Area.Item.GetRawValueString("ErpDownMessageTheme")) ? " theme " + Model.Area.Item.GetRawValueString("ErpDownMessageTheme").Replace(" ", "").Trim().ToLower() : "theme light"; 703 704 <div class="position-fixed bottom-0 end-0 p-3" style="z-index: 1040"> 705 <div class="toast fade show border-0 @erpDownMessageTheme" role="alert" aria-live="assertive" aria-atomic="true"> 706 <div class="toast-header"> 707 <strong class="me-auto">@Translate("Connection down")</strong> 708 <button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button> 709 </div> 710 <div class="toast-body"> 711 @Translate("We are experiencing some connectivity issues. Not all features may be available to you.") 712 </div> 713 </div> 714 </div> 715 } 716 717 @if (miniCartEnabled) 718 { 719 @* Open MiniCart when the cart is updated *@ 720 <script type="module"> 721 document.addEventListener('updated.swift.cart', (event) => { 722 let orderContext = event?.detail?.formData?.get("OrderContext"); 723 updateCartSummary(orderContext); 724 725 @if (offcanvasMiniCartBehaviour == "2" || offcanvasMiniCartBehaviour == "3") { 726 <text>openMiniCartOffcanvas();</text> 727 } 728 }); 729 </script> 730 731 if (offcanvasMiniCartBehaviour == "1" || offcanvasMiniCartBehaviour == "3") 732 { 733 @* Open MiniCart when toggle is clicked *@ 734 <script type="module"> 735 let miniCartToggles = document.querySelectorAll('.mini-cart-quantity'); 736 miniCartToggles?.forEach((toggle) => { 737 toggle.parentElement.addEventListener('click', (event) => { 738 event.preventDefault(); 739 let orderContext = toggle.dataset?.orderContext; 740 updateCartSummary(orderContext); 741 742 openMiniCartOffcanvas(); 743 }); 744 }); 745 </script> 746 } 747 748 <script> 749 750 const updateCartSummary = (orderContext) => { 751 const dynamicOffcanvas = document.getElementById('DynamicOffcanvas'); 752 swift.PageUpdater.UpdateFromUrlInline(event, '/Default.aspx?ID=@(cartSummaryPageId)&CartType=minicart&RequestPageID=@(Pageview.Page.ID)&OrderContext=' + orderContext +'', 'Swift_CartSummary.cshtml', dynamicOffcanvas); 753 }; 754 755 const openMiniCartOffcanvas = () => { 756 const dynamicOffcanvas = document.getElementById('DynamicOffcanvas'); 757 const miniCartOffcanvas = bootstrap.Offcanvas.getOrCreateInstance(dynamicOffcanvas); 758 dynamicOffcanvas.classList.add('overflow-y-auto'); 759 760 if (!miniCartOffcanvas._isShown) { 761 miniCartOffcanvas.show(); 762 hideActiveOffcanvases(miniCartOffcanvas); 763 } 764 }; 765 766 const hideActiveOffcanvases = (miniCartOffcanvas) => { 767 let activeOffcanvases = document.querySelectorAll('.offcanvas.show'); 768 activeOffcanvases?.forEach((offCanvas) => { 769 offCanvas = bootstrap.Offcanvas.getInstance(offCanvas); 770 if (offCanvas !== miniCartOffcanvas) { 771 offCanvas.hide(); 772 } 773 }); 774 }; 775 776 </script> 777 } 778 779 @* START CUSTOM CODE *@ 780 @RenderFaqModal() 781 @RenderCustomFontStyleTag() 782 @* END CUSTOM CODE *@ 783 784 785 786 @{ 787 var cookieBannerParameter = Dynamicweb.Context.Current.Request.QueryString["showcookiebanner"]; 788 string showCookieBanner = cookieBannerParameter != null ? cookieBannerParameter.ToString() : ""; 789 790 //if(showCookieBanner.Equals("true", StringComparison.OrdinalIgnoreCase)) 791 if(CookieManager.GetCookieOptInCategories().Any() && CookieManager.IsCookieManagementActive) 792 { 793 @RenderPartial($"CookieWarning/{Pageview.Area.CookieWarningTemplate}") 794 } 795 } 796 797 <button class="btn btn-secondary position-fixed bottom-0 mb-2 ms-2 bg-white" id="launchCookieModal" onclick="renderCookieModal()"> 798 @RenderIcon("/Files/Icons/cookies.svg") 799 </button> 800 </body> 801 802 </html> 803 804 } 805 else if (Pageview.IsVisualEditorMode) 806 { 807 <head> 808 <title>@Model.Title</title> 809 @* Bootstrap + Swift stylesheet *@ 810 <link href="/Files/Templates/Designs/Swift/Assets/css/styles.css" rel="stylesheet" media="all" type="text/css"> 811 </head> 812 <body class="p-3"> 813 <div class="alert alert-danger" role="alert"> 814 @Translate("Basic Swift setup is needed!") 815 </div> 816 817 @if (brandingPage == null) 818 { 819 <div class="alert alert-warning" role="alert"> 820 @Translate("Please add a Branding page and reference it in website settings") 821 </div> 822 } 823 824 @if (themesParagraphs == null) 825 { 826 <div class="alert alert-warning" role="alert"> 827 @Translate("Please add a Themes collection page and reference it in website settings") 828 </div> 829 } 830 831 832 </body> 833 } 834 835 836 @functions { 837 void SetMetaTags() 838 { 839 //Verification Tokens 840 string siteVerificationGoogle = Model.Area.Item.GetString("Google_Site_Verification") != null ? Model.Area.Item.GetString("Google_Site_Verification") : ""; 841 842 //Generic Site Values 843 string openGraphFacebookAppID = Model.Area.Item.GetString("Fb_app_id") != null ? Model.Area.Item.GetString("Fb_app_id") : ""; 844 string openGraphType = Model.Area.Item.GetString("Open_Graph_Type") != null ? Model.Area.Item.GetString("Open_Graph_Type") : ""; 845 string openGraphSiteName = Model.Area.Item.GetString("Open_Graph_Site_Name") != null ? Model.Area.Item.GetString("Open_Graph_Site_Name") : ""; 846 847 string twitterCardSite = Model.Area.Item.GetString("Twitter_Site") != null ? Model.Area.Item.GetString("Twitter_Site") : ""; 848 849 //Page specific values 850 string openGraphSiteTitle = Model.Area.Item.GetString("Open_Graph_Title") != null ? Model.Area.Item.GetString("Open_Graph_Title") : ""; 851 FileViewModel openGraphImage = Model.Area.Item.GetFile("Open_Graph_Image"); 852 string openGraphImageALT = Model.Area.Item.GetString("Open_Graph_Image_ALT") != null ? Model.Area.Item.GetString("Open_Graph_Image_ALT") : ""; 853 string openGraphDescription = Model.Area.Item.GetString("Open_Graph_Description") != null ? Model.Area.Item.GetString("Open_Graph_Description") : ""; 854 855 string twitterCardURL = Model.Area.Item.GetString("Twitter_URL") != null ? Model.Area.Item.GetString("Twitter_URL") : ""; 856 string twitterCardTitle = Model.Area.Item.GetString("Twitter_Title") != null ? Model.Area.Item.GetString("Twitter_Title") : ""; 857 string twitterCardDescription = Model.Area.Item.GetString("Twitter_Description") != null ? Model.Area.Item.GetString("Twitter_Description") : ""; 858 FileViewModel twitterCardImage = Model.Area.Item.GetFile("Twitter_Image"); 859 string twitterCardImageALT = Model.Area.Item.GetString("Twitter_Image_ALT") != null ? Model.Area.Item.GetString("Twitter_Image_ALT") : ""; 860 string topImage = Pageview.Page.TopImage.StartsWith("/Files", StringComparison.OrdinalIgnoreCase) ? Pageview.Page.TopImage : $"/Files{Pageview.Page.TopImage}"; 861 862 if (string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["ProductID"])) 863 { 864 if (!string.IsNullOrEmpty(Model.Description)) 865 { 866 Pageview.Meta.AddTag($"<meta property=\"og:description\" content=\"{Model.Description}\">"); 867 } 868 else 869 { 870 Pageview.Meta.AddTag($"<meta property=\"og:description\" content=\"{openGraphDescription}\">"); 871 } 872 873 if (!string.IsNullOrEmpty(Pageview.Page.TopImage)) 874 { 875 Pageview.Meta.AddTag($"<meta property=\"og:image\" content=\"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{topImage}\">"); 876 Pageview.Meta.AddTag($"<meta property=\"og:image:secure_url\" content=\"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{topImage}\">"); 877 } 878 else if (openGraphImage != null) 879 { 880 Pageview.Meta.AddTag($"<meta property=\"og:image\" content=\"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{openGraphImage.Path}\">"); 881 Pageview.Meta.AddTag($"<meta property=\"og:image:secure_url\" content=\"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{openGraphImage.Path}\">"); 882 } 883 884 if (!string.IsNullOrEmpty(openGraphImageALT)) 885 { 886 Pageview.Meta.AddTag($"<meta property=\"og:image:alt\" content=\"{openGraphImageALT}\">"); 887 } 888 if (!string.IsNullOrEmpty(twitterCardDescription)) 889 { 890 Pageview.Meta.AddTag("twitter:description", twitterCardDescription); 891 } 892 893 if (!string.IsNullOrEmpty(Pageview.Page.TopImage)) 894 { 895 Pageview.Meta.AddTag("twitter:image", $"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{topImage}"); 896 } 897 else if (twitterCardImage != null) 898 { 899 Pageview.Meta.AddTag("twitter:image", $"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{openGraphImage.Path}"); 900 } 901 902 if (!string.IsNullOrEmpty(twitterCardImageALT)) 903 { 904 Pageview.Meta.AddTag("twitter:image:alt", twitterCardImageALT); 905 } 906 } 907 908 if (!string.IsNullOrEmpty(siteVerificationGoogle)) 909 { 910 Pageview.Meta.AddTag("google-site-verification", siteVerificationGoogle); 911 } 912 913 if (!string.IsNullOrEmpty(openGraphFacebookAppID)) 914 { 915 Pageview.Meta.AddTag($"<meta property=\"fb:app_id\" content=\"{openGraphFacebookAppID}\">"); 916 } 917 918 if (!string.IsNullOrEmpty(openGraphType)) 919 { 920 Pageview.Meta.AddTag($"<meta property=\"og:type\" content=\"{openGraphType}\">"); 921 } 922 923 if (!string.IsNullOrEmpty(openGraphSiteName)) 924 { 925 Pageview.Meta.AddTag($"<meta property=\"og:url\" content=\"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{Pageview.SearchFriendlyUrl}\">"); 926 } 927 928 if (!string.IsNullOrEmpty(openGraphSiteName)) 929 { 930 Pageview.Meta.AddTag($"<meta property=\"og:site_name\" content=\"{openGraphSiteName}\">"); 931 } 932 933 if (!string.IsNullOrEmpty(Model.Title)) 934 { 935 Pageview.Meta.AddTag($"<meta property=\"og:title\" content=\"{Model.Title}\">"); 936 } 937 else 938 { 939 Pageview.Meta.AddTag($"<meta property=\"og:title\" content=\"{openGraphSiteTitle}\">"); 940 } 941 942 if (!string.IsNullOrEmpty(twitterCardSite)) 943 { 944 Pageview.Meta.AddTag("twitter:site", twitterCardSite); 945 } 946 947 if (!string.IsNullOrEmpty(twitterCardURL)) 948 { 949 Pageview.Meta.AddTag("twitter:url", twitterCardURL); 950 } 951 952 if (!string.IsNullOrEmpty(twitterCardTitle)) 953 { 954 Pageview.Meta.AddTag("twitter:title", twitterCardTitle); 955 } 956 } 957 } 958 @* START CUSTOM CODE *@ 959 @helper RenderFaqModal() 960 { 961 var faqPage = Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl(GetPageIdByNavigationTag("FAQ")); 962 <div class="modal modal-xl faq-modal" aria-hidden="true" data-url="@faqPage"> 963 <div class="modal-dialog"> 964 <div class="modal-content"> 965 <div class="modal-header"> 966 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 967 </div> 968 <div class="modal-body p-0"></div> 969 </div> 970 </div> 971 </div> 972 } 973 @helper RenderCustomFontStyleTag() 974 { 975 <style> 976 @@font-face { 977 font-family: 'Arsilon'; 978 font-style: normal; 979 font-weight: 400; 980 font-display: swap; 981 src: url(/Files/Templates/Designs/Swift/Assets/fonts/Arsilon.woff2) format('woff2'); 982 } 983 984 985 span.special { 986 font-family:'Arsilon'; 987 font-weight:400; 988 font-style:normal; 989 line-height:1.5; 990 font-size:72px; 991 } 992 </style> 993 } 994 @helper RenderIcon(string icon) 995 { 996 if (System.IO.Path.GetExtension(icon).ToLower() == ".svg") 997 { 998 if (!icon.ToLower().Contains("none") && icon != string.Empty) 999 { 1000 <span class="icon-auto"> 1001 @ReadFile(icon) 1002 </span> 1003 } 1004 } 1005 } 1006 @* END CUSTOM CODE *@