Error executing template "Designs/Swift/_parsed/Swift_Page.parsed.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
at CompiledRazorTemplates.Dynamic.RazorEngine_469181be9a92461ba7abe6ec4499d194.SetMetaTags() in D:\dynamicweb.net\Solutions\plus.cloud.dynamicweb-cms.com\Files\Templates\Designs\Swift\_parsed\Swift_Page.parsed.cshtml:line 777
at CompiledRazorTemplates.Dynamic.RazorEngine_469181be9a92461ba7abe6ec4499d194.Execute() in D:\dynamicweb.net\Solutions\plus.cloud.dynamicweb-cms.com\Files\Templates\Designs\Swift\_parsed\Swift_Page.parsed.cshtml:line 161
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 System.Collections.Generic
4 @using System.Linq
5 @using Dynamicweb.Content
6 @using Dynamicweb.Environment
7 @using Dynamicweb.Frontend
8 @using System.Runtime
9 @using Dynamicweb.Ecommerce.Products
10
11 @{
12 string swiftVersion = ReadFile("/Files/Templates/Designs/Swift/swift_version.txt");
13 bool renderAsResponsive = Model.Area.Item.GetString("DeviceRendering", "responsive").Equals("responsive", StringComparison.OrdinalIgnoreCase);
14 bool renderMobile = Pageview.Device == Dynamicweb.Frontend.Devices.DeviceType.Mobile || Pageview.Device == Dynamicweb.Frontend.Devices.DeviceType.Tablet;
15 string responsiveClassDesktop = string.Empty;
16 string responsiveClassMobile = string.Empty;
17 if (renderAsResponsive)
18 {
19 responsiveClassDesktop = " d-none d-xl-block";
20 responsiveClassMobile = " d-block d-xl-none";
21 }
22
23 if (Dynamicweb.Ecommerce.Common.Context.LanguageID != Pageview.Area.EcomLanguageId)
24 {
25 Dynamicweb.Ecommerce.Common.Context.LanguageID = Pageview.Area.EcomLanguageId;
26 }
27
28 //Cart Values start
29 var CartId = Dynamicweb.Ecommerce.Common.Context.Cart == null ? "" : Dynamicweb.Ecommerce.Common.Context.Cart.Id;
30 var CartValue = "";
31 var CartUrl = "";
32
33 if (!String.IsNullOrEmpty(CartId))
34 {
35 var order = Dynamicweb.Ecommerce.Services.Orders.GetById(CartId);
36 CartValue = order.Price.PricePIP.ToString();
37 string host = Dynamicweb.Context.Current.Request.Url.Host;
38 string hostScheme = Dynamicweb.Context.Current.Request.Url.Scheme;
39
40 var languageId = "";
41 var productString = "";
42 int counter = 0;
43 @* Orderlines *@
44 if (order.OrderLines.Count > 0)
45 {
46 foreach (var orderline in order.OrderLines)
47 {
48 productString += $"&Product[{counter}]=" + orderline.ProductNumber;
49 productString += $"&Quantity[{counter}]=" + orderline.Quantity;
50 languageId = Dynamicweb.Ecommerce.Common.Context.LanguageID;
51 counter++;
52 }
53
54 var dealerId = string.Empty;
55 switch (languageId)
56 {
57 case "LANG3":
58 dealerId = "0002";
59 break;
60 case "LANG4":
61 dealerId = "0003";
62 break;
63 case "LANG5":
64 dealerId = "0004";
65 break;
66 default:
67 dealerId = "0001";
68 break;
69 }
70
71 var urlName = !string.IsNullOrEmpty(PageView.Current().Area.UrlName) ? "/" + PageView.Current().Area.UrlName : string.Empty;
72 var linkToNewCart = hostScheme + "://" + host + urlName + "/PlusDesigner.asmx/PlusDealer" + "?DealerID=" + dealerId + "&lang=";
73 linkToNewCart += languageId;
74 linkToNewCart += "&Currency=" + order.CurrencyCode;
75 linkToNewCart += productString;
76 CartUrl = linkToNewCart;
77 }
78 }
79 //Cart Values end
80
81 var disableWideBreakpoints = Model.Area?.Item?.GetRawValueString("DisableWideBreakpoints", "default");
82
83 var brandingPageId = Model.Area.Item.GetLink("BrandingPage") != null ? Model.Area.Item.GetLink("BrandingPage").PageId : 0;
84 var themePageId = Model.Area.Item.GetLink("ThemesPage") != null ? Model.Area.Item.GetLink("ThemesPage").PageId : 0;
85 string customHeaderInclude = Model.Area.Item.GetFile("CustomHeaderInclude") != null ? Model.Area.Item.GetFile("CustomHeaderInclude").Name : string.Empty;
86
87 var brandingPage = Dynamicweb.Content.Services.Pages?.GetPage(brandingPageId) ?? null;
88 var themesParagraphLastChanged = Dynamicweb.Content.Services.Paragraphs.GetParagraphsByPageId(themePageId).OrderByDescending(p => p.Audit.LastModifiedAt).FirstOrDefault();
89
90 var cssLastModified = brandingPage.Audit.LastModifiedAt > themesParagraphLastChanged.Audit.LastModifiedAt ? brandingPage.Audit.LastModifiedAt : themesParagraphLastChanged.Audit.LastModifiedAt;
91 var cssThemeAndBrandingStyleFileInfo = new System.IO.FileInfo(Dynamicweb.Core.SystemInformation.MapPath($"/Files/Templates/Designs/Swift/_parsed/Swift_css/Swift_styles_{Model.Area.ID}.min.css"));
92
93 // Schema.org details for PDP
94 string productId = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("ProductID")) ? Dynamicweb.Context.Current.Request.QueryString.Get("ProductID") : "";
95 bool isProductDetailsPage = !string.IsNullOrEmpty(productId);
96 bool isArticlePage = Model.ItemType == "Swift_Article";
97 string schemaOrgType = string.Empty;
98
99 if (isProductDetailsPage)
100 {
101 schemaOrgType = "itemscope=\"\" itemtype=\"https://schema.org/Product\"";
102 }
103
104 if (isArticlePage)
105 {
106 schemaOrgType = "itemscope=\"\" itemtype=\"https://schema.org/Article\"";
107 }
108
109 if (!cssThemeAndBrandingStyleFileInfo.Exists || cssThemeAndBrandingStyleFileInfo.LastWriteTime < brandingPage.Audit.LastModifiedAt)
110 {
111 //Branding page has been saved or the file is missing. Rewrite the file to disc.
112 if (brandingPageId > 0)
113 {
114 var brandingPageview = Dynamicweb.Frontend.PageView.GetPageviewByPageID(brandingPageId);
115 brandingPageview.Redirect = false;
116 brandingPageview.Output();
117 }
118 }
119
120 if (!cssThemeAndBrandingStyleFileInfo.Exists || cssThemeAndBrandingStyleFileInfo.LastWriteTime < themesParagraphLastChanged.Audit.LastModifiedAt)
121 {
122 //Branding page has been saved or the file is missing. Rewrite the file to disc.
123 if (themePageId > 0)
124 {
125 var themePageview = Dynamicweb.Frontend.PageView.GetPageviewByPageID(themePageId);
126 themePageview.Redirect = false;
127 themePageview.Output();
128 }
129 }
130
131 var cssStyleFileInfo = new System.IO.FileInfo(Dynamicweb.Core.SystemInformation.MapPath("/Files/Templates/Designs/Swift/Assets/css/styles.css"));
132 var jsFileInfo = new System.IO.FileInfo(Dynamicweb.Core.SystemInformation.MapPath("/Files/Templates/Designs/Swift/Assets/js/scripts.js"));
133
134 string masterTheme = !string.IsNullOrWhiteSpace(Model.Area.Item.GetRawValueString("Theme")) ? " theme " + Model.Area.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : "";
135
136 string favicon = Model.Area.Item.GetFile("Favicon") != null ? Model.Area.Item.GetFile("Favicon").Path : "/Files/Templates/Designs/Swift/Assets/Images/favicon.png";
137
138 string headerCssClass = "sticky-top";
139 bool movePageBehind = false;
140
141 if (Pageview.Page.PropertyItem != null)
142 {
143 headerCssClass = Pageview.Page.PropertyItem["MoveThisPageBehindTheHeader"] != null ? Pageview.Page.PropertyItem["MoveThisPageBehindTheHeader"].ToString() : "sticky-top";
144 movePageBehind = headerCssClass == "fixed-top" && !Pageview.IsVisualEditorMode ? true : false;
145 }
146
147 headerCssClass = headerCssClass == "" ? "sticky-top" : headerCssClass;
148 headerCssClass = Pageview.IsVisualEditorMode ? "" : headerCssClass;
149
150 string googleAnalyticsTrackingID = Model.Area.Item.GetString("GoogleAnalyticsTrackingID");
151 string googleAnalyticsMeasurementID = Model.Area.Item.GetString("GoogleAnalyticsMeasurementID");
152 var cookieOptInLevel = CookieManager.GetCookieOptInLevel();
153 bool allowTracking = cookieOptInLevel == CookieOptInLevel.All || (cookieOptInLevel == CookieOptInLevel.Functional && CookieManager.GetCookieOptInCategories().Contains("Statistical"));
154
155 Dynamicweb.Context.Current.Response.AddHeader("link", $"</Files/Templates/Designs/Swift/Assets/css/styles.css?{cssStyleFileInfo.LastWriteTime.Ticks}>; rel=preload; as=style;");
156 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;");
157 Dynamicweb.Context.Current.Response.AddHeader("link", $"</Files/Templates/Designs/Swift/Assets/js/aos.js?{jsFileInfo.LastWriteTime.Ticks}; rel=preload; as=script;");
158 Dynamicweb.Context.Current.Response.AddHeader("link", $"</Files/Templates/Designs/Swift/Assets/js/scripts.js?{jsFileInfo.LastWriteTime.Ticks}; rel=preload; as=script;");
159 //Dynamicweb.Context.Current.Response.Flush(); //This sends the headers where we are now in the rendering making the TTFB faster
160
161 SetMetaTags();
162
163 List<Dynamicweb.Content.Page> languages = new List<Dynamicweb.Content.Page>();
164
165 if (Pageview.Area.IsMaster)
166 {
167 languages.Add(Pageview.Page);
168 if (Pageview.Page.Languages != null)
169 {
170 foreach (var language in Pageview.Page.Languages)
171 {
172 languages.Add(language);
173 }
174 }
175 }
176 else
177 {
178 languages.Add(Pageview.Page.MasterPage);
179 if (Pageview.Page.MasterPage != null)
180 {
181 if (Pageview.Page.MasterPage.Languages != null)
182 {
183 foreach (var language in Pageview.Page.MasterPage.Languages)
184 {
185 languages.Add(language);
186 }
187 }
188 }
189 }
190
191 string siteLanguage = Pageview.Area.CultureInfo.Name;
192 Uri url = Dynamicweb.Context.Current.Request.Url;
193 string hostName = url.Host; // domain.com/da-dk or domain.com/en-us
194
195 var ecomCountries = Dynamicweb.Ecommerce.Services.Countries.GetCountries();
196 var ecomCurrencies = Dynamicweb.Ecommerce.Services.Currencies.GetAllCurrencies();
197
198 string values = string.Empty;
199 IEnumerable<Paragraph> utmCampaignParagraphs = Enumerable.Empty<Paragraph>();
200 var utmCampaignPage = Dynamicweb.Content.Services.Pages.GetPageByNavigationTag(Pageview.AreaID, "UTM_Campaigns");
201 if (utmCampaignPage != null)
202 {
203 utmCampaignParagraphs = Dynamicweb.Content.Services.Paragraphs.GetParagraphsByPageId(utmCampaignPage.ID).Where(x => x.ShowParagraph);
204 var utmCampaignString = utmCampaignParagraphs.Select(p => p.Item);
205 foreach (Dynamicweb.Content.Items.Item item in utmCampaignString)
206 {
207 values = item.FirstOrDefault(k => k.Key == "ParameterValue").Value.ToString();
208 }
209 }
210
211 }
212 <!doctype html>
213 <html lang="@Pageview.Area.CultureInfo.TwoLetterISOLanguageName">
214 <head>
215 <!-- Google Tag Managere - STAPE -->
216 <script>
217 (function (w, d, s, l, i) {
218 w[l] = w[l] || []; w[l].push({
219 'gtm.start':
220 new Date().getTime(), event: 'gtm.js'
221 }); var f = d.getElementsByTagName(s)[0],
222 j = d.createElement(s); j.async = true; j.src =
223 "https://sst.plus.dk/6ksjiuesll.js?" + i; f.parentNode.insertBefore(j, f);
224 })(window, document, 'script', 'dataLayer', 'amfy=aWQ9R1RNLU1EU0ZEUVg%3D&sort=desc');
225 </script>
226
227 <!-- END Google Tag Managere - STAPE -->
228 <!-- @swiftVersion -->
229 @* Required meta tags *@
230 <meta name="facebook-domain-verification" content="49gii406emphmj02uqo2yyq1li9kzu" />
231 <meta charset="utf-8">
232 <meta name="viewport" content="height=device-height, width=device-width, initial-scale=1.0">
233 <link rel="shortcut icon" href="@favicon">
234 <link rel="apple-touch-icon" href="/Files/Templates/Designs/Swift/Assets/Images/logo_transparent.png">
235
236 @{
237 string metaTagsHtml = (Model.MetaTags ?? string.Empty).ToString();
238 metaTagsHtml = System.Text.RegularExpressions.Regex.Replace(
239 metaTagsHtml,
240 "<meta\\s+name=\"(og:[^\"]+|fb:[^\"]+)\"",
241 "<meta property=\"$1\"",
242 System.Text.RegularExpressions.RegexOptions.IgnoreCase);
243 }
244 @metaTagsHtml
245
246 @{
247 @* Languages meta data *@
248 foreach (var language in languages)
249 {
250 if (language?.Area != null)
251 {
252 if (language != null && language.Published && language.Active && language.Area.Active && language.Area.Published && language.Area.ID != Dynamicweb.Frontend.PageView.Current().AreaID)
253 {
254 if (!string.IsNullOrEmpty(language.Area.DomainLock))
255 {
256 hostName = language.Area.DomainLock; //dk.domain.com or dk-domain.dk
257 }
258 string querystring = $"Default.aspx?ID={language.ID}";
259 if (!string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["GroupID"]))
260 {
261 querystring += $"&GroupID={Dynamicweb.Context.Current.Request.QueryString["GroupID"]}";
262 }
263 if (!string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["ProductID"]))
264 {
265 querystring += $"&ProductID={Dynamicweb.Context.Current.Request.QueryString["ProductID"]}";
266 }
267 if (!string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["VariantID"]))
268 {
269 querystring += $"&VariantID={Dynamicweb.Context.Current.Request.QueryString["VariantID"]}";
270 }
271
272 string friendlyUrl = Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl(querystring);
273 string href = $"{url.Scheme}://{hostName}{friendlyUrl}";
274
275 <link rel="alternate" hreflang="@language.Area.CultureInfo.Name.ToLower()" href="@href">
276 }
277 }
278 }
279 }
280
281 <title>@Model.Title</title>
282 @* Bootstrap + Swift stylesheet *@
283 <link href="/Files/Templates/Designs/Swift/Assets/css/styles.css?@cssStyleFileInfo.LastWriteTime.Ticks" rel="stylesheet" media="all" type="text/css">
284
285 @if (disableWideBreakpoints != "disableBoth")
286 {
287 <style>
288 @@media (min-width: 1600px) {
289 .container-xxl,
290 .container-xl,
291 .container-lg,
292 .container-md,
293 .container-sm,
294 .container {
295 max-width: 1520px;
296 }
297 }
298 </style>
299
300 if (disableWideBreakpoints != "disableUltraWideOnly")
301 {
302 <style>
303 @@media (min-width: 1920px) {
304 .container-xxl,
305 .container-xl,
306 .container-lg,
307 .container-md,
308 .container-sm,
309 .container {
310 max-width: 1820px;
311 }
312 }
313 </style>
314 }
315 }
316
317 @* Branding and Themes min stylesheet *@
318 <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">
319 <script src="/Files/Templates/Designs/Swift/Assets/js/aos.js?@jsFileInfo.LastWriteTime.Ticks" defer></script>
320 <script src="/Files/Templates/Designs/Swift/Assets/js/scripts.js?@jsFileInfo.LastWriteTime.Ticks" defer></script>
321
322 <script type="module">
323 AOS.init({ offset: 0, duration: 400, delay: 100, easing: 'ease-in-out', mirror: true, disable: window.matchMedia('(prefers-reduced-motion: reduce)') });
324 swift.Scroll.hideHeadersOnScroll();
325 swift.Scroll.handleAlternativeTheme();
326 </script>
327
328 <script type="module" src="/Files/Templates/Designs/Swift/Assets/js/rizzo/sr-emptyCart.js?@jsFileInfo.LastWriteTime.Ticks" defer></script>
329
330 <script>
331 document.addEventListener("DOMContentLoaded", function() {
332 // Function to get the value of a parameter from the URL
333 function getParameterByName(name, url) {
334 if (!url) url = window.location.href;
335 name = name.replace(/[\[\]]/g, "\\$&");
336 var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
337 results = regex.exec(url);
338 if (!results) return null;
339 if (!results[2]) return '';
340 return decodeURIComponent(results[2].replace(/\+/g, " "));
341 }
342
343 // Get the value of the parameter from the URL
344 var paramValue = getParameterByName('utm_campaign');
345 var valueString = '@values';
346 console.log(valueString);
347 if (paramValue != null && valueString.includes(paramValue)) {
348 // Create a session cookie with the parameter value
349 let cName = "RemoveDealer",
350 cValue = true;
351 document.cookie = cName + "=" + cValue + "; path=/";
352 }
353 });
354 </script>
355
356 @* Global site tag (gtag.js) - Google Analytics *@
357 @if (!string.IsNullOrWhiteSpace(googleAnalyticsTrackingID) && allowTracking)
358 {
359 <script src="https://www.googletagmanager.com/gtag/js?id=@googleAnalyticsTrackingID" async></script>
360 <script>
361 window.dataLayer = window.dataLayer || [];
362 function gtag() { window.dataLayer.push(arguments); }
363 gtag('js', new Date());
364 gtag('config', '@googleAnalyticsTrackingID');
365 </script>
366 }
367
368 @if (!string.IsNullOrWhiteSpace(googleAnalyticsMeasurementID) && allowTracking)
369 {
370 var GoogleAnalyticsDebugMode = "";
371 bool isLoggedInBackendUser = false;
372
373 if (Dynamicweb.Environment.ExecutingContext.IsAdminLoggedIn())
374 {
375 isLoggedInBackendUser = true;
376 }
377
378 if (Model.Area.Item.GetBoolean("EnableGoogleAnalyticsDebugMode") && @isLoggedInBackendUser)
379 {
380 GoogleAnalyticsDebugMode = ", {'debug_mode': true}";
381 }
382
383 <script async src="https://www.googletagmanager.com/gtag/js?id=@googleAnalyticsMeasurementID"></script>
384 <script>
385 window.dataLayer = window.dataLayer || [];
386 function gtag() { dataLayer.push(arguments); }
387 gtag('js', new Date());
388 gtag('config', '@googleAnalyticsMeasurementID'@GoogleAnalyticsDebugMode);
389 </script>
390 }
391
392 @if (!string.IsNullOrWhiteSpace(customHeaderInclude))
393 {
394 @RenderPartial($"Components/Custom/{customHeaderInclude}")
395 }
396 </head>
397 <body class="brand @(masterTheme)" id="page@(Model.ID)">
398 <!-- Google Tag Manager (noscript) -->
399 @*<noscript>
400 <iframe src="https://www.googletagmanager.com/ns.html?id=GTM-MDSFDQX"
401 height="0" width="0" style="display:none;visibility:hidden"></iframe>
402 </noscript>*@
403 <!-- End Google Tag Manager (noscript) -->
404 @if (renderAsResponsive || !renderMobile)
405 {
406 <header class="page-header @headerCssClass top-0@(responsiveClassDesktop)" id="page-header-desktop">
407 @if (@Model.Area.Item.GetLink("HeaderDesktop") != null)
408 {
409 @RenderGrid(@Model.Area.Item.GetLink("HeaderDesktop").PageId)
410 }
411 </header>
412 }
413
414 @if ((renderAsResponsive || renderMobile))
415 {
416 <header class="page-header @headerCssClass top-0@(responsiveClassMobile)" id="page-header-mobile">
417 @if (@Model.Area.Item.GetLink("HeaderMobile") != null)
418 {
419 @RenderGrid(@Model.Area.Item.GetLink("HeaderMobile").PageId)
420 }
421 </header>
422 }
423
424 <main id="content" @(schemaOrgType)>
425 <div data-intersect></div>
426 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.PageViewModel>
427 @using System
428 @using Dynamicweb.Ecommerce.ProductCatalog
429
430
431 @{
432 string productIdFromUrl = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("ProductID")) ? Dynamicweb.Context.Current.Request.QueryString.Get("ProductID") : string.Empty;
433 bool isProductDetail = !string.IsNullOrEmpty(productIdFromUrl) && Pageview.Page.NavigationTag.ToLower() == "shop";
434
435 bool isArticlePagePage = Model.ItemType == "Swift_Article";
436 string schemaOrgProp = string.Empty;
437 if(isArticlePagePage)
438 {
439 schemaOrgProp = "itemprop=\"articleBody\"";
440 }
441
442 string theme = "";
443 string gridContent = "";
444
445 if (Model.PropertyItem != null)
446 {
447 theme = !string.IsNullOrWhiteSpace(Model.PropertyItem.GetRawValueString("Theme")) ? " theme " + Model.PropertyItem.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : "";
448 }
449
450 if (Model.Item != null || Pageview.IsVisualEditorMode)
451 {
452 if (!isProductDetail)
453 {
454 gridContent = Model.Grid("Grid", "Grid", "default:true;sort:1", "Page");
455 }
456 else
457 {
458 var productObject = Dynamicweb.Ecommerce.Products.Product.GetProductById(productIdFromUrl);
459 var detailPage = Dynamicweb.Ecommerce.Services.ProductGroups.GetGroup(productObject.PrimaryGroupId)?.Meta.PrimaryPage ?? string.Empty;
460 var detailPageId = detailPage != string.Empty ? Convert.ToInt16(detailPage.Substring(detailPage.LastIndexOf('=') + 1)) : GetPageIdByNavigationTag("ProductDetailPage");
461
462 @RenderGrid(detailPageId)
463 }
464 }
465
466 bool doNotRenderPage = false;
467
468 //Check if we are on the poduct detail page, and if there is data to render
469 ProductViewModel product = new ProductViewModel();
470 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails"))
471 {
472 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"];
473 if (string.IsNullOrEmpty(product.Id)) {
474 doNotRenderPage = true;
475 }
476 }
477
478 //Render the page
479 if (!doNotRenderPage) {
480 string itemIdentifier = Model?.Item?.SystemName != null ? "item_" + Model.Item.SystemName.ToLower() : "item_Swift_Page";
481
482 if (theme != "")
483 {
484 <div class="@theme item_@itemIdentifier" @schemaOrgProp>
485 @gridContent
486 </div>
487 }
488 else
489 {
490 <div class="item_@itemIdentifier" @schemaOrgProp>
491 @gridContent
492 </div>
493 }
494 } else {
495 <div class="container">
496 <div class="alert alert-info" role="alert">@Translate("Sorry. There is nothing to view here")</div>
497 </div>
498 }
499
500 if (!Model.IsCurrentUserAllowed)
501 {
502 int signInPage = GetPageIdByNavigationTag("SignInPage");
503 int dashboardPage = GetPageIdByNavigationTag("MyAccountDashboardPage");
504
505 if (!Pageview.IsVisualEditorMode)
506 {
507 if (signInPage != 0)
508 {
509 if (signInPage != Model.ID) {
510 Dynamicweb.Context.Current.Response.Redirect("/Default.aspx?ID=" + signInPage);
511 } else {
512 if (dashboardPage != 0) {
513 Dynamicweb.Context.Current.Response.Redirect("/Default.aspx?ID=" + dashboardPage);
514 } else {
515 Dynamicweb.Context.Current.Response.Redirect("/");
516 }
517 }
518 }
519 else
520 {
521 <div class="alert alert-dark m-0" role="alert">
522 <span>@Translate("You do not have access to this page")</span>
523 </div>
524 }
525 }
526 else
527 {
528 <div class="alert alert-dark m-0" role="alert">
529 <span>@Translate("To work on this page, you must be signed in, in the frontend")</span>
530 </div>
531 }
532 }
533 }
534
535 </main>
536
537 @if (renderAsResponsive || !renderMobile)
538 {
539 <footer class="page-footer@(responsiveClassDesktop)" id="page-footer-desktop">
540 @if (@Model.Area.Item.GetLink("FooterDesktop") != null)
541 {
542 @RenderGrid(@Model.Area.Item.GetLink("FooterDesktop").PageId)
543 }
544 </footer>
545 }
546
547 @if (renderAsResponsive || renderMobile)
548 {
549 <footer class="page-footer@(responsiveClassMobile)" id="page-footer-mobile">
550 @if (@Model.Area.Item.GetLink("FooterMobile") != null)
551 {
552 @RenderGrid(@Model.Area.Item.GetLink("FooterMobile").PageId)
553 }
554 </footer>
555 }
556
557 @* Render any offcanvas menu here *@
558 @RenderSnippet("offcanvas")
559
560 @{
561 bool isErpConnectionDown = !Dynamicweb.Ecommerce.DynamicwebLiveIntegration.TemplatesHelper.IsWebServiceConnectionAvailable();
562 }
563
564 @* Language selector modal *@
565 @if (languages.Count > 1 || ecomCountries.Count > 1 || ecomCurrencies.Count() > 1)
566 {
567 <div class="modal fade" id="PreferencesModal" tabindex="-1" aria-hidden="true">
568 <div class="modal-dialog modal-dialog-centered modal-sm" id="PreferencesModalContent">
569 @* The content here comes from an external request *@
570 </div>
571 </div>
572 }
573
574 @* Favorite toast *@
575 <div aria-live="polite" aria-atomic="true">
576 <div class="position-fixed bottom-0 end-0 p-3" style="z-index: 11">
577 <div id="favoriteNotificationToast" class="toast" role="alert" aria-live="assertive" aria-atomic="true">
578 <div class="toast-header">
579 <strong class="me-auto">@Translate("Favorite list updated")</strong>
580 <button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
581 </div>
582 <div class="toast-body d-flex gap-3">
583 <div id="favoriteNotificationToast_Image"></div>
584 <div id="favoriteNotificationToast_Text"></div>
585 </div>
586 </div>
587 </div>
588 </div>
589
590 @* Modal for dynamic content *@
591 <div class="modal fade js-product" id="DynamicModal" tabindex="-1" aria-hidden="true">
592 <div class="modal-dialog modal-dialog-centered modal-md">
593 <div class="modal-content theme light" id="DynamicModalContent">
594 @* The content here comes from an external request *@
595 </div>
596 </div>
597 </div>
598
599 @* Offcanvas for dynamic content *@
600 <div class="offcanvas offcanvas-end theme light" tabindex="-1" id="DynamicOffcanvas" style="width: 30rem">
601 @* The content here comes from an external request *@
602 </div>
603
604 @if (isErpConnectionDown && Model.Area.Item.GetBoolean("ShowErpDownMessage"))
605 {
606 string erpDownMessageTheme = !string.IsNullOrWhiteSpace(Model.Area.Item.GetRawValueString("ErpDownMessageTheme")) ? " theme " + Model.Area.Item.GetRawValueString("ErpDownMessageTheme").Replace(" ", "").Trim().ToLower() : "theme light";
607
608 <div class="position-fixed bottom-0 end-0 p-3" style="z-index: 1040">
609 <div class="toast fade show border-0 @erpDownMessageTheme" role="alert" aria-live="assertive" aria-atomic="true">
610 <div class="toast-header">
611 <strong class="me-auto">@Translate("Connection down")</strong>
612 <button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
613 </div>
614 <div class="toast-body">
615 @Translate("We are experiencing some connectivity issues. Not all features may be available to you.")
616 </div>
617 </div>
618 </div>
619 }
620
621 </body>
622 </html>
623 @functions {
624 void SetMetaTags()
625 {
626 //Verification Tokens
627 string siteVerificationGoogle = Model.Area.Item.GetString("Google_Site_Verification") != null ? Model.Area.Item.GetString("Google_Site_Verification") : "";
628 //string siteVerificationYandex = Model.Area.Item.GetString("Yandex_Verification") != null ? Model.Area.Item.GetString("Yandex_Verification") : "";
629 //string siteVerificationMS = Model.Area.Item.GetString("Msvalidate_01") != null ? Model.Area.Item.GetString("Msvalidate_01") : "";
630 //string siteVerificationAlexa = Model.Area.Item.GetString("AlexaVerifyID") != null ? Model.Area.Item.GetString("AlexaVerifyID") : "";
631 //string siteVerificationPinterest = Model.Area.Item.GetString("P_domain_verify") != null ? Model.Area.Item.GetString("P_domain_verify") : "";
632 //string siteVerificationNorton = Model.Area.Item.GetString("Norton_safeweb_site_verification") != null ? Model.Area.Item.GetString("Norton_safeweb_site_verification") : "";
633
634 //Generic Site Values
635 string openGraphFacebookAppID = Model.Area.Item.GetString("Fb_app_id") != null ? Model.Area.Item.GetString("Fb_app_id") : "";
636 string openGraphType = Model.Area.Item.GetString("Open_Graph_Type") != null ? Model.Area.Item.GetString("Open_Graph_Type") : "";
637 string openGraphSiteName = Model.Area.Item.GetString("Open_Graph_Site_Name") != null ? Model.Area.Item.GetString("Open_Graph_Site_Name") : "";
638
639 string twitterCardSite = Model.Area.Item.GetString("Twitter_Site") != null ? Model.Area.Item.GetString("Twitter_Site") : "";
640
641 //Page specific values
642 string openGraphSiteTitle = Model.Area.Item.GetString("Open_Graph_Title") != null ? Model.Area.Item.GetString("Open_Graph_Title") : "";
643 FileViewModel openGraphImage = Model.Area.Item.GetFile("Open_Graph_Image");
644 string openGraphImageALT = Model.Area.Item.GetString("Open_Graph_Image_ALT") != null ? Model.Area.Item.GetString("Open_Graph_Image_ALT") : "";
645 string openGraphDescription = Model.Area.Item.GetString("Open_Graph_Description") != null ? Model.Area.Item.GetString("Open_Graph_Description") : "";
646
647 string twitterCardURL = Model.Area.Item.GetString("Twitter_URL") != null ? Model.Area.Item.GetString("Twitter_URL") : "";
648 string twitterCardTitle = Model.Area.Item.GetString("Twitter_Title") != null ? Model.Area.Item.GetString("Twitter_Title") : "";
649 string twitterCardDescription = Model.Area.Item.GetString("Twitter_Description") != null ? Model.Area.Item.GetString("Twitter_Description") : "";
650 FileViewModel twitterCardImage = Model.Area.Item.GetFile("Twitter_Image");
651 string twitterCardImageALT = Model.Area.Item.GetString("Twitter_Image_ALT") != null ? Model.Area.Item.GetString("Twitter_Image_ALT") : "";
652
653 if (!string.IsNullOrEmpty(siteVerificationGoogle))
654 {
655 Pageview.Meta.AddTag("google-site-verification", siteVerificationGoogle);
656 }
657
658 if (!string.IsNullOrEmpty(openGraphFacebookAppID))
659 {
660 Pageview.Meta.AddTag("fb:app_id", openGraphFacebookAppID);
661 }
662
663 if (!string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["ProductID"]))
664 {
665 Pageview.Meta.AddTag("og:type", "product");
666 }
667 else if (Model.ItemType == "Swift_Article")
668 {
669 Pageview.Meta.AddTag("og:type", "article");
670 }
671 else if (!string.IsNullOrEmpty(openGraphType))
672 {
673 Pageview.Meta.AddTag("og:type", openGraphType);
674 }
675
676 if (!string.IsNullOrEmpty(openGraphSiteName))
677 {
678 Pageview.Meta.AddTag("og:site_name", openGraphSiteName);
679 }
680
681 if (!string.IsNullOrEmpty(Model.Title))
682 {
683 Pageview.Meta.AddTag("og:title", Model.Title);
684 }
685 else
686 {
687 Pageview.Meta.AddTag("og:title", openGraphSiteTitle);
688 }
689
690 if (string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["ProductID"]))
691 {
692 if (!string.IsNullOrEmpty(Model.Description))
693 {
694 Pageview.Meta.AddTag("og:description", Model.Description);
695 }
696 else
697 {
698 Pageview.Meta.AddTag("og:description", openGraphDescription);
699 }
700
701 string contentImagePath = string.Empty;
702 string contentImageFileNameBase = $"page-{Pageview.Page.ID}";
703
704 if (openGraphImage != null && !string.IsNullOrWhiteSpace(openGraphImage.Path))
705 {
706 contentImagePath = openGraphImage.Path;
707 }
708 else if (!string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString["GroupID"]))
709 {
710 var groupId = Dynamicweb.Context.Current.Request.QueryString.Get("GroupID");
711 var group = Dynamicweb.Ecommerce.Services.ProductGroups.GetGroup(groupId);
712 contentImagePath = group != null && !string.IsNullOrEmpty(group.LargeImage) ? "/Files" + group.LargeImage : string.Empty;
713 contentImageFileNameBase = groupId;
714 }
715
716 if (string.IsNullOrWhiteSpace(contentImagePath) && Model.ItemType == "Swift_Article")
717 {
718 contentImagePath = GetFirstItemFilePath("CoverImage", "Image", "HeaderImage", "HeroImage", "Thumbnail", "ListImage");
719 }
720
721 if (string.IsNullOrWhiteSpace(contentImagePath) && !string.IsNullOrEmpty(Pageview.Page.TopImage))
722 {
723 contentImagePath = Pageview.Page.TopImage;
724 }
725
726 if (!string.IsNullOrWhiteSpace(contentImagePath))
727 {
728 string contentImageUrl = GetOpenGraphImageUrl(contentImagePath, contentImageFileNameBase);
729 Pageview.Meta.AddTag("og:image", contentImageUrl);
730 Pageview.Meta.AddTag("og:image:width", "1200");
731 Pageview.Meta.AddTag("og:image:height", "630");
732 Pageview.Meta.AddTag("og:image:alt", GetContentOpenGraphImageAlt(openGraphImageALT));
733 Pageview.Meta.AddTag("twitter:card", "summary_large_image");
734 if (twitterCardImage == null)
735 {
736 Pageview.Meta.AddTag("twitter:image", contentImageUrl);
737 }
738 }
739
740 if (!string.IsNullOrEmpty(twitterCardDescription))
741 {
742 Pageview.Meta.AddTag("twitter:description", twitterCardDescription);
743 }
744
745 if (twitterCardImage != null)
746 {
747 Pageview.Meta.AddTag("twitter:image", twitterCardImage.Path);
748 }
749
750 if (!string.IsNullOrEmpty(twitterCardImageALT))
751 {
752 Pageview.Meta.AddTag("twitter:image:alt", twitterCardImageALT);
753 }
754 }
755 else
756 {
757 var productId = Dynamicweb.Context.Current.Request.QueryString.Get("ProductID");
758 var variantId = Dynamicweb.Context.Current.Request.QueryString.Get("VariantID");
759 var languageId = Dynamicweb.Ecommerce.Common.Context.LanguageID;
760 var product = Dynamicweb.Ecommerce.Services.Products.GetProductById(productId, variantId, languageId);
761
762 var imageService = new ProductImageService();
763
764 string imagePath = imageService.GetImagePath(product);
765 if (!string.IsNullOrEmpty(imagePath))
766 {
767 string openGraphImageUrl = GetOpenGraphImageUrl(imagePath, productId);
768
769 Pageview.Meta.AddTag("og:image", openGraphImageUrl);
770 Pageview.Meta.AddTag("og:image:width", "1200");
771 Pageview.Meta.AddTag("og:image:height", "630");
772 Pageview.Meta.AddTag("og:image:alt", GetOpenGraphImageAlt(product.Name));
773 Pageview.Meta.AddTag("twitter:card", "summary_large_image");
774 Pageview.Meta.AddTag("twitter:image", openGraphImageUrl);
775 }
776
777 if (!string.IsNullOrEmpty(product.Name))
778 {
779 Pageview.Meta.AddTag("og:title", product.Meta.Title);
780 }
781
782 if (!string.IsNullOrEmpty(product.Meta.Description))
783 {
784 Pageview.Meta.AddTag("og:description", product.Meta.Description);
785 }
786 }
787
788 if (!string.IsNullOrEmpty(twitterCardSite))
789 {
790 Pageview.Meta.AddTag("twitter:site", twitterCardSite);
791 }
792
793 if (!string.IsNullOrEmpty(twitterCardURL))
794 {
795 Pageview.Meta.AddTag("twitter:url", twitterCardURL);
796 }
797
798 if (!string.IsNullOrEmpty(twitterCardTitle))
799 {
800 Pageview.Meta.AddTag("twitter:title", twitterCardTitle);
801 }
802 }
803
804 string GetOpenGraphImageUrl(string imagePath, string fileNameBase)
805 {
806 if (string.IsNullOrWhiteSpace(imagePath))
807 {
808 return string.Empty;
809 }
810
811 if (imagePath.StartsWith("http", StringComparison.OrdinalIgnoreCase))
812 {
813 Uri imageUri;
814 if (Uri.TryCreate(imagePath, UriKind.Absolute, out imageUri))
815 {
816 imagePath = imageUri.PathAndQuery;
817 }
818 else
819 {
820 return imagePath;
821 }
822 }
823
824 imagePath = GetPublicFilesImagePath(imagePath);
825
826 string encodedImagePath = System.Web.HttpUtility.UrlEncode(imagePath);
827 string encodedFileName = System.Web.HttpUtility.UrlEncode(GetOpenGraphImageFileName(fileNameBase));
828 string openGraphImagePath = $"/Admin/Public/GetImage.ashx?Image={encodedImagePath}&Width=1200&Height=630&Crop=5&FillCanvas=true&Background=ffffff&Format=jpg&Compression=90&Filename={encodedFileName}";
829
830 return $"{Dynamicweb.Context.Current.Request.Url.Scheme}://{Dynamicweb.Context.Current.Request.Url.Host}{openGraphImagePath}";
831 }
832
833 string GetPublicFilesImagePath(string imagePath)
834 {
835 if (string.IsNullOrWhiteSpace(imagePath))
836 {
837 return string.Empty;
838 }
839
840 if (!imagePath.StartsWith("/"))
841 {
842 imagePath = "/" + imagePath;
843 }
844
845 if (imagePath.StartsWith("/Files/", StringComparison.OrdinalIgnoreCase))
846 {
847 return imagePath;
848 }
849
850 if (imagePath.StartsWith("/Images/", StringComparison.OrdinalIgnoreCase))
851 {
852 return "/Files" + imagePath;
853 }
854
855 return imagePath;
856 }
857
858 string GetFirstItemFilePath(params string[] fieldNames)
859 {
860 if (Model.Item == null || fieldNames == null)
861 {
862 return string.Empty;
863 }
864
865 foreach (string fieldName in fieldNames)
866 {
867 if (string.IsNullOrWhiteSpace(fieldName))
868 {
869 continue;
870 }
871
872 FileViewModel file = Model.Item.GetFile(fieldName);
873 if (file != null && !string.IsNullOrWhiteSpace(file.Path))
874 {
875 return file.Path;
876 }
877 }
878
879 return string.Empty;
880 }
881
882 string GetOpenGraphImageAlt(string fallbackAlt)
883 {
884 if (!string.IsNullOrWhiteSpace(fallbackAlt))
885 {
886 return fallbackAlt;
887 }
888
889 if (!string.IsNullOrWhiteSpace(Model.Title))
890 {
891 return Model.Title;
892 }
893
894 return "PLUS A/S";
895 }
896
897 string GetContentOpenGraphImageAlt(string openGraphImageAlt)
898 {
899 if (!string.IsNullOrWhiteSpace(openGraphImageAlt) && !LooksLikeFileName(openGraphImageAlt))
900 {
901 return openGraphImageAlt;
902 }
903
904 return GetOpenGraphImageAlt(Model.Title);
905 }
906
907 bool LooksLikeFileName(string value)
908 {
909 if (string.IsNullOrWhiteSpace(value))
910 {
911 return false;
912 }
913
914 string trimmedValue = value.Trim();
915
916 if (trimmedValue.StartsWith("_") || trimmedValue.IndexOf(".") > -1 || trimmedValue.IndexOf("/") > -1 || trimmedValue.IndexOf("\\") > -1)
917 {
918 return true;
919 }
920
921 return false;
922 }
923
924 string GetOpenGraphImageFileName(string fileNameBase)
925 {
926 if (string.IsNullOrWhiteSpace(fileNameBase))
927 {
928 return "product-og-image.jpg";
929 }
930
931 string fileName = fileNameBase.Trim().ToLower();
932 fileName = fileName.Replace(" ", "-").Replace("/", "-").Replace("\\", "-").Replace("_", "-");
933
934 return $"{fileName}-og-image.jpg";
935 }
936 }
937 @*Sleeknote SiteData*@
938 <script>
939 window.sleeknoteSiteData ||= [];
940 window.sleeknoteSiteData.push({ 'CartValue': '@CartValue' });
941 window.sleeknoteSiteData.push({'CartUrl':'@CartUrl'});
942 </script>
943