Error executing template "Designs/Swift/_parsed/Swift_Page.parsed.cshtml"
System.Data.SqlClient.SqlException (0x80131904): Login failed for user 'dansukker'. Reason: Server is in single user mode. Only one administrator can connect at this time.
   at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)
   at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal& connection)
   at System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection)
   at System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
   at System.Data.SqlClient.SqlConnection.TryOpenInner(TaskCompletionSource`1 retry)
   at System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)
   at System.Data.SqlClient.SqlConnection.Open()
   at Dynamicweb.Data.DatabaseConnectionProvider.CreateConnection(Boolean open)
   at Dynamicweb.Data.Database.CreateConnection()
   at Dynamicweb.Data.Database.CreateDataReader(CommandBuilder commandBuilder, IDbConnection connection, IDbTransaction transaction, Int32 commandTimeout)
   at Dynamicweb.Ecommerce.Products.ProductRepository.GetProductById(String productId, String productVariantId, String productLanguageId)
   at Dynamicweb.Ecommerce.Products.ProductService.FetchMissingProductsInternal(IProductRepository repo, IEnumerable`1 keys)
   at Dynamicweb.Caching.ServiceCache`2.GetCache(IEnumerable`1 keys)
   at Dynamicweb.Caching.ServiceCache`2.GetCache(TKey key)
   at Dynamicweb.Ecommerce.Products.ProductService.GetProductById(String productId, String productVariantId, String productLanguageId, User user, Boolean showUntranslated)
   at Dynamicweb.Ecommerce.Products.ProductService.GetProductById(String productId, String productVariantId, String productLanguageId, Boolean useAssortments)
   at Dynamicweb.Ecommerce.Products.ProductService.GetProductById(String productId, String productVariantId, String productLanguageId)
   at CompiledRazorTemplates.Dynamic.RazorEngine_b9e731468ed3460a811f4bc19f73fda4.Execute() in E:\Dynamicweb.Net\Solutions\Dynamicweb\dansukker.prod\Swift-Files\Templates\Designs\Swift\_parsed\Swift_Page.parsed.cshtml:line 165
   at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader)
   at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer)
   at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template)
   at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template)
   at Dynamicweb.Rendering.Template.RenderRazorTemplate()
ClientConnectionId:fae4d84e-9a76-4712-864b-23b00a16cfe6
Error Number:18461,State:1,Class:14

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 8 @{ 9 string swiftVersion = ReadFile("~/Files/Templates/Designs/Swift/swift_version.txt"); 10 bool renderAsResponsive = Model.Area.Item.GetString("DeviceRendering", "responsive").Equals("responsive", StringComparison.OrdinalIgnoreCase); 11 bool renderMobile = Pageview.Device == Dynamicweb.Frontend.Devices.DeviceType.Mobile || Pageview.Device == Dynamicweb.Frontend.Devices.DeviceType.Tablet; 12 string responsiveHeaderClassDesktop = string.Empty; 13 string responsiveHeaderClassMobile = string.Empty; 14 if (renderAsResponsive) 15 { 16 responsiveHeaderClassDesktop = " d-none d-xl-block"; 17 responsiveHeaderClassMobile = " d-block d-xl-none"; 18 19 } 20 21 var brandingPageId = Model.Area.Item.GetLink("BrandingPage") != null ? Model.Area.Item.GetLink("BrandingPage").PageId : 0; 22 var themePageId = Model.Area.Item.GetLink("ThemesPage") != null ? Model.Area.Item.GetLink("ThemesPage").PageId : 0; 23 string customHeaderInclude = Model.Area.Item.GetFile("CustomHeaderInclude") != null ? Model.Area.Item.GetFile("CustomHeaderInclude").Name : string.Empty; 24 25 var brandingPage = Dynamicweb.Services.Pages?.GetPage(brandingPageId) ?? null; 26 var themesParagraphLastChanged = Services.Paragraphs.GetParagraphsByPageId(themePageId).OrderByDescending(p => p.Audit.LastModifiedAt).FirstOrDefault(); 27 28 var cssLastModified = brandingPage.Audit.LastModifiedAt > themesParagraphLastChanged.Audit.LastModifiedAt ? brandingPage.Audit.LastModifiedAt : themesParagraphLastChanged.Audit.LastModifiedAt; 29 var cssThemeAndBrandingStyleFileInfo = new System.IO.FileInfo(Dynamicweb.Core.SystemInformation.MapPath($"~/Files/Templates/Designs/Swift/_parsed/Swift_css/Swift_styles_{Model.Area.ID}.min.css")); 30 31 // Schema.org details for PDP 32 string productId = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("ProductID")) ? Dynamicweb.Context.Current.Request.QueryString.Get("ProductID") : ""; 33 bool isProductDetailsPage = !string.IsNullOrEmpty(productId); 34 string schema = Pageview.Page.NavigationTag == "Recipes" ? "Recipe" : "Product"; // Set specific schema for recipe-products pages 35 string productDetailPageSchemaOrg = isProductDetailsPage ? "itemscope=\"\" itemtype=\"https://schema.org/"+ schema + "\"" : ""; 36 37 if (!cssThemeAndBrandingStyleFileInfo.Exists || cssThemeAndBrandingStyleFileInfo.LastWriteTime < brandingPage.Audit.LastModifiedAt) 38 { 39 //Branding page has been saved or the file is missing. Rewrite the file to disc. 40 if (brandingPageId > 0) 41 { 42 var brandingPageview = Dynamicweb.Frontend.PageView.GetPageviewByPageID(brandingPageId); 43 brandingPageview.Redirect = false; 44 brandingPageview.Output(); 45 } 46 } 47 48 49 if (!cssThemeAndBrandingStyleFileInfo.Exists || cssThemeAndBrandingStyleFileInfo.LastWriteTime < themesParagraphLastChanged.Audit.LastModifiedAt) 50 { 51 //Branding page has been saved or the file is missing. Rewrite the file to disc. 52 if (themePageId > 0) 53 { 54 var themePageview = Dynamicweb.Frontend.PageView.GetPageviewByPageID(themePageId); 55 themePageview.Redirect = false; 56 themePageview.Output(); 57 } 58 } 59 60 var cssStyleFileInfo = new System.IO.FileInfo(Dynamicweb.Core.SystemInformation.MapPath("~/Files/Templates/Designs/Swift/Assets/css/styles.css")); 61 var jsFileInfo = new System.IO.FileInfo(Dynamicweb.Core.SystemInformation.MapPath("~/Files/Templates/Designs/Swift/Assets/js/scripts.js")); 62 63 string masterTheme = !string.IsNullOrWhiteSpace(Model.Area.Item.GetRawValueString("Theme")) ? " theme " + Model.Area.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 64 65 string headerCssClass = "sticky-top"; 66 bool movePageBehind = false; 67 string scrollInitScript = "swift.Scroll.init(); AOS.init({offset: 240, duration: 600, delay: 100}); disable: window.matchMedia('(prefers-reduced-motion: reduce)');"; 68 if (Pageview.Page.PropertyItem != null) 69 { 70 headerCssClass = Pageview.Page.PropertyItem["MoveThisPageBehindTheHeader"] != null ? Pageview.Page.PropertyItem["MoveThisPageBehindTheHeader"].ToString() : "sticky-top"; 71 movePageBehind = headerCssClass == "fixed-top" && !Pageview.IsVisualEditorMode ? true : false; 72 } 73 headerCssClass = headerCssClass == "" ? "sticky-top" : headerCssClass; 74 headerCssClass = Pageview.IsVisualEditorMode ? "" : headerCssClass; 75 76 string googleAnalyticsTrackingID = Model.Area.Item.GetString("GoogleAnalyticsTrackingID"); 77 string etrackerDataSecureCode = Model.Area.Item.GetString("EtrakerDataSecureCode"); 78 bool allowCookies = CookieManager.GetCookieOptInLevel() == 0 || !CookieManager.CookieOptInLevelExists ? false : true; 79 string cookieDeclarationKey = Model.Area.Item.GetString("CookieDeclarationKey"); 80 81 string gtmLanguage = Pageview.Area.CultureInfo.TwoLetterISOLanguageName; 82 bool showGtm = gtmLanguage == "sv" || gtmLanguage == "da" || gtmLanguage == "fi"; 83 84 Dynamicweb.Context.Current.Response.AddHeader("link", $"</Files/Templates/Designs/Swift/Assets/css/styles.css?{cssStyleFileInfo.LastWriteTime.Ticks}>; rel=preload; as=style;"); 85 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;"); 86 Dynamicweb.Context.Current.Response.AddHeader("link", $"</Files/Templates/Designs/Swift/Assets/js/aos.js?{jsFileInfo.LastWriteTime.Ticks}; rel=preload; as=script;"); 87 Dynamicweb.Context.Current.Response.AddHeader("link", $"</Files/Templates/Designs/Swift/Assets/js/scripts.js?{jsFileInfo.LastWriteTime.Ticks}; rel=preload; as=script;"); 88 //Dynamicweb.Context.Current.Response.Flush(); //This sends the headers where we are now in the rendering making the TTFB faster 89 90 SetMetaTags(); 91 92 List<Dynamicweb.Content.Page> languages = new List<Dynamicweb.Content.Page>(); 93 94 if (Pageview.Area.IsMaster) { 95 languages.Add(Pageview.Page); 96 if (Pageview.Page.Languages != null) { 97 foreach (var language in Pageview.Page.Languages) { 98 languages.Add(language); 99 } 100 } 101 } else { 102 languages.Add(Pageview.Page.MasterPage); 103 if (Pageview.Page.MasterPage != null) { 104 if (Pageview.Page.MasterPage.Languages != null) { 105 foreach (var language in Pageview.Page.MasterPage.Languages) { 106 languages.Add(language); 107 } 108 } 109 } 110 } 111 112 string siteLanguage = Pageview.Area.CultureInfo.Name; 113 Uri url = Dynamicweb.Context.Current.Request.Url; 114 string hostName = url.Host; // domain.com/da-dk or domain.com/en-us 115 116 var ecomCountries = Dynamicweb.Ecommerce.Services.Countries.GetCountries(); 117 var ecomCurrencies = Dynamicweb.Ecommerce.Services.Currencies.GetAllCurrencies(); 118 119 string googleSiteVerification = Model.Area.Item.GetString("GoogleSiteVerification"); 120 } 121 <!doctype html> 122 <html lang="@Pageview.Area.CultureInfo.TwoLetterISOLanguageName"> 123 <head> 124 @if (showGtm) 125 { 126 <!-- Google Tag Manager --> 127 <script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': 128 new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], 129 j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= 130 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f); 131 })(window,document,'script','dataLayer','GTM-TMPJLQW9');</script> 132 <!-- End Google Tag Manager --> 133 } 134 <!-- @swiftVersion --> 135 @* Required meta tags *@ 136 <meta charset="utf-8"> 137 <meta name="viewport" content="height=device-height, width=device-width, initial-scale=1.0"> 138 139 @if(!string.IsNullOrEmpty(googleSiteVerification)) 140 { 141 <meta name="google-site-verification" content="@googleSiteVerification" /> 142 } 143 144 <link rel="preconnect" href="https://fonts.gstatic.com" /> 145 146 <link rel="shortcut icon" href="~/Files/Templates/Designs/Swift/Assets/Images/favicon.png"> 147 <link rel="apple-touch-icon" href="~/Files/Templates/Designs/Swift/Assets/Images/apple-touch-icon.png"> 148 <link rel="apple-touch-icon" sizes="152x152" href="~/Files/Templates/Designs/Swift/Assets/Images/apple-touch-icon-152x152.png"> 149 <link rel="apple-touch-icon" sizes="180x180" href="~/Files/Templates/Designs/Swift/Assets/Images/apple-touch-icon-180x180.png"> 150 <link rel="apple-touch-icon" sizes="114x114" href="~/Files/Templates/Designs/Swift/Assets/Images/apple-touch-icon-114x114.png"> 151 <link rel="apple-touch-icon" sizes="120x120" href="~/Files/Templates/Designs/Swift/Assets/Images/apple-touch-icon-120x120.png"> 152 <link rel="apple-touch-icon" sizes="144x144" href="~/Files/Templates/Designs/Swift/Assets/Images/apple-touch-icon-144x144.png"> 153 154 155 @{ 156 // 1. Hämta SEO-sökvägen från Pageview 157 string seoPath = Pageview.SearchFriendlyUrl; 158 159 // Recept och produkter: canonical utan kategori i sökvägen (#361). Samma recept nås även via 160 // /se/recept/godis/kokostryffel, som annars blev sin egen canonical bredvid /se/recept/kokostryffel. 161 // Länken byggs som i sitemapen: sidan + ProductID (basprodukten, utan GroupID och VariantID), med webbplatsens ecom-språk. 162 // Bara när produkten finns, så att en ProductID-parameter på en annan sida inte ger en produkt-canonical. 163 string canonicalProductId = Dynamicweb.Context.Current.Request.QueryString["ProductID"]; 164 string canonicalLanguageId = Pageview.Area.EcomLanguageId; 165 if (!string.IsNullOrEmpty(canonicalProductId) 166 && Dynamicweb.Ecommerce.Services.Products.GetProductById(canonicalProductId, "", canonicalLanguageId) != null) 167 { 168 string productLink = $"Default.aspx?ID={Pageview.ID}&ProductID={HttpUtility.UrlEncode(canonicalProductId)}"; 169 string productUrl = Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl(productLink, canonicalLanguageId); 170 if (!string.IsNullOrEmpty(productUrl) && productUrl.IndexOf("Default.aspx", StringComparison.OrdinalIgnoreCase) < 0 && !productUrl.Contains("?")) 171 { 172 seoPath = productUrl; 173 } 174 } 175 176 // Fallback om Pageview är tom 177 if (string.IsNullOrEmpty(seoPath) || seoPath == "/") 178 { 179 seoPath = Dynamicweb.Context.Current.Request.Url.AbsolutePath; 180 } 181 182 // 2. Rensa bort query-parametrar (?GroupFacetID...) direkt om de finns kvar 183 if (seoPath.Contains("?")) 184 { 185 seoPath = seoPath.Split('?')[0]; 186 } 187 188 // 3. Kontrollera och städa bort duplicerade slutsegment (t.ex. /dessert/dessert) 189 // Vi delar upp sökvägen i bitar baserat på "/" 190 var segments = seoPath.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); 191 if (segments.Length > 1 && segments[segments.Length - 1].Equals(segments[segments.Length - 2], StringComparison.OrdinalIgnoreCase)) 192 { 193 // Om de två sista segmenten är identiska, ta bort det sista och bygg ihop sökvägen igen 194 seoPath = "/" + string.Join("/", segments.Take(segments.Length - 1)); 195 } 196 197 // 4. Bygg ihop till den slutgiltiga absoluta URL:en 198 string finalCanonicalUrl = seoPath.StartsWith("http", StringComparison.OrdinalIgnoreCase) 199 ? seoPath 200 : $"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{seoPath}"; 201 } 202 <link rel="canonical" href="@finalCanonicalUrl" /> 203 204 @Model.MetaTags 205 206 @{ 207 @* Languages meta data *@ 208 foreach (var language in languages) 209 { 210 if (language != null) { 211 if (!string.IsNullOrEmpty(language.Area.DomainLock)) 212 { 213 hostName = language.Area.DomainLock; //dk.domain.com or dk-domain.dk 214 } 215 string friendlyUrl = Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl(language.ID); 216 string href = $"{url.Scheme}://{hostName}{friendlyUrl}"; 217 218 <link rel="alternate" hreflang="@language.Area.CultureInfo.Name.ToLower()" href="@href" /> 219 } 220 } 221 } 222 223 <title>@Model.Title</title> 224 @* Bootstrap + Swift stylesheet *@ 225 <link href="~/Files/Templates/Designs/Swift/Assets/css/styles.css?@cssStyleFileInfo.LastWriteTime.Ticks" rel="stylesheet" media="all" type="text/css"> 226 @* Branding and Themes min stylesheet *@ 227 <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"> 228 229 @* cookie Consent - Cookiebot runs from the CMS on all sites, also SE/DK/FI that have GTM (must not be added as a tag in GTM too) *@ 230 @if (!string.IsNullOrWhiteSpace(cookieDeclarationKey)) 231 { 232 <script> 233 (function (C, oo, k, ie, b, o, t) { 234 var a=C.scripts[0],d=C.createElement(ie),e=[/^\/([a-z]{2})([_-][a-z]{2})?(\/.*)?$/g. 235 exec(k),/^([a-z]{2})\./.exec(oo),/\.([a-z]{2,})$/.exec(oo)],f={au:"en",ca:"en",cz: 236 "cs",dk:"da",ee:"et",no:"nb",se:"sv",ie:"en",uk:"en"},g=0;for(g of e){e=g?g[1]:t;if(g)break} 237 e=e.length!=2?t:e;e=f[e]?f[e]:e;d.id=b;d.setAttribute("data-culture",e);d.src= 238 "https://consent.cookiebot.com/uc.js?cbid="+o;d.async=!0;a.parentNode.insertBefore( 239 d, a)})(document,location.hostname,location.pathname,"script","Cookiebot","@cookieDeclarationKey", "en") 240 </script> 241 } 242 243 <style type="text/css"> 244 a#CybotCookiebotDialogPoweredbyCybot, 245 div#CybotCookiebotDialogPoweredByText { 246 display: none; 247 } 248 249 #CookiebotWidget .CookiebotWidget-body .CookiebotWidget-main-logo { 250 display: none; 251 } 252 </style> 253 254 <script src="~/Files/Templates/Designs/Swift/Assets/js/aos.js?@jsFileInfo.LastWriteTime.Ticks" defer></script> 255 <script src="~/Files/Templates/Designs/Swift/Assets/js/scripts.js?@jsFileInfo.LastWriteTime.Ticks" defer></script> 256 257 258 @* Global site tag (gtag.js) - Google Analytics *@ 259 @if (!string.IsNullOrWhiteSpace(googleAnalyticsTrackingID) && allowCookies) 260 { 261 <script src="https://www.googletagmanager.com/gtag/js?id=@googleAnalyticsTrackingID" async></script> 262 <script>@googleAnalyticsTrackingID</script> 263 } 264 265 @* Etracker *@ 266 @if (!string.IsNullOrWhiteSpace(etrackerDataSecureCode)) 267 { 268 <!-- .SE --> 269 <!-- Copyright (c) 2000-2022 etracker GmbH. All rights reserved. --> 270 <!-- This material may not be reproduced, displayed, modified or distributed --> 271 <!-- without the express prior written permission of the copyright holder. --> 272 <!-- etracker tracklet 5.0 --> 273 <script type="text/javascript"> 274 // var et_pagename = ""; 275 // var et_areas = ""; 276 // var et_tval = 0; 277 // var et_tsale = 0; 278 // var et_tonr = ""; 279 // var et_basket = ""; 280 </script> 281 <script id="_etLoader" type="text/javascript" charset="UTF-8" data-block-cookies="true" data-respect-dnt="true" data-secure-code="@etrackerDataSecureCode" src="//code.etracker.com/code/e.js" async></script> 282 <!-- etracker tracklet 5.0 end --> 283 } 284 285 @if (!string.IsNullOrWhiteSpace(customHeaderInclude)) 286 { 287 @RenderPartial($"Components/Custom/{customHeaderInclude}") 288 } 289 290 @{ 291 // Set a body class based on Page.NavigationTag for targeted UX customizations 292 string bodyClass = Pageview.Page.NavigationTag; 293 if (!Dynamicweb.Context.Current.Items.Contains("ProductList")) 294 { 295 bodyClass = bodyClass + "Page"; 296 } 297 } 298 299 </head> 300 <body class="brand @(masterTheme) @bodyClass" id="page@(Model.ID)" onload="@scrollInitScript"> 301 @if (showGtm) 302 { 303 <!-- Google Tag Manager (noscript) --> 304 <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-TMPJLQW9" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript> 305 <!-- End Google Tag Manager (noscript) --> 306 } 307 @if (renderAsResponsive || !renderMobile) 308 { 309 <header class="page-header @headerCssClass top-0@(responsiveHeaderClassDesktop)" id="page-header-desktop"> 310 @if (@Model.Area.Item.GetLink("HeaderDesktop") != null) 311 { 312 @RenderGrid(@Model.Area.Item.GetLink("HeaderDesktop").PageId) 313 } 314 </header> 315 } 316 317 @if ((renderAsResponsive || renderMobile)) 318 { 319 <header class="page-header @headerCssClass top-0@(responsiveHeaderClassMobile)" id="page-header-mobile"> 320 @if (@Model.Area.Item.GetLink("HeaderMobile") != null) 321 { 322 @RenderGrid(@Model.Area.Item.GetLink("HeaderMobile").PageId) 323 } 324 </header> 325 } 326 327 <main id="content" @productDetailPageSchemaOrg> 328 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.PageViewModel> 329 @using Dynamicweb.Ecommerce.ProductCatalog 330 @using System 331 @using Dynamicweb.Extensibility; 332 @using Dynamicweb.Indexing.Querying; 333 @using CustomUtilities 334 335 336 337 @{ 338 string theme = ""; 339 string gridContent = ""; 340 341 if (Model.PropertyItem != null) 342 { 343 theme = !string.IsNullOrWhiteSpace(Model.PropertyItem.GetRawValueString("Theme")) ? " theme " + Model.PropertyItem.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 344 } 345 346 if (Model.Item != null) 347 { 348 gridContent = Model.Grid("Grid", "Grid", "default:true;sort:1", "Page"); 349 } 350 351 bool doNotRenderPage = false; 352 353 //Check if we are on the poduct detail page, and if there is data to render 354 ProductViewModel product = new ProductViewModel(); 355 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 356 { 357 358 359 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 360 if (string.IsNullOrEmpty(product.Id)) 361 { 362 doNotRenderPage = true; 363 } 364 365 var currentProduct = Dynamicweb.Ecommerce.Services.Products.GetProductById(product.Id, null, product.LanguageId); 366 int pageLoadPeriod = Pageview.AreaSettings.GetInt32("PageLoadPeriod"); 367 368 if (pageLoadPeriod == 0 && Model.Area.Item != null) 369 { 370 var websiteSettings = Dynamicweb.Services.Items.GetItem(Model.Area.ItemType, Model.Area.ItemId); 371 websiteSettings["PageLoadPeriod"] = 30; 372 Dynamicweb.Services.Items.SaveItem(websiteSettings); 373 374 pageLoadPeriod = 30; 375 } 376 377 378 // Custom 404 logic for product catalog based pages 379 // because there is not separate product cataloges per site and all information is tagged as Danish 380 if (!currentProduct.Active && !Pageview.IsVisualEditorMode) 381 { 382 HttpContext.Current.Response.Redirect("/404/", true); 383 } 384 385 // The "All" index has information about which site each product catalog based pages belongs to in the field "WebsiteID" 386 // The query matches WebsiteID = Pageview.Context.WebsiteID and returns only records relevant for current síte 387 var queryService = ServiceLocator.Current.GetInstance<IQueryService>(); 388 var query = queryService.LoadQuery("All", "QueryAll"); 389 390 if (query == null) 391 { 392 throw new MissingMethodException("Specified query not found."); 393 } 394 395 IDictionary<string, object> parameters = new Dictionary<string, object>(); 396 parameters.Add("docID", product.Id); 397 var settings = new QuerySettings 398 { 399 Skip = 0, 400 Take = 10, 401 Parameters = parameters 402 }; 403 404 var result = queryService.Query(query, settings); 405 406 var autoIds = new List<long>(); 407 foreach (Dictionary<string, object> document in result.QueryResult) 408 { 409 if (document.ContainsKey("AutoID")) 410 { 411 var productAutoId = Convert.ToInt64(document["AutoID"]); 412 autoIds.Add(productAutoId); 413 } 414 } 415 var products = Dynamicweb.Ecommerce.Services.Products.GetByAutoIDs(autoIds); 416 417 // 404 redirect the product page if it did not belong to the current website accoring to the All index 418 if (products.Count() == 0 && !Pageview.IsVisualEditorMode) 419 { 420 HttpContext.Current.Response.Redirect("/404/", true); 421 } 422 423 424 Helpers helper = new Helpers(); 425 426 helper.CleanPageLoadsJsonData(currentProduct, pageLoadPeriod); 427 helper.UpdatePageLoadsJsonData(currentProduct); 428 } 429 430 //Render the page 431 if (!doNotRenderPage) 432 { 433 if (theme != "") 434 { 435 <div class="@theme"> 436 @gridContent 437 </div> 438 } 439 else 440 { 441 @gridContent 442 } 443 } 444 else 445 { 446 <div class="container"> 447 <div class="alert alert-info" role="alert">@Translate("Sorry. There is nothing to view here")</div> 448 </div> 449 } 450 451 if (!Model.IsCurrentUserAllowed) 452 { 453 int signInPage = GetPageIdByNavigationTag("SignInPage"); 454 int dashboardPage = GetPageIdByNavigationTag("MyAccountDashboardPage"); 455 456 if (!Pageview.IsVisualEditorMode) 457 { 458 if (signInPage != 0) 459 { 460 if (signInPage != Model.ID) 461 { 462 Dynamicweb.Context.Current.Response.Redirect("/Default.aspx?ID=" + signInPage); 463 } 464 else 465 { 466 if (dashboardPage != 0) 467 { 468 Dynamicweb.Context.Current.Response.Redirect("/Default.aspx?ID=" + dashboardPage); 469 } 470 else 471 { 472 Dynamicweb.Context.Current.Response.Redirect("/"); 473 } 474 } 475 } 476 else 477 { 478 <div class="alert alert-dark m-0" role="alert"> 479 <span>@Translate("You do not have access to this page")</span> 480 </div> 481 } 482 } 483 else 484 { 485 <div class="alert alert-dark m-0" role="alert"> 486 <span>@Translate("To work on this page, you must be signed in, in the frontend")</span> 487 </div> 488 } 489 } 490 } 491 492 </main> 493 494 @if (renderAsResponsive || !renderMobile) 495 { 496 <footer class="page-footer d-none d-lg-block" id="page-footer-desktop"> 497 @if (@Model.Area.Item.GetLink("FooterDesktop") != null) 498 { 499 @RenderGrid(@Model.Area.Item.GetLink("FooterDesktop").PageId) 500 } 501 </footer> 502 } 503 504 @if (renderAsResponsive || renderMobile) 505 { 506 <footer class="page-footer d-block d-lg-none" id="page-footer-mobile"> 507 @if (@Model.Area.Item.GetLink("FooterMobile") != null) 508 { 509 @RenderGrid(@Model.Area.Item.GetLink("FooterMobile").PageId) 510 } 511 </footer> 512 } 513 514 @* Render any offcanvas menu here *@ 515 @RenderSnippet("offcanvas") 516 517 @* Language selector modal *@ 518 @if (languages.Count > 1 || ecomCountries.Count > 1 || ecomCurrencies.Count() > 1) 519 { 520 <div class="modal fade" id="PreferencesModal" tabindex="-1" aria-hidden="true"> 521 <div class="modal-dialog modal-dialog-centered modal-sm" id="PreferencesModalContent"> 522 @* The content here comes from an external request *@ 523 </div> 524 </div> 525 } 526 527 @* Favorite toast *@ 528 <div aria-live="polite" aria-atomic="true"> 529 <div class="position-fixed bottom-0 end-0 p-3" style="z-index: 11"> 530 <div id="favoriteNotificationToast" class="toast" role="alert" aria-live="assertive" aria-atomic="true"> 531 <div class="toast-header"> 532 <strong class="me-auto">@Translate("Favorite list updated")</strong> 533 <button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button> 534 </div> 535 <div class="toast-body d-flex gap-3"> 536 <div id="favoriteNotificationToast_Image"></div> 537 <div id="favoriteNotificationToast_Text"></div> 538 </div> 539 </div> 540 </div> 541 </div> 542 543 @* Offcanvas for dynamic content *@ 544 <div class="offcanvas offcanvas-end theme light" tabindex="-1" id="DynamicOffcanvas" style="width: 30rem"> 545 @* The content here comes from an external request *@ 546 </div> 547 548 <script src="https://code.jquery.com/jquery-2.1.4.min.js"></script> 549 550 <script type="text/javascript"> 551 552 window.addEventListener('CookiebotOnDecline', function (e) { 553 if (Cookiebot.declined) 554 { 555 setOptInCookie(0); 556 } 557 }, false); 558 559 window.addEventListener('CookiebotOnAccept', function (e) { 560 if (Cookiebot.consented) 561 { 562 setOptInCookie(2); 563 } 564 }, false); 565 566 async function setOptInCookie(optInLevel) { 567 let response = await fetch("/admin/public/CookieOptInLevelConfig.aspx?cmd=SetCookieOptInLevel&OptInLevel=" + optInLevel); 568 } 569 570 </script> 571 572 </body> 573 </html> 574 575 @functions { 576 577 void SetMetaTags() 578 { 579 //Verification Tokens 580 string siteVerificationGoogle = Model.Area.Item.GetString("Google_Site_Verification") != null ? Model.Area.Item.GetString("Google_Site_Verification") : ""; 581 //string siteVerificationYandex = Model.Area.Item.GetString("Yandex_Verification") != null ? Model.Area.Item.GetString("Yandex_Verification") : ""; 582 //string siteVerificationMS = Model.Area.Item.GetString("Msvalidate_01") != null ? Model.Area.Item.GetString("Msvalidate_01") : ""; 583 //string siteVerificationAlexa = Model.Area.Item.GetString("AlexaVerifyID") != null ? Model.Area.Item.GetString("AlexaVerifyID") : ""; 584 //string siteVerificationPinterest = Model.Area.Item.GetString("P_domain_verify") != null ? Model.Area.Item.GetString("P_domain_verify") : ""; 585 //string siteVerificationNorton = Model.Area.Item.GetString("Norton_safeweb_site_verification") != null ? Model.Area.Item.GetString("Norton_safeweb_site_verification") : ""; 586 587 //Generic Site Values 588 string openGraphFacebookAppID = Model.Area.Item.GetString("Fb_app_id") != null ? Model.Area.Item.GetString("Fb_app_id") : ""; 589 string openGraphType = Model.Area.Item.GetString("Open_Graph_Type") != null ? Model.Area.Item.GetString("Open_Graph_Type") : ""; 590 string openGraphSiteName = Model.Area.Item.GetString("Open_Graph_Site_Name") != null ? Model.Area.Item.GetString("Open_Graph_Site_Name") : ""; 591 592 string twitterCardSite = Model.Area.Item.GetString("Twitter_Site") != null ? Model.Area.Item.GetString("Twitter_Site") : ""; 593 594 //Page specific values 595 string openGraphSiteTitle = Model.Area.Item.GetString("Open_Graph_Title") != null ? Model.Area.Item.GetString("Open_Graph_Title") : ""; 596 FileViewModel openGraphImage = Model.Area.Item.GetFile("Open_Graph_Image"); 597 string openGraphImageALT = Model.Area.Item.GetString("Open_Graph_Image_ALT") != null ? Model.Area.Item.GetString("Open_Graph_Image_ALT") : ""; 598 string openGraphDescription = Model.Area.Item.GetString("Open_Graph_Description") != null ? Model.Area.Item.GetString("Open_Graph_Description") : ""; 599 600 string twitterCardURL = Model.Area.Item.GetString("Twitter_URL") != null ? Model.Area.Item.GetString("Twitter_URL") : ""; 601 string twitterCardTitle = Model.Area.Item.GetString("Twitter_Title") != null ? Model.Area.Item.GetString("Twitter_Title") : ""; 602 string twitterCardDescription = Model.Area.Item.GetString("Twitter_Description") != null ? Model.Area.Item.GetString("Twitter_Description") : ""; 603 FileViewModel twitterCardImage = Model.Area.Item.GetFile("Twitter_Image"); 604 string twitterCardImageALT = Model.Area.Item.GetString("Twitter_Image_ALT") != null ? Model.Area.Item.GetString("Twitter_Image_ALT") : ""; 605 606 if (!string.IsNullOrEmpty(siteVerificationGoogle)) 607 { 608 Pageview.Meta.AddTag("google-site-verification", siteVerificationGoogle); 609 } 610 611 if (!string.IsNullOrEmpty(openGraphFacebookAppID)) 612 { 613 Pageview.Meta.AddTag("fb:app_id", openGraphFacebookAppID); 614 } 615 616 if (!string.IsNullOrEmpty(openGraphType)) 617 { 618 Pageview.Meta.AddTag("og:type", openGraphType); 619 } 620 621 if (!string.IsNullOrEmpty(openGraphSiteName)) 622 { 623 Pageview.Meta.AddTag("og:site_name", openGraphSiteName); 624 } 625 626 if (!string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["GroupID"]) && !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["ProductID"])) 627 { 628 if (!string.IsNullOrEmpty(Model.Title)) 629 { 630 Pageview.Meta.AddTag("og:title", Model.Title); 631 } 632 else 633 { 634 Pageview.Meta.AddTag("og:title", openGraphSiteTitle); 635 } 636 if (!string.IsNullOrEmpty(Model.Description)) 637 { 638 Pageview.Meta.AddTag("og:description", Model.Description); 639 } 640 else 641 { 642 Pageview.Meta.AddTag("og:description", openGraphDescription); 643 } 644 } 645 646 if (openGraphImage != null) 647 { 648 Pageview.Meta.AddTag("og:image", openGraphImage.Path); 649 } 650 651 if (!string.IsNullOrEmpty(openGraphImageALT)) 652 { 653 Pageview.Meta.AddTag("og:image:alt", openGraphImageALT); 654 } 655 656 if (!string.IsNullOrEmpty(twitterCardSite)) 657 { 658 Pageview.Meta.AddTag("twitter:site", twitterCardSite); 659 } 660 661 if (!string.IsNullOrEmpty(twitterCardURL)) 662 { 663 Pageview.Meta.AddTag("twitter:url", twitterCardURL); 664 } 665 666 if (!string.IsNullOrEmpty(twitterCardTitle)) 667 { 668 Pageview.Meta.AddTag("twitter:title", twitterCardTitle); 669 } 670 671 if (!string.IsNullOrEmpty(twitterCardDescription)) 672 { 673 Pageview.Meta.AddTag("twitter:description", twitterCardDescription); 674 } 675 676 if (twitterCardImage != null) 677 { 678 Pageview.Meta.AddTag("twitter:image", twitterCardImage.Path); 679 } 680 681 if (!string.IsNullOrEmpty(twitterCardImageALT)) 682 { 683 Pageview.Meta.AddTag("twitter:image:alt", twitterCardImageALT); 684 } 685 } 686 } 687