Error executing template "Designs/Swift/_parsed/Swift_Page.parsed.cshtml"
System.Data.SqlClient.SqlException (0x80131904): A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: Named Pipes Provider, error: 40 - Could not open a connection to SQL Server) ---> System.ComponentModel.Win32Exception (0x80004005): The system cannot find the file specified
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:00000000-0000-0000-0000-000000000000
Error Number:2,State:0,Class:20
1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.PageViewModel>
2 @using System
3 @using Dynamicweb
4 @using Dynamicweb.Environment
5 @using Dynamicweb.Frontend
6 @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