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