Themes

Take full control of all the colors

Error executing template "Designs/Swift/Paragraph/Swift_Theme.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_f772bb33646d4d3c8ae681f5d1cf870b.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.ParagraphViewModel> 2 @using System.Globalization 3 @using System.Drawing 4 @using Dynamicweb.Core 5 6 @functions{ 7 // Get Hex Color from color picker and translate to RGB color 8 public static string HexToRGB(string hexString) 9 { 10 if (hexString.Equals("none") || string.IsNullOrEmpty(hexString)) 11 return hexString; 12 13 //replace # occurences 14 if (hexString.IndexOf('#') != -1) 15 hexString = hexString.Replace("#", ""); 16 try 17 { 18 int r, g, b = 0; 19 r = int.Parse(hexString.Substring(0, 2), NumberStyles.AllowHexSpecifier); 20 g = int.Parse(hexString.Substring(2, 2), NumberStyles.AllowHexSpecifier); 21 b = int.Parse(hexString.Substring(4, 2), NumberStyles.AllowHexSpecifier); 22 23 string rgb = r + "," + g + "," + b; 24 return rgb; 25 } 26 catch (System.Exception) 27 { 28 return hexString; 29 } 30 } 31 32 public static string GetColorSquare(string hex) 33 { 34 return $"<div class=\"swift-checkered d-inline-block me-1\" style=\"height:1.25rem;width:1.25rem;\"><div style=\"background-color: {hex};border:.0625rem solid grey\" class=\"w-100 h-100\"></div></div>"; 35 } 36 37 public class HSL 38 { 39 public double h { get; set; } 40 public double s { get; set; } 41 public double l { get; set; } 42 } 43 44 //Convert HEX to HSL 45 public static HSL HexToHsl(string hexString) 46 { 47 System.Drawing.Color color = System.Drawing.ColorTranslator.FromHtml(hexString); 48 49 float _R = (color.R / 255f); 50 float _G = (color.G / 255f); 51 float _B = (color.B / 255f); 52 53 float _Min = Math.Min(Math.Min(_R, _G), _B); 54 float _Max = Math.Max(Math.Max(_R, _G), _B); 55 float _Delta = _Max - _Min; 56 57 float S = 0; 58 float L = (float)((_Max + _Min) / 2.0f); 59 60 if (_Delta != 0) 61 { 62 if (L < 0.5f) 63 { 64 S = (float)(_Delta / (_Max + _Min)); 65 } 66 else 67 { 68 S = (float)(_Delta / (2.0f - _Max - _Min)); 69 } 70 } 71 72 HSL hsl = new HSL(); 73 hsl.h = Math.Round(color.GetHue(), 2); 74 hsl.s = Math.Round(S, 2) * 100; 75 hsl.l = Math.Round(L, 2) * 100; 76 77 return hsl; 78 } 79 80 //Find contrast color (white, black) 81 public static Color GetContrastColor(string hexString) 82 { 83 System.Drawing.Color bg = System.Drawing.ColorTranslator.FromHtml(hexString); 84 85 int nThreshold = 105; 86 int bgDelta = Convert.ToInt32((bg.R * 0.299) + (bg.G * 0.587) + 87 (bg.B * 0.114)); 88 89 Color foreColor = (255 - bgDelta < nThreshold) ? Color.Black : Color.White; 90 return foreColor; 91 } 92 93 public string GetColorVariation(string hexString) 94 { 95 var contrast = GetContrastColor(hexString); 96 HSL currentHslColor = HexToHsl(hexString); 97 HSL variantHslColor = currentHslColor; 98 99 if (contrast == Color.Black) 100 { 101 variantHslColor.l = 85; //The background is dark - Color should be light 102 } 103 else 104 { 105 variantHslColor.l = 40; //The background is light - Color should be less light 106 } 107 108 //Color is very light 109 if (currentHslColor.l > 85) 110 { 111 variantHslColor.l = 95; 112 } 113 114 string colorString = variantHslColor.h + ", " + variantHslColor.s + "%, " + variantHslColor.l + "%"; 115 return colorString; 116 } 117 118 private class ThemeDatabaseUsage 119 { 120 public string TableName { get; set; } 121 public string ColumnName { get; set; } 122 } 123 124 private class ThemeUsage 125 { 126 public string Id { get; set; } 127 public List<int> WebsiteUsages { get; set; } = new List<int>(); 128 public List<int> PageUsages { get; set; } = new List<int>(); 129 public List<int> PagePropertyUsages { get; set; } = new List<int>(); 130 public List<int> GridRowUsages { get; set; } = new List<int>(); 131 public List<int> ParagraphUsages { get; set; } = new List<int>(); 132 public int AllUsages { get; set; } = 0; 133 } 134 135 private List<ThemeDatabaseUsage> GetThemeColumnsFromSchema() 136 { 137 string cacheKey = "ThemesFromDatabase"; 138 if (Dynamicweb.Context.Current.Items.Contains(cacheKey)) 139 { 140 return (List<ThemeDatabaseUsage>)Dynamicweb.Context.Current.Items[cacheKey]; 141 } 142 var schema = Dynamicweb.Data.Database.CreateDataReader("SELECT table_name, column_name FROM information_schema.columns WHERE column_name LIKE '%Theme%'"); 143 144 var rawData = new List<ThemeDatabaseUsage>(); 145 while (schema.Read()) { 146 rawData.Add(new ThemeDatabaseUsage {TableName = schema["table_name"].ToString(), ColumnName = schema["column_name"].ToString() }); 147 } 148 Dynamicweb.Context.Current.Items[cacheKey] = rawData; 149 return rawData; 150 } 151 152 private List<ThemeUsage> GetAllThemeUsages() 153 { 154 string cacheKey = "ThemeUsages"; 155 if (Dynamicweb.Context.Current.Items.Contains(cacheKey)) 156 { 157 return (List<ThemeUsage>)Dynamicweb.Context.Current.Items[cacheKey]; 158 } 159 160 var rawData = GetThemeColumnsFromSchema(); 161 var themeUsages = new List<ThemeUsage>(); 162 163 var sqlQuery = ""; 164 foreach (var row in rawData) 165 { 166 if (!string.IsNullOrEmpty(sqlQuery)) { sqlQuery += " UNION ALL "; } 167 sqlQuery += $"SELECT '{row.TableName}' AS TableName, '{row.ColumnName}' AS ColumnName, {row.ColumnName} AS ColumnValue, Id, AreaId, Page.PageId, PageProperties.PageId as 'PagePropertiesId', GridRowId, ParagraphId" + 168 $" FROM [{row.TableName}]" + 169 $" LEFT JOIN Area ON AreaItemType = '{row.TableName.Replace("ItemType_","")}' AND AreaItemId = Id" + 170 $" LEFT JOIN Page ON PageItemType = '{row.TableName.Replace("ItemType_","")}' AND PageItemId = Id" + 171 $" LEFT JOIN Page as PageProperties ON AreaItemTypePageProperty = '{row.TableName.Replace("ItemType_","")}' AND PageProperties.PagePropertyItemId = Id" + 172 $" LEFT JOIN GridRow ON GridRowItemType = '{row.TableName.Replace("ItemType_","")}' AND GridRowItemId = Id" + 173 $" LEFT JOIN Paragraph ON ParagraphItemType = '{row.TableName.Replace("ItemType_","")}' AND ParagraphItemId = Id" + 174 $" WHERE {row.ColumnName} != '' AND {row.ColumnName} IS NOT NULL" + 175 $" AND (AreaId IS NULL OR AreaId = {Pageview.AreaID})" + 176 $" AND (Page.PageId IS NULL OR Page.PageAreaId = {Pageview.AreaID})" + 177 $" AND (PageProperties.PageId IS NULL OR PageProperties.PageAreaId = {Pageview.AreaID})" + 178 $" AND (GridRowId IS NULL OR GridRowPageId IN (SELECT value From string_split(@pageIds, ',')))" + 179 $" AND (ParagraphId IS NULL OR ParagraphPageId IN (SELECT value From string_split(@pageIds, ',')))"; 180 181 var maxMsSqlServerCharLimit = 60000; 182 if (sqlQuery.Length > maxMsSqlServerCharLimit) 183 { 184 UpdateThemeUsages(sqlQuery, themeUsages); 185 sqlQuery = ""; 186 } 187 } 188 189 if (!string.IsNullOrEmpty(sqlQuery)) 190 { 191 UpdateThemeUsages(sqlQuery, themeUsages); 192 } 193 UpdateAllUsages(themeUsages); 194 195 Dynamicweb.Context.Current.Items[cacheKey] = themeUsages; 196 return themeUsages; 197 } 198 199 private void UpdateThemeUsages(string sqlQuery, List<ThemeUsage> themeUsages) 200 { 201 sqlQuery = $"DECLARE @pageIds NVARCHAR(max) = (SELECT STRING_AGG(PageId,',') FROM Page WHERE PageAreaId = {Pageview.AreaID}) " + sqlQuery; 202 var rawData = Dynamicweb.Data.Database.CreateDataReader(sqlQuery); 203 while (rawData.Read()) 204 { 205 var themeId = rawData["ColumnValue"].ToString(); 206 var themeUsageExists = themeUsages.FirstOrDefault(t => t.Id == themeId) != null; 207 if (!themeUsageExists) 208 { 209 themeUsages.Add(new ThemeUsage { Id = themeId }); 210 } 211 212 var themeUsage = themeUsages.FirstOrDefault(t => t.Id == themeId); 213 if (themeUsage == null) continue; 214 215 if (Converter.ToInt32(rawData["AreaId"].ToString()) > 0) { themeUsage.WebsiteUsages.Add(Converter.ToInt32(rawData["AreaId"].ToString())); } 216 if (Converter.ToInt32(rawData["PageId"].ToString()) > 0) { themeUsage.PageUsages.Add(Converter.ToInt32(rawData["PageId"].ToString())); } 217 if (Converter.ToInt32(rawData["PagePropertiesId"].ToString()) > 0) { themeUsage.PagePropertyUsages.Add(Converter.ToInt32(rawData["PagePropertiesId"].ToString())); } 218 if (Converter.ToInt32(rawData["GridRowId"].ToString()) > 0) { themeUsage.GridRowUsages.Add(Converter.ToInt32(rawData["GridRowId"].ToString())); } 219 if (Converter.ToInt32(rawData["ParagraphId"].ToString()) > 0) { themeUsage.ParagraphUsages.Add(Converter.ToInt32(rawData["ParagraphId"].ToString())); } 220 } 221 } 222 223 private void UpdateAllUsages(List<ThemeUsage> themeUsages) 224 { 225 foreach (var themeUsage in themeUsages) 226 { 227 int websiteUsages = themeUsage?.WebsiteUsages.Count ?? 0; 228 int pageUsages = themeUsage?.PageUsages.Count ?? 0; 229 int pagePropertyUsages = themeUsage?.PagePropertyUsages.Count ?? 0; 230 int gridRowUsages = themeUsage?.GridRowUsages.Count ?? 0; 231 int paragraphUsages = themeUsage?.ParagraphUsages.Count ?? 0; 232 themeUsage.AllUsages = websiteUsages + pageUsages + pagePropertyUsages + gridRowUsages + paragraphUsages; 233 } 234 } 235 236 private ThemeUsage GetThemeUsageById(string themeId) 237 { 238 var themeUsages = GetAllThemeUsages(); 239 return themeUsages.FirstOrDefault(t => t.Id == themeId); 240 } 241 242 } 243 244 @{ 245 string themeName = !string.IsNullOrWhiteSpace(Model.Item.GetString("Name")) ? Model.Item.GetString("Name") : "Default"; 246 string themeClassName = !string.IsNullOrWhiteSpace(Model.Item.GetString("CSSClassName")) ? Model.Item.GetString("CSSClassName").Replace(" ", "").Trim().ToLower() : ""; 247 248 //Theme colors 249 string foregroundColor = Model.Item.GetString("ForegroundColor", "currentColor"); 250 string backgroundColor = Model.Item.GetString("BackgroundColor", "transparent"); 251 string accentColor = Model.Item.GetString("accentColor", "currentColor"); 252 string borderColor = Model.Item.GetString("BorderColor", "transparent"); 253 254 //Primary button 255 string buttonPrimaryForegroundColor = Model.Item.GetString("ButtonPrimaryForegroundColor", "currentColor"); 256 string buttonPrimaryBackgroundColor = Model.Item.GetString("ButtonPrimaryBackgroundColor", "transparent"); 257 string buttonPrimaryBorderColor = Model.Item.GetString("ButtonPrimaryBorderColor", "transparent"); 258 259 string buttonPrimaryHoverBackgroundColorAuto = "transparent"; 260 if (buttonPrimaryBackgroundColor != "transparent") 261 { 262 buttonPrimaryHoverBackgroundColorAuto = "hsl(" + GetColorVariation(buttonPrimaryBackgroundColor) + ")"; 263 } 264 string buttonPrimaryHoverForegroundColor = Model.Item.GetString("ButtonPrimaryHoverForegroundColor", buttonPrimaryForegroundColor); 265 string buttonPrimaryHoverBackgroundColor = Model.Item.GetString("ButtonPrimaryHoverBackgroundColor", buttonPrimaryHoverBackgroundColorAuto); 266 string buttonPrimaryHoverBorderColor = Model.Item.GetString("ButtonPrimaryHoverBorderColor", buttonPrimaryBorderColor); 267 268 //Secondary buton 269 string buttonSecondaryForegroundColor = Model.Item.GetString("ButtonSecondaryForegroundColor", "currentColor"); 270 string buttonSecondaryBackgroundColor = Model.Item.GetString("ButtonSecondaryBackgroundColor", "transparent"); 271 string buttonSecondaryBorderColor = Model.Item.GetString("ButtonSecondaryBorderColor", "transparent"); 272 273 string buttonSecondaryHoverBackgroundColorAuto = "transparent"; 274 if (buttonSecondaryBackgroundColor != "transparent") 275 { 276 buttonSecondaryHoverBackgroundColorAuto = "hsl(" + GetColorVariation(buttonSecondaryBackgroundColor) + ")"; 277 } 278 string buttonSecondaryHoverForegroundColor = Model.Item.GetString("ButtonSecondaryHoverForegroundColor", buttonSecondaryForegroundColor); 279 string buttonSecondaryHoverBackgroundColor = Model.Item.GetString("ButtonSecondaryHoverBackgroundColor", buttonSecondaryHoverBackgroundColorAuto); 280 string buttonSecondaryHoverBorderColor = Model.Item.GetString("ButtonSecondaryHoverBorderColor", buttonSecondaryBorderColor); 281 282 //Link buton 283 string buttonLinkColor = Model.Item.GetString("ButtonLinkColor", "currentColor"); 284 string buttonLinkHoverColorAuto = "currentColor"; 285 if (buttonLinkColor != "currentColor") 286 { 287 buttonLinkHoverColorAuto = "hsl(" + GetColorVariation(buttonLinkColor) + ")"; 288 } 289 string buttonLinkHoverColor = Model.Item.GetString("ButtonLinkHoverColor", buttonLinkHoverColorAuto); 290 291 string accessibilityOutlineColor = Model.Item.GetString("OutlineColor") != null ? Model.Item.GetString("OutlineColor") : ""; 292 293 if (!string.IsNullOrEmpty(themeClassName)) 294 { 295 var sb = new System.Text.StringBuilder(); 296 297 sb.AppendLine("." + themeClassName + "{"); 298 if (!string.IsNullOrEmpty(foregroundColor)) 299 { 300 sb.AppendLine($"--swift-foreground-color: {foregroundColor};"); 301 sb.AppendLine($"--swift-foreground-color-rgb: {HexToRGB(foregroundColor)};"); 302 } 303 if (!string.IsNullOrEmpty(backgroundColor)) 304 { 305 sb.AppendLine($"--swift-background-color: {backgroundColor};"); 306 sb.AppendLine($"--swift-background-color-rgb: {HexToRGB(backgroundColor)};"); 307 } 308 if (!string.IsNullOrEmpty(accentColor)) 309 { 310 sb.AppendLine($"--swift-accent-color: {accentColor};"); 311 sb.AppendLine($"--swift-accent-color-rgb: {HexToRGB(accentColor)};"); 312 } 313 if (!string.IsNullOrEmpty(borderColor)) 314 { 315 sb.AppendLine($"--swift-border-color: {borderColor};"); 316 sb.AppendLine($"--swift-border-color-rgb: {HexToRGB(borderColor)};"); 317 } 318 319 if (!string.IsNullOrEmpty(buttonPrimaryBackgroundColor)) 320 { 321 sb.AppendLine($"--swift-button-primary-background-color: {buttonPrimaryBackgroundColor};"); 322 sb.AppendLine($"--swift-button-primary-background-color-rgb: {HexToRGB(buttonPrimaryBackgroundColor)};"); 323 } 324 if (!string.IsNullOrEmpty(buttonPrimaryForegroundColor)) 325 { 326 sb.AppendLine($"--swift-button-primary-foreground-color: {buttonPrimaryForegroundColor};"); 327 sb.AppendLine($"--swift-button-primary-foreground-color-rgb: {HexToRGB(buttonPrimaryForegroundColor)};"); 328 } 329 if (!string.IsNullOrEmpty(buttonPrimaryBorderColor)) 330 { 331 sb.AppendLine($"--swift-button-primary-border-color: {buttonPrimaryBorderColor};"); 332 sb.AppendLine($"--swift-button-primary-border-color-rgb: {HexToRGB(buttonPrimaryBorderColor)};"); 333 } 334 335 if (!string.IsNullOrEmpty(buttonPrimaryHoverBackgroundColor)) 336 { 337 sb.AppendLine($"--swift-button-primary-hover-background-color: {buttonPrimaryHoverBackgroundColor};"); 338 sb.AppendLine($"--swift-button-primary-hover-background-color-rgb: {HexToRGB(buttonPrimaryHoverBackgroundColor)};"); 339 } 340 if (!string.IsNullOrEmpty(buttonPrimaryHoverForegroundColor)) 341 { 342 sb.AppendLine($"--swift-button-primary-hover-foreground-color: {buttonPrimaryHoverForegroundColor};"); 343 sb.AppendLine($"--swift-button-primary-hover-foreground-color-rgb: {HexToRGB(buttonPrimaryHoverForegroundColor)};"); 344 } 345 if (!string.IsNullOrEmpty(buttonPrimaryHoverBorderColor)) 346 { 347 sb.AppendLine($"--swift-button-primary-hover-border-color: {buttonPrimaryHoverBorderColor};"); 348 sb.AppendLine($"--swift-button-primary-hover-border-color-rgb: {HexToRGB(buttonPrimaryHoverBorderColor)};"); 349 } 350 351 if (!string.IsNullOrEmpty(buttonSecondaryBackgroundColor)) 352 { 353 sb.AppendLine($"--swift-button-secondary-background-color: {buttonSecondaryBackgroundColor};"); 354 sb.AppendLine($"--swift-button-secondary-background-color-rgb: {HexToRGB(buttonSecondaryBackgroundColor)};"); 355 } 356 if (!string.IsNullOrEmpty(buttonSecondaryForegroundColor)) 357 { 358 sb.AppendLine($"--swift-button-secondary-foreground-color: {buttonSecondaryForegroundColor};"); 359 sb.AppendLine($"--swift-button-secondary-foreground-color-rgb: {HexToRGB(buttonSecondaryForegroundColor)};"); 360 } 361 if (!string.IsNullOrEmpty(buttonSecondaryBorderColor)) 362 { 363 sb.AppendLine($"--swift-button-secondary-border-color: {buttonSecondaryBorderColor};"); 364 sb.AppendLine($"--swift-button-secondary-border-color-rgb: {HexToRGB(buttonSecondaryBorderColor)};"); 365 } 366 if (!string.IsNullOrEmpty(buttonSecondaryHoverBackgroundColor)) 367 { 368 sb.AppendLine($"--swift-button-secondary-hover-background-color: {buttonSecondaryHoverBackgroundColor};"); 369 sb.AppendLine($"--swift-button-secondary-hover-background-color-rgb: {HexToRGB(buttonSecondaryHoverBackgroundColor)};"); 370 } 371 if (!string.IsNullOrEmpty(buttonSecondaryHoverForegroundColor)) 372 { 373 sb.AppendLine($"--swift-button-secondary-hover-foreground-color: {buttonSecondaryHoverForegroundColor};"); 374 sb.AppendLine($"--swift-button-secondary-hover-foreground-color-rgb: {HexToRGB(buttonSecondaryHoverForegroundColor)};"); 375 } 376 if (!string.IsNullOrEmpty(buttonSecondaryHoverBorderColor)) 377 { 378 sb.AppendLine($"--swift-button-secondary-hover-border-color: {buttonSecondaryHoverBorderColor};"); 379 sb.AppendLine($"--swift-button-secondary-hover-border-color-rgb: {HexToRGB(buttonSecondaryHoverBorderColor)};"); 380 } 381 if (!string.IsNullOrEmpty(buttonLinkColor)) 382 { 383 sb.AppendLine($"--swift-button-link-color: {buttonLinkColor};"); 384 sb.AppendLine($"--swift-button-link-color-rgb: {HexToRGB(buttonLinkColor)};"); 385 } 386 if (!string.IsNullOrEmpty(buttonLinkHoverColor)) 387 { 388 sb.AppendLine($"--swift-button-link-hover-color: {buttonLinkHoverColor};"); 389 sb.AppendLine($"--swift-button-link-hover-color-rgb: {HexToRGB(buttonLinkHoverColor)};"); 390 } 391 if (!string.IsNullOrEmpty(accessibilityOutlineColor)) 392 { 393 sb.AppendLine($"--swift-a11y-outline-color: {accessibilityOutlineColor};"); 394 sb.AppendLine($"--swift-a11y-outline-color-rgb: {HexToRGB(accessibilityOutlineColor)};"); 395 } 396 sb.AppendLine("}"); 397 398 Dynamicweb.Core.Helpers.TextFileHelper.WriteTextFile(sb.ToString() + Environment.NewLine, Dynamicweb.Context.Current.Server.MapPath($"/Files/Templates/Designs/Swift/_parsed/Swift_css/Swift_theme_styles_{Dynamicweb.Content.Services.Pages.GetPage(Model.PageID).AreaId}.tmp"), true); 399 } 400 } 401 402 @if (themeClassName != "") 403 { 404 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 405 406 <div class="g-col-12 g-col-lg-4"> 407 <div class="swift-checkered p-3"> 408 <div class="theme-option theme @themeClassName p-lg-3"> 409 <div class="mb-2 pb-1 border-bottom d-flex align-items-center"> 410 <span class="icon-3 me-2"> 411 @ReadFile(iconPath + "sun.svg") 412 </span> 413 <h4 class="m-0 p-0">@themeName</h4> 414 </div> 415 <div class="d-flex flex-column gap-1 mb-2"> 416 <p class="mb-0">This is the body text of the theme.</p> 417 <p class="mb-0" style="color: var(--swift-accent-color)">This is the accent color of the theme.</p> 418 </div> 419 <div> 420 <button class="btn btn-primary me-1">Primary</button> 421 <button class="btn btn-secondary">Secondary</button> 422 <button class="btn btn-link">Link</button> 423 </div> 424 </div> 425 </div> 426 </div> 427 <div class="g-col-12 g-col-lg-8 mb-4 mb-lg-0"> 428 <div class="grid fs-7"> 429 <div class="g-col-12 g-col-lg-3"> 430 <table class="table table-borderless table-sm w-100"> 431 <thead> 432 <tr> 433 <th colspan="2" class="fw-bold">@Translate("Base")</th> 434 </tr> 435 </thead> 436 <tr> 437 <td>@Translate("Foreground")</td> 438 <td>@GetColorSquare(foregroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@foregroundColor');">@foregroundColor</a></td> 439 </tr> 440 <tr> 441 <td>@Translate("Background")</td> 442 <td>@GetColorSquare(backgroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@backgroundColor');">@backgroundColor</a></td> 443 </tr> 444 <tr> 445 <td>@Translate("Accent")</td> 446 <td>@GetColorSquare(accentColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@accentColor');">@accentColor</a></td> 447 </tr> 448 <tr> 449 <td>@Translate("Border")</td> 450 <td>@GetColorSquare(borderColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@borderColor');">@borderColor</a></td> 451 </tr> 452 </table> 453 </div> 454 <div class="g-col-12 g-col-lg-3"> 455 <table class="table table-borderless table-sm w-100"> 456 <thead> 457 <tr> 458 <th colspan="2" class="fw-bold">@Translate("Primary button")</th> 459 </tr> 460 </thead> 461 <tr> 462 <td>@Translate("Foreground")</td> 463 <td>@GetColorSquare(buttonPrimaryForegroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonPrimaryForegroundColor');">@buttonPrimaryForegroundColor</a></td> 464 </tr> 465 <tr> 466 <td>@Translate("Background")</td> 467 <td>@GetColorSquare(buttonPrimaryBackgroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonPrimaryBackgroundColor');">@buttonPrimaryBackgroundColor</a></td> 468 </tr> 469 @if (!string.IsNullOrEmpty(buttonPrimaryBorderColor)) 470 { 471 <tr> 472 <td>@Translate("Border")</td> 473 <td>@GetColorSquare(buttonPrimaryBorderColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonPrimaryBorderColor');">@buttonPrimaryBorderColor</a></td> 474 </tr> 475 } 476 <thead> 477 <tr> 478 <th colspan="2" class="fw-bold">@Translate("Primary button hover")</th> 479 </tr> 480 </thead> 481 @if (!string.IsNullOrEmpty(buttonPrimaryHoverForegroundColor)) 482 { 483 <tr> 484 <td>@Translate("Foreground")</td> 485 <td>@GetColorSquare(buttonPrimaryHoverForegroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonPrimaryHoverForegroundColor');">@buttonPrimaryHoverForegroundColor</a></td> 486 </tr> 487 } 488 @if (!string.IsNullOrEmpty(buttonPrimaryHoverBackgroundColor)) 489 { 490 <tr> 491 <td>@Translate("Background")</td> 492 <td>@GetColorSquare(buttonPrimaryHoverBackgroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonPrimaryHoverBackgroundColor');">@buttonPrimaryHoverBackgroundColor</a></td> 493 </tr> 494 } 495 @if (!string.IsNullOrEmpty(buttonPrimaryHoverBorderColor)) 496 { 497 <tr> 498 <td>@Translate("Border")</td> 499 <td>@GetColorSquare(buttonPrimaryHoverBorderColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonPrimaryHoverBorderColor');">@buttonPrimaryHoverBorderColor</a></td> 500 </tr> 501 } 502 </table> 503 </div> 504 <div class="g-col-12 g-col-lg-3"> 505 <table class="table table-borderless table-sm w-100"> 506 <thead> 507 <tr> 508 <th colspan="2" class="fw-bold">@Translate("Secondary button")</th> 509 </tr> 510 </thead> 511 <tr> 512 <td>@Translate("Foreground")</td> 513 <td>@GetColorSquare(buttonSecondaryForegroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonSecondaryForegroundColor');">@buttonSecondaryForegroundColor</a></td> 514 </tr> 515 <tr> 516 <td>@Translate("Background")</td> 517 <td>@GetColorSquare(buttonSecondaryBackgroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonSecondaryBackgroundColor');">@buttonSecondaryBackgroundColor</a></td> 518 </tr> 519 @if (!string.IsNullOrEmpty(buttonSecondaryBorderColor)) 520 { 521 <tr> 522 <td>@Translate("Border")</td> 523 <td>@GetColorSquare(buttonSecondaryBorderColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonSecondaryBorderColor');">@buttonSecondaryBorderColor</a></td> 524 </tr> 525 } 526 <thead> 527 <tr> 528 <th colspan="2" class="fw-bold">@Translate("Secondary button hover")</th> 529 </tr> 530 </thead> 531 @if (!string.IsNullOrEmpty(buttonSecondaryHoverForegroundColor)) 532 { 533 <tr> 534 <td>@Translate("Foreground")</td> 535 <td>@GetColorSquare(buttonSecondaryHoverForegroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonSecondaryHoverForegroundColor');">@buttonSecondaryHoverForegroundColor</a></td> 536 </tr> 537 } 538 @if (!string.IsNullOrEmpty(buttonSecondaryHoverBackgroundColor)) 539 { 540 <tr> 541 <td>@Translate("Background")</td> 542 <td>@GetColorSquare(buttonSecondaryHoverBackgroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonSecondaryHoverBackgroundColor');">@buttonSecondaryHoverBackgroundColor</a></td> 543 </tr> 544 } 545 @if (!string.IsNullOrEmpty(buttonSecondaryHoverBorderColor)) 546 { 547 <tr> 548 <td>@Translate("Border")</td> 549 <td>@GetColorSquare(buttonSecondaryHoverBorderColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonSecondaryHoverBorderColor');">@buttonSecondaryHoverBorderColor</a></td> 550 </tr> 551 } 552 @if (!string.IsNullOrEmpty(accessibilityOutlineColor)) 553 { 554 <tr> 555 <td>@Translate("Focus outline")</td> 556 <td>@GetColorSquare(accessibilityOutlineColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@accessibilityOutlineColor');">@accessibilityOutlineColor</a></td> 557 </tr> 558 } 559 </table> 560 </div> 561 <div class="g-col-12 g-col-lg-3"> 562 <table class="table table-borderless table-sm w-100"> 563 <thead> 564 <tr> 565 <th colspan="2" class="fw-bold">@Translate("Link button")</th> 566 </tr> 567 </thead> 568 <tr> 569 <td>@Translate("Link color")</td> 570 <td>@GetColorSquare(buttonLinkColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonLinkColor');">@buttonLinkColor</a></td> 571 </tr> 572 <thead> 573 <tr> 574 <th colspan="2" class="fw-bold">@Translate("Link button hover")</th> 575 </tr> 576 </thead> 577 @if (!string.IsNullOrEmpty(buttonLinkHoverColor)) 578 { 579 <tr> 580 <td>@Translate("Link hover color")</td> 581 <td>@GetColorSquare(buttonLinkHoverColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonLinkHoverColor');">@buttonLinkHoverColor</a></td> 582 </tr> 583 } 584 585 @if (!string.IsNullOrEmpty(accessibilityOutlineColor)) 586 { 587 <tr> 588 <td>@Translate("Focus outline")</td> 589 <td>@GetColorSquare(accessibilityOutlineColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@accessibilityOutlineColor');">@accessibilityOutlineColor</a></td> 590 </tr> 591 } 592 </table> 593 </div> 594 <div class="g-col-12 g-col-lg-3"> 595 <table class="table table-borderless table-sm w-100"> 596 <tr> 597 <td class="fw-bold">@Translate("Implementation")</td> 598 </tr> 599 <tr> 600 <td> 601 @{ 602 string implementation = $"<div class=\"theme {themeClassName}\"></div>"; 603 } 604 <div class="text-muted"> 605 <code class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@themeClassName');"> 606 @Dynamicweb.Core.Encoders.HtmlEncoder.HtmlEncode(implementation) 607 </code> 608 </div> 609 </td> 610 </tr> 611 </table> 612 </div> 613 @{ 614 bool hasUsageData = Converter.ToBoolean(Dynamicweb.Context.Current.Request["auditUsages"]); 615 if (hasUsageData) 616 { 617 var themeUsage = GetThemeUsageById(themeClassName); 618 int allUsages = themeUsage?.AllUsages ?? 0; 619 string buttonAttributes = allUsages > 0 ? $"data-bs-toggle=\"modal\" data-bs-target=\"#themeUsage_{Model.ID}\"" : string.Empty; 620 string badgeStyle = allUsages > 0 ? "bg-success" : "bg-danger"; 621 622 <div class="g-col-12 g-col-lg-9 position-relative"> 623 <button type="button" class="btn btn-primary btn-sm position-absolute end-0" @buttonAttributes> 624 Usages 625 <span class="position-absolute top-0 start-100 translate-middle badge rounded-pill text-white @badgeStyle">@allUsages</span> 626 </button> 627 628 @if (allUsages > 0) 629 { 630 int websiteUsages = themeUsage?.WebsiteUsages.Count ?? 0; 631 int pageUsages = themeUsage?.PageUsages.Count ?? 0; 632 int pagePropertyUsages = themeUsage?.PagePropertyUsages.Count ?? 0; 633 int gridRowUsages = themeUsage?.GridRowUsages.Count ?? 0; 634 int paragraphUsages = themeUsage?.ParagraphUsages.Count ?? 0; 635 636 <!-- Modal --> 637 <div class="modal fade" id="themeUsage_@(Model.ID)" tabindex="-1" aria-labelledby="usagesModelLabel" aria-hidden="true"> 638 <div class="modal-dialog modal-xl modal-dialog-centered modal-dialog-scrollable"> 639 <div class="modal-content"> 640 <div class="modal-header"> 641 <h5 class="modal-title" id="usagesModelLabel">@allUsages Usages of '@(themeName)' (@(themeClassName))</h5> 642 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 643 </div> 644 <div class="modal-body"> 645 <div class="accordion" id="accordionWrapper"> 646 @if(websiteUsages > 0) 647 { 648 <div class="accordion-item"> 649 <h2 class="accordion-header" id="headingWebsites"> 650 <button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseWebsites" aria-expanded="true" aria-controls="collapseWebsites"> 651 <span class="pe-2">Websites</span><span class="badge rounded-pill bg-secondary text-light">@websiteUsages</span> 652 </button> 653 </h2> 654 <div id="collapseWebsites" class="accordion-collapse collapse show" aria-labelledby="headingWebsites" data-bs-parent="#accordionWrapper"> 655 <div class="accordion-body"> 656 <ul> 657 @foreach (var usage in themeUsage.WebsiteUsages) 658 { 659 var website = Dynamicweb.Content.Services.Areas.GetArea(usage); 660 <li><span class="fw-bold">@website.Name</span>@($" (ID: {usage})")</li> 661 } 662 </ul> 663 </div> 664 </div> 665 </div> 666 } 667 @if (pageUsages > 0) 668 { 669 var pages = Dynamicweb.Content.Services.Pages.GetPages(themeUsage.PageUsages.ToArray()); 670 671 <div class="accordion-item"> 672 <h2 class="accordion-header" id="headingPages"> 673 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapsePages" aria-expanded="false" aria-controls="collapsePages"> 674 <span class="pe-2">Pages</span><span class="badge rounded-pill bg-secondary text-light">@pageUsages</span> 675 </button> 676 </h2> 677 <div id="collapsePages" class="accordion-collapse collapse" aria-labelledby="headingPages" data-bs-parent="#accordionWrapper"> 678 <div class="accordion-body"> 679 <ul> 680 @foreach (var usage in themeUsage.PageUsages) 681 { 682 <li><span class="fw-bold">@pages.FirstOrDefault(p => p.ID == usage).GetDisplayName()</span>@($" (ID: {usage})")</li> 683 } 684 </ul> 685 </div> 686 </div> 687 </div> 688 } 689 @if (pagePropertyUsages > 0) 690 { 691 var pages = Dynamicweb.Content.Services.Pages.GetPages(themeUsage.PageUsages.ToArray()); 692 693 <div class="accordion-item"> 694 <h2 class="accordion-header" id="headingPageProperties"> 695 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapsePageProperties" aria-expanded="false" aria-controls="collapsePageProperties"> 696 <span class="pe-2">Page Properties</span><span class="badge rounded-pill bg-secondary text-light">@pagePropertyUsages</span> 697 </button> 698 </h2> 699 <div id="collapsePageProperties" class="accordion-collapse collapse" aria-labelledby="headingPageProperties" data-bs-parent="#accordionWrapper"> 700 <div class="accordion-body"> 701 <ul> 702 @foreach (var usage in themeUsage.PagePropertyUsages) 703 { 704 <li><span class="fw-bold">@pages.FirstOrDefault(p => p.ID == usage).GetDisplayName()</span>@($" (ID: {usage})")</li> 705 } 706 </ul> 707 </div> 708 </div> 709 </div> 710 } 711 @if (gridRowUsages > 0) 712 { 713 var rows = Dynamicweb.Content.Services.Grids.GetGridRows().Where(r => themeUsage.GridRowUsages.Contains(r.ID)).ToList(); 714 715 <div class="accordion-item"> 716 <h2 class="accordion-header" id="headingRows"> 717 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseRows" aria-expanded="false" aria-controls="collapseRows"> 718 <span class="pe-2">Rows</span><span class="badge rounded-pill bg-secondary text-light">@gridRowUsages</span> 719 </button> 720 </h2> 721 <div id="collapseRows" class="accordion-collapse collapse" aria-labelledby="headingRows" data-bs-parent="#accordionWrapper"> 722 <div class="accordion-body"> 723 <ul> 724 @foreach (var usage in themeUsage.GridRowUsages) 725 { 726 var row = rows.FirstOrDefault(r => r.ID == usage); 727 var page = Dynamicweb.Content.Services.Pages.GetPage(row.PageId); 728 <li>Page: <span class="fw-bold">@(page.GetDisplayName())</span>@($"( ID: {page.ID}) - Row: ")<span class="fw-bold">@(row.DefinitionId)</span>@($" (ID: {usage})")</li> 729 } 730 </ul> 731 </div> 732 </div> 733 </div> 734 } 735 @if (paragraphUsages > 0) 736 { 737 var paragraphs = Dynamicweb.Content.Services.Paragraphs.GetParagraphs().Where(p => themeUsage.ParagraphUsages.Contains(p.ID)).ToList(); 738 739 <div class="accordion-item"> 740 <h2 class="accordion-header" id="headingParagraphs"> 741 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseParagraphs" aria-expanded="false" aria-controls="collapseParagraphs"> 742 <span class="pe-2">Paragraphs</span><span class="badge rounded-pill bg-secondary text-light">@paragraphUsages</span> 743 </button> 744 </h2> 745 <div id="collapseParagraphs" class="accordion-collapse collapse" aria-labelledby="headingParagraphs" data-bs-parent="#accordionWrapper"> 746 <div class="accordion-body"> 747 <ul> 748 @foreach (var usage in themeUsage.ParagraphUsages) 749 { 750 var paragraph = paragraphs.FirstOrDefault(p => p.ID == usage); 751 var page = Dynamicweb.Content.Services.Pages.GetPage(paragraph.PageID); 752 <li>Page: <span class="fw-bold">@(page.GetDisplayName())</span>@($"( ID: {page.ID}) - Paragraph: ")<span class="fw-bold">@paragraph.Header</span>@($" (ID: {usage})")</li> 753 } 754 </ul> 755 </div> 756 </div> 757 </div> 758 } 759 </div> 760 </div> 761 </div> 762 </div> 763 </div> 764 } 765 </div> 766 } 767 } 768 </div> 769 </div> 770 } 771 <div class="g-col-12"> 772 <hr /> 773 </div> 774
Error executing template "Designs/Swift/Paragraph/Swift_Theme.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_f772bb33646d4d3c8ae681f5d1cf870b.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.ParagraphViewModel> 2 @using System.Globalization 3 @using System.Drawing 4 @using Dynamicweb.Core 5 6 @functions{ 7 // Get Hex Color from color picker and translate to RGB color 8 public static string HexToRGB(string hexString) 9 { 10 if (hexString.Equals("none") || string.IsNullOrEmpty(hexString)) 11 return hexString; 12 13 //replace # occurences 14 if (hexString.IndexOf('#') != -1) 15 hexString = hexString.Replace("#", ""); 16 try 17 { 18 int r, g, b = 0; 19 r = int.Parse(hexString.Substring(0, 2), NumberStyles.AllowHexSpecifier); 20 g = int.Parse(hexString.Substring(2, 2), NumberStyles.AllowHexSpecifier); 21 b = int.Parse(hexString.Substring(4, 2), NumberStyles.AllowHexSpecifier); 22 23 string rgb = r + "," + g + "," + b; 24 return rgb; 25 } 26 catch (System.Exception) 27 { 28 return hexString; 29 } 30 } 31 32 public static string GetColorSquare(string hex) 33 { 34 return $"<div class=\"swift-checkered d-inline-block me-1\" style=\"height:1.25rem;width:1.25rem;\"><div style=\"background-color: {hex};border:.0625rem solid grey\" class=\"w-100 h-100\"></div></div>"; 35 } 36 37 public class HSL 38 { 39 public double h { get; set; } 40 public double s { get; set; } 41 public double l { get; set; } 42 } 43 44 //Convert HEX to HSL 45 public static HSL HexToHsl(string hexString) 46 { 47 System.Drawing.Color color = System.Drawing.ColorTranslator.FromHtml(hexString); 48 49 float _R = (color.R / 255f); 50 float _G = (color.G / 255f); 51 float _B = (color.B / 255f); 52 53 float _Min = Math.Min(Math.Min(_R, _G), _B); 54 float _Max = Math.Max(Math.Max(_R, _G), _B); 55 float _Delta = _Max - _Min; 56 57 float S = 0; 58 float L = (float)((_Max + _Min) / 2.0f); 59 60 if (_Delta != 0) 61 { 62 if (L < 0.5f) 63 { 64 S = (float)(_Delta / (_Max + _Min)); 65 } 66 else 67 { 68 S = (float)(_Delta / (2.0f - _Max - _Min)); 69 } 70 } 71 72 HSL hsl = new HSL(); 73 hsl.h = Math.Round(color.GetHue(), 2); 74 hsl.s = Math.Round(S, 2) * 100; 75 hsl.l = Math.Round(L, 2) * 100; 76 77 return hsl; 78 } 79 80 //Find contrast color (white, black) 81 public static Color GetContrastColor(string hexString) 82 { 83 System.Drawing.Color bg = System.Drawing.ColorTranslator.FromHtml(hexString); 84 85 int nThreshold = 105; 86 int bgDelta = Convert.ToInt32((bg.R * 0.299) + (bg.G * 0.587) + 87 (bg.B * 0.114)); 88 89 Color foreColor = (255 - bgDelta < nThreshold) ? Color.Black : Color.White; 90 return foreColor; 91 } 92 93 public string GetColorVariation(string hexString) 94 { 95 var contrast = GetContrastColor(hexString); 96 HSL currentHslColor = HexToHsl(hexString); 97 HSL variantHslColor = currentHslColor; 98 99 if (contrast == Color.Black) 100 { 101 variantHslColor.l = 85; //The background is dark - Color should be light 102 } 103 else 104 { 105 variantHslColor.l = 40; //The background is light - Color should be less light 106 } 107 108 //Color is very light 109 if (currentHslColor.l > 85) 110 { 111 variantHslColor.l = 95; 112 } 113 114 string colorString = variantHslColor.h + ", " + variantHslColor.s + "%, " + variantHslColor.l + "%"; 115 return colorString; 116 } 117 118 private class ThemeDatabaseUsage 119 { 120 public string TableName { get; set; } 121 public string ColumnName { get; set; } 122 } 123 124 private class ThemeUsage 125 { 126 public string Id { get; set; } 127 public List<int> WebsiteUsages { get; set; } = new List<int>(); 128 public List<int> PageUsages { get; set; } = new List<int>(); 129 public List<int> PagePropertyUsages { get; set; } = new List<int>(); 130 public List<int> GridRowUsages { get; set; } = new List<int>(); 131 public List<int> ParagraphUsages { get; set; } = new List<int>(); 132 public int AllUsages { get; set; } = 0; 133 } 134 135 private List<ThemeDatabaseUsage> GetThemeColumnsFromSchema() 136 { 137 string cacheKey = "ThemesFromDatabase"; 138 if (Dynamicweb.Context.Current.Items.Contains(cacheKey)) 139 { 140 return (List<ThemeDatabaseUsage>)Dynamicweb.Context.Current.Items[cacheKey]; 141 } 142 var schema = Dynamicweb.Data.Database.CreateDataReader("SELECT table_name, column_name FROM information_schema.columns WHERE column_name LIKE '%Theme%'"); 143 144 var rawData = new List<ThemeDatabaseUsage>(); 145 while (schema.Read()) { 146 rawData.Add(new ThemeDatabaseUsage {TableName = schema["table_name"].ToString(), ColumnName = schema["column_name"].ToString() }); 147 } 148 Dynamicweb.Context.Current.Items[cacheKey] = rawData; 149 return rawData; 150 } 151 152 private List<ThemeUsage> GetAllThemeUsages() 153 { 154 string cacheKey = "ThemeUsages"; 155 if (Dynamicweb.Context.Current.Items.Contains(cacheKey)) 156 { 157 return (List<ThemeUsage>)Dynamicweb.Context.Current.Items[cacheKey]; 158 } 159 160 var rawData = GetThemeColumnsFromSchema(); 161 var themeUsages = new List<ThemeUsage>(); 162 163 var sqlQuery = ""; 164 foreach (var row in rawData) 165 { 166 if (!string.IsNullOrEmpty(sqlQuery)) { sqlQuery += " UNION ALL "; } 167 sqlQuery += $"SELECT '{row.TableName}' AS TableName, '{row.ColumnName}' AS ColumnName, {row.ColumnName} AS ColumnValue, Id, AreaId, Page.PageId, PageProperties.PageId as 'PagePropertiesId', GridRowId, ParagraphId" + 168 $" FROM [{row.TableName}]" + 169 $" LEFT JOIN Area ON AreaItemType = '{row.TableName.Replace("ItemType_","")}' AND AreaItemId = Id" + 170 $" LEFT JOIN Page ON PageItemType = '{row.TableName.Replace("ItemType_","")}' AND PageItemId = Id" + 171 $" LEFT JOIN Page as PageProperties ON AreaItemTypePageProperty = '{row.TableName.Replace("ItemType_","")}' AND PageProperties.PagePropertyItemId = Id" + 172 $" LEFT JOIN GridRow ON GridRowItemType = '{row.TableName.Replace("ItemType_","")}' AND GridRowItemId = Id" + 173 $" LEFT JOIN Paragraph ON ParagraphItemType = '{row.TableName.Replace("ItemType_","")}' AND ParagraphItemId = Id" + 174 $" WHERE {row.ColumnName} != '' AND {row.ColumnName} IS NOT NULL" + 175 $" AND (AreaId IS NULL OR AreaId = {Pageview.AreaID})" + 176 $" AND (Page.PageId IS NULL OR Page.PageAreaId = {Pageview.AreaID})" + 177 $" AND (PageProperties.PageId IS NULL OR PageProperties.PageAreaId = {Pageview.AreaID})" + 178 $" AND (GridRowId IS NULL OR GridRowPageId IN (SELECT value From string_split(@pageIds, ',')))" + 179 $" AND (ParagraphId IS NULL OR ParagraphPageId IN (SELECT value From string_split(@pageIds, ',')))"; 180 181 var maxMsSqlServerCharLimit = 60000; 182 if (sqlQuery.Length > maxMsSqlServerCharLimit) 183 { 184 UpdateThemeUsages(sqlQuery, themeUsages); 185 sqlQuery = ""; 186 } 187 } 188 189 if (!string.IsNullOrEmpty(sqlQuery)) 190 { 191 UpdateThemeUsages(sqlQuery, themeUsages); 192 } 193 UpdateAllUsages(themeUsages); 194 195 Dynamicweb.Context.Current.Items[cacheKey] = themeUsages; 196 return themeUsages; 197 } 198 199 private void UpdateThemeUsages(string sqlQuery, List<ThemeUsage> themeUsages) 200 { 201 sqlQuery = $"DECLARE @pageIds NVARCHAR(max) = (SELECT STRING_AGG(PageId,',') FROM Page WHERE PageAreaId = {Pageview.AreaID}) " + sqlQuery; 202 var rawData = Dynamicweb.Data.Database.CreateDataReader(sqlQuery); 203 while (rawData.Read()) 204 { 205 var themeId = rawData["ColumnValue"].ToString(); 206 var themeUsageExists = themeUsages.FirstOrDefault(t => t.Id == themeId) != null; 207 if (!themeUsageExists) 208 { 209 themeUsages.Add(new ThemeUsage { Id = themeId }); 210 } 211 212 var themeUsage = themeUsages.FirstOrDefault(t => t.Id == themeId); 213 if (themeUsage == null) continue; 214 215 if (Converter.ToInt32(rawData["AreaId"].ToString()) > 0) { themeUsage.WebsiteUsages.Add(Converter.ToInt32(rawData["AreaId"].ToString())); } 216 if (Converter.ToInt32(rawData["PageId"].ToString()) > 0) { themeUsage.PageUsages.Add(Converter.ToInt32(rawData["PageId"].ToString())); } 217 if (Converter.ToInt32(rawData["PagePropertiesId"].ToString()) > 0) { themeUsage.PagePropertyUsages.Add(Converter.ToInt32(rawData["PagePropertiesId"].ToString())); } 218 if (Converter.ToInt32(rawData["GridRowId"].ToString()) > 0) { themeUsage.GridRowUsages.Add(Converter.ToInt32(rawData["GridRowId"].ToString())); } 219 if (Converter.ToInt32(rawData["ParagraphId"].ToString()) > 0) { themeUsage.ParagraphUsages.Add(Converter.ToInt32(rawData["ParagraphId"].ToString())); } 220 } 221 } 222 223 private void UpdateAllUsages(List<ThemeUsage> themeUsages) 224 { 225 foreach (var themeUsage in themeUsages) 226 { 227 int websiteUsages = themeUsage?.WebsiteUsages.Count ?? 0; 228 int pageUsages = themeUsage?.PageUsages.Count ?? 0; 229 int pagePropertyUsages = themeUsage?.PagePropertyUsages.Count ?? 0; 230 int gridRowUsages = themeUsage?.GridRowUsages.Count ?? 0; 231 int paragraphUsages = themeUsage?.ParagraphUsages.Count ?? 0; 232 themeUsage.AllUsages = websiteUsages + pageUsages + pagePropertyUsages + gridRowUsages + paragraphUsages; 233 } 234 } 235 236 private ThemeUsage GetThemeUsageById(string themeId) 237 { 238 var themeUsages = GetAllThemeUsages(); 239 return themeUsages.FirstOrDefault(t => t.Id == themeId); 240 } 241 242 } 243 244 @{ 245 string themeName = !string.IsNullOrWhiteSpace(Model.Item.GetString("Name")) ? Model.Item.GetString("Name") : "Default"; 246 string themeClassName = !string.IsNullOrWhiteSpace(Model.Item.GetString("CSSClassName")) ? Model.Item.GetString("CSSClassName").Replace(" ", "").Trim().ToLower() : ""; 247 248 //Theme colors 249 string foregroundColor = Model.Item.GetString("ForegroundColor", "currentColor"); 250 string backgroundColor = Model.Item.GetString("BackgroundColor", "transparent"); 251 string accentColor = Model.Item.GetString("accentColor", "currentColor"); 252 string borderColor = Model.Item.GetString("BorderColor", "transparent"); 253 254 //Primary button 255 string buttonPrimaryForegroundColor = Model.Item.GetString("ButtonPrimaryForegroundColor", "currentColor"); 256 string buttonPrimaryBackgroundColor = Model.Item.GetString("ButtonPrimaryBackgroundColor", "transparent"); 257 string buttonPrimaryBorderColor = Model.Item.GetString("ButtonPrimaryBorderColor", "transparent"); 258 259 string buttonPrimaryHoverBackgroundColorAuto = "transparent"; 260 if (buttonPrimaryBackgroundColor != "transparent") 261 { 262 buttonPrimaryHoverBackgroundColorAuto = "hsl(" + GetColorVariation(buttonPrimaryBackgroundColor) + ")"; 263 } 264 string buttonPrimaryHoverForegroundColor = Model.Item.GetString("ButtonPrimaryHoverForegroundColor", buttonPrimaryForegroundColor); 265 string buttonPrimaryHoverBackgroundColor = Model.Item.GetString("ButtonPrimaryHoverBackgroundColor", buttonPrimaryHoverBackgroundColorAuto); 266 string buttonPrimaryHoverBorderColor = Model.Item.GetString("ButtonPrimaryHoverBorderColor", buttonPrimaryBorderColor); 267 268 //Secondary buton 269 string buttonSecondaryForegroundColor = Model.Item.GetString("ButtonSecondaryForegroundColor", "currentColor"); 270 string buttonSecondaryBackgroundColor = Model.Item.GetString("ButtonSecondaryBackgroundColor", "transparent"); 271 string buttonSecondaryBorderColor = Model.Item.GetString("ButtonSecondaryBorderColor", "transparent"); 272 273 string buttonSecondaryHoverBackgroundColorAuto = "transparent"; 274 if (buttonSecondaryBackgroundColor != "transparent") 275 { 276 buttonSecondaryHoverBackgroundColorAuto = "hsl(" + GetColorVariation(buttonSecondaryBackgroundColor) + ")"; 277 } 278 string buttonSecondaryHoverForegroundColor = Model.Item.GetString("ButtonSecondaryHoverForegroundColor", buttonSecondaryForegroundColor); 279 string buttonSecondaryHoverBackgroundColor = Model.Item.GetString("ButtonSecondaryHoverBackgroundColor", buttonSecondaryHoverBackgroundColorAuto); 280 string buttonSecondaryHoverBorderColor = Model.Item.GetString("ButtonSecondaryHoverBorderColor", buttonSecondaryBorderColor); 281 282 //Link buton 283 string buttonLinkColor = Model.Item.GetString("ButtonLinkColor", "currentColor"); 284 string buttonLinkHoverColorAuto = "currentColor"; 285 if (buttonLinkColor != "currentColor") 286 { 287 buttonLinkHoverColorAuto = "hsl(" + GetColorVariation(buttonLinkColor) + ")"; 288 } 289 string buttonLinkHoverColor = Model.Item.GetString("ButtonLinkHoverColor", buttonLinkHoverColorAuto); 290 291 string accessibilityOutlineColor = Model.Item.GetString("OutlineColor") != null ? Model.Item.GetString("OutlineColor") : ""; 292 293 if (!string.IsNullOrEmpty(themeClassName)) 294 { 295 var sb = new System.Text.StringBuilder(); 296 297 sb.AppendLine("." + themeClassName + "{"); 298 if (!string.IsNullOrEmpty(foregroundColor)) 299 { 300 sb.AppendLine($"--swift-foreground-color: {foregroundColor};"); 301 sb.AppendLine($"--swift-foreground-color-rgb: {HexToRGB(foregroundColor)};"); 302 } 303 if (!string.IsNullOrEmpty(backgroundColor)) 304 { 305 sb.AppendLine($"--swift-background-color: {backgroundColor};"); 306 sb.AppendLine($"--swift-background-color-rgb: {HexToRGB(backgroundColor)};"); 307 } 308 if (!string.IsNullOrEmpty(accentColor)) 309 { 310 sb.AppendLine($"--swift-accent-color: {accentColor};"); 311 sb.AppendLine($"--swift-accent-color-rgb: {HexToRGB(accentColor)};"); 312 } 313 if (!string.IsNullOrEmpty(borderColor)) 314 { 315 sb.AppendLine($"--swift-border-color: {borderColor};"); 316 sb.AppendLine($"--swift-border-color-rgb: {HexToRGB(borderColor)};"); 317 } 318 319 if (!string.IsNullOrEmpty(buttonPrimaryBackgroundColor)) 320 { 321 sb.AppendLine($"--swift-button-primary-background-color: {buttonPrimaryBackgroundColor};"); 322 sb.AppendLine($"--swift-button-primary-background-color-rgb: {HexToRGB(buttonPrimaryBackgroundColor)};"); 323 } 324 if (!string.IsNullOrEmpty(buttonPrimaryForegroundColor)) 325 { 326 sb.AppendLine($"--swift-button-primary-foreground-color: {buttonPrimaryForegroundColor};"); 327 sb.AppendLine($"--swift-button-primary-foreground-color-rgb: {HexToRGB(buttonPrimaryForegroundColor)};"); 328 } 329 if (!string.IsNullOrEmpty(buttonPrimaryBorderColor)) 330 { 331 sb.AppendLine($"--swift-button-primary-border-color: {buttonPrimaryBorderColor};"); 332 sb.AppendLine($"--swift-button-primary-border-color-rgb: {HexToRGB(buttonPrimaryBorderColor)};"); 333 } 334 335 if (!string.IsNullOrEmpty(buttonPrimaryHoverBackgroundColor)) 336 { 337 sb.AppendLine($"--swift-button-primary-hover-background-color: {buttonPrimaryHoverBackgroundColor};"); 338 sb.AppendLine($"--swift-button-primary-hover-background-color-rgb: {HexToRGB(buttonPrimaryHoverBackgroundColor)};"); 339 } 340 if (!string.IsNullOrEmpty(buttonPrimaryHoverForegroundColor)) 341 { 342 sb.AppendLine($"--swift-button-primary-hover-foreground-color: {buttonPrimaryHoverForegroundColor};"); 343 sb.AppendLine($"--swift-button-primary-hover-foreground-color-rgb: {HexToRGB(buttonPrimaryHoverForegroundColor)};"); 344 } 345 if (!string.IsNullOrEmpty(buttonPrimaryHoverBorderColor)) 346 { 347 sb.AppendLine($"--swift-button-primary-hover-border-color: {buttonPrimaryHoverBorderColor};"); 348 sb.AppendLine($"--swift-button-primary-hover-border-color-rgb: {HexToRGB(buttonPrimaryHoverBorderColor)};"); 349 } 350 351 if (!string.IsNullOrEmpty(buttonSecondaryBackgroundColor)) 352 { 353 sb.AppendLine($"--swift-button-secondary-background-color: {buttonSecondaryBackgroundColor};"); 354 sb.AppendLine($"--swift-button-secondary-background-color-rgb: {HexToRGB(buttonSecondaryBackgroundColor)};"); 355 } 356 if (!string.IsNullOrEmpty(buttonSecondaryForegroundColor)) 357 { 358 sb.AppendLine($"--swift-button-secondary-foreground-color: {buttonSecondaryForegroundColor};"); 359 sb.AppendLine($"--swift-button-secondary-foreground-color-rgb: {HexToRGB(buttonSecondaryForegroundColor)};"); 360 } 361 if (!string.IsNullOrEmpty(buttonSecondaryBorderColor)) 362 { 363 sb.AppendLine($"--swift-button-secondary-border-color: {buttonSecondaryBorderColor};"); 364 sb.AppendLine($"--swift-button-secondary-border-color-rgb: {HexToRGB(buttonSecondaryBorderColor)};"); 365 } 366 if (!string.IsNullOrEmpty(buttonSecondaryHoverBackgroundColor)) 367 { 368 sb.AppendLine($"--swift-button-secondary-hover-background-color: {buttonSecondaryHoverBackgroundColor};"); 369 sb.AppendLine($"--swift-button-secondary-hover-background-color-rgb: {HexToRGB(buttonSecondaryHoverBackgroundColor)};"); 370 } 371 if (!string.IsNullOrEmpty(buttonSecondaryHoverForegroundColor)) 372 { 373 sb.AppendLine($"--swift-button-secondary-hover-foreground-color: {buttonSecondaryHoverForegroundColor};"); 374 sb.AppendLine($"--swift-button-secondary-hover-foreground-color-rgb: {HexToRGB(buttonSecondaryHoverForegroundColor)};"); 375 } 376 if (!string.IsNullOrEmpty(buttonSecondaryHoverBorderColor)) 377 { 378 sb.AppendLine($"--swift-button-secondary-hover-border-color: {buttonSecondaryHoverBorderColor};"); 379 sb.AppendLine($"--swift-button-secondary-hover-border-color-rgb: {HexToRGB(buttonSecondaryHoverBorderColor)};"); 380 } 381 if (!string.IsNullOrEmpty(buttonLinkColor)) 382 { 383 sb.AppendLine($"--swift-button-link-color: {buttonLinkColor};"); 384 sb.AppendLine($"--swift-button-link-color-rgb: {HexToRGB(buttonLinkColor)};"); 385 } 386 if (!string.IsNullOrEmpty(buttonLinkHoverColor)) 387 { 388 sb.AppendLine($"--swift-button-link-hover-color: {buttonLinkHoverColor};"); 389 sb.AppendLine($"--swift-button-link-hover-color-rgb: {HexToRGB(buttonLinkHoverColor)};"); 390 } 391 if (!string.IsNullOrEmpty(accessibilityOutlineColor)) 392 { 393 sb.AppendLine($"--swift-a11y-outline-color: {accessibilityOutlineColor};"); 394 sb.AppendLine($"--swift-a11y-outline-color-rgb: {HexToRGB(accessibilityOutlineColor)};"); 395 } 396 sb.AppendLine("}"); 397 398 Dynamicweb.Core.Helpers.TextFileHelper.WriteTextFile(sb.ToString() + Environment.NewLine, Dynamicweb.Context.Current.Server.MapPath($"/Files/Templates/Designs/Swift/_parsed/Swift_css/Swift_theme_styles_{Dynamicweb.Content.Services.Pages.GetPage(Model.PageID).AreaId}.tmp"), true); 399 } 400 } 401 402 @if (themeClassName != "") 403 { 404 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 405 406 <div class="g-col-12 g-col-lg-4"> 407 <div class="swift-checkered p-3"> 408 <div class="theme-option theme @themeClassName p-lg-3"> 409 <div class="mb-2 pb-1 border-bottom d-flex align-items-center"> 410 <span class="icon-3 me-2"> 411 @ReadFile(iconPath + "sun.svg") 412 </span> 413 <h4 class="m-0 p-0">@themeName</h4> 414 </div> 415 <div class="d-flex flex-column gap-1 mb-2"> 416 <p class="mb-0">This is the body text of the theme.</p> 417 <p class="mb-0" style="color: var(--swift-accent-color)">This is the accent color of the theme.</p> 418 </div> 419 <div> 420 <button class="btn btn-primary me-1">Primary</button> 421 <button class="btn btn-secondary">Secondary</button> 422 <button class="btn btn-link">Link</button> 423 </div> 424 </div> 425 </div> 426 </div> 427 <div class="g-col-12 g-col-lg-8 mb-4 mb-lg-0"> 428 <div class="grid fs-7"> 429 <div class="g-col-12 g-col-lg-3"> 430 <table class="table table-borderless table-sm w-100"> 431 <thead> 432 <tr> 433 <th colspan="2" class="fw-bold">@Translate("Base")</th> 434 </tr> 435 </thead> 436 <tr> 437 <td>@Translate("Foreground")</td> 438 <td>@GetColorSquare(foregroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@foregroundColor');">@foregroundColor</a></td> 439 </tr> 440 <tr> 441 <td>@Translate("Background")</td> 442 <td>@GetColorSquare(backgroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@backgroundColor');">@backgroundColor</a></td> 443 </tr> 444 <tr> 445 <td>@Translate("Accent")</td> 446 <td>@GetColorSquare(accentColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@accentColor');">@accentColor</a></td> 447 </tr> 448 <tr> 449 <td>@Translate("Border")</td> 450 <td>@GetColorSquare(borderColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@borderColor');">@borderColor</a></td> 451 </tr> 452 </table> 453 </div> 454 <div class="g-col-12 g-col-lg-3"> 455 <table class="table table-borderless table-sm w-100"> 456 <thead> 457 <tr> 458 <th colspan="2" class="fw-bold">@Translate("Primary button")</th> 459 </tr> 460 </thead> 461 <tr> 462 <td>@Translate("Foreground")</td> 463 <td>@GetColorSquare(buttonPrimaryForegroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonPrimaryForegroundColor');">@buttonPrimaryForegroundColor</a></td> 464 </tr> 465 <tr> 466 <td>@Translate("Background")</td> 467 <td>@GetColorSquare(buttonPrimaryBackgroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonPrimaryBackgroundColor');">@buttonPrimaryBackgroundColor</a></td> 468 </tr> 469 @if (!string.IsNullOrEmpty(buttonPrimaryBorderColor)) 470 { 471 <tr> 472 <td>@Translate("Border")</td> 473 <td>@GetColorSquare(buttonPrimaryBorderColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonPrimaryBorderColor');">@buttonPrimaryBorderColor</a></td> 474 </tr> 475 } 476 <thead> 477 <tr> 478 <th colspan="2" class="fw-bold">@Translate("Primary button hover")</th> 479 </tr> 480 </thead> 481 @if (!string.IsNullOrEmpty(buttonPrimaryHoverForegroundColor)) 482 { 483 <tr> 484 <td>@Translate("Foreground")</td> 485 <td>@GetColorSquare(buttonPrimaryHoverForegroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonPrimaryHoverForegroundColor');">@buttonPrimaryHoverForegroundColor</a></td> 486 </tr> 487 } 488 @if (!string.IsNullOrEmpty(buttonPrimaryHoverBackgroundColor)) 489 { 490 <tr> 491 <td>@Translate("Background")</td> 492 <td>@GetColorSquare(buttonPrimaryHoverBackgroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonPrimaryHoverBackgroundColor');">@buttonPrimaryHoverBackgroundColor</a></td> 493 </tr> 494 } 495 @if (!string.IsNullOrEmpty(buttonPrimaryHoverBorderColor)) 496 { 497 <tr> 498 <td>@Translate("Border")</td> 499 <td>@GetColorSquare(buttonPrimaryHoverBorderColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonPrimaryHoverBorderColor');">@buttonPrimaryHoverBorderColor</a></td> 500 </tr> 501 } 502 </table> 503 </div> 504 <div class="g-col-12 g-col-lg-3"> 505 <table class="table table-borderless table-sm w-100"> 506 <thead> 507 <tr> 508 <th colspan="2" class="fw-bold">@Translate("Secondary button")</th> 509 </tr> 510 </thead> 511 <tr> 512 <td>@Translate("Foreground")</td> 513 <td>@GetColorSquare(buttonSecondaryForegroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonSecondaryForegroundColor');">@buttonSecondaryForegroundColor</a></td> 514 </tr> 515 <tr> 516 <td>@Translate("Background")</td> 517 <td>@GetColorSquare(buttonSecondaryBackgroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonSecondaryBackgroundColor');">@buttonSecondaryBackgroundColor</a></td> 518 </tr> 519 @if (!string.IsNullOrEmpty(buttonSecondaryBorderColor)) 520 { 521 <tr> 522 <td>@Translate("Border")</td> 523 <td>@GetColorSquare(buttonSecondaryBorderColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonSecondaryBorderColor');">@buttonSecondaryBorderColor</a></td> 524 </tr> 525 } 526 <thead> 527 <tr> 528 <th colspan="2" class="fw-bold">@Translate("Secondary button hover")</th> 529 </tr> 530 </thead> 531 @if (!string.IsNullOrEmpty(buttonSecondaryHoverForegroundColor)) 532 { 533 <tr> 534 <td>@Translate("Foreground")</td> 535 <td>@GetColorSquare(buttonSecondaryHoverForegroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonSecondaryHoverForegroundColor');">@buttonSecondaryHoverForegroundColor</a></td> 536 </tr> 537 } 538 @if (!string.IsNullOrEmpty(buttonSecondaryHoverBackgroundColor)) 539 { 540 <tr> 541 <td>@Translate("Background")</td> 542 <td>@GetColorSquare(buttonSecondaryHoverBackgroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonSecondaryHoverBackgroundColor');">@buttonSecondaryHoverBackgroundColor</a></td> 543 </tr> 544 } 545 @if (!string.IsNullOrEmpty(buttonSecondaryHoverBorderColor)) 546 { 547 <tr> 548 <td>@Translate("Border")</td> 549 <td>@GetColorSquare(buttonSecondaryHoverBorderColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonSecondaryHoverBorderColor');">@buttonSecondaryHoverBorderColor</a></td> 550 </tr> 551 } 552 @if (!string.IsNullOrEmpty(accessibilityOutlineColor)) 553 { 554 <tr> 555 <td>@Translate("Focus outline")</td> 556 <td>@GetColorSquare(accessibilityOutlineColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@accessibilityOutlineColor');">@accessibilityOutlineColor</a></td> 557 </tr> 558 } 559 </table> 560 </div> 561 <div class="g-col-12 g-col-lg-3"> 562 <table class="table table-borderless table-sm w-100"> 563 <thead> 564 <tr> 565 <th colspan="2" class="fw-bold">@Translate("Link button")</th> 566 </tr> 567 </thead> 568 <tr> 569 <td>@Translate("Link color")</td> 570 <td>@GetColorSquare(buttonLinkColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonLinkColor');">@buttonLinkColor</a></td> 571 </tr> 572 <thead> 573 <tr> 574 <th colspan="2" class="fw-bold">@Translate("Link button hover")</th> 575 </tr> 576 </thead> 577 @if (!string.IsNullOrEmpty(buttonLinkHoverColor)) 578 { 579 <tr> 580 <td>@Translate("Link hover color")</td> 581 <td>@GetColorSquare(buttonLinkHoverColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonLinkHoverColor');">@buttonLinkHoverColor</a></td> 582 </tr> 583 } 584 585 @if (!string.IsNullOrEmpty(accessibilityOutlineColor)) 586 { 587 <tr> 588 <td>@Translate("Focus outline")</td> 589 <td>@GetColorSquare(accessibilityOutlineColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@accessibilityOutlineColor');">@accessibilityOutlineColor</a></td> 590 </tr> 591 } 592 </table> 593 </div> 594 <div class="g-col-12 g-col-lg-3"> 595 <table class="table table-borderless table-sm w-100"> 596 <tr> 597 <td class="fw-bold">@Translate("Implementation")</td> 598 </tr> 599 <tr> 600 <td> 601 @{ 602 string implementation = $"<div class=\"theme {themeClassName}\"></div>"; 603 } 604 <div class="text-muted"> 605 <code class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@themeClassName');"> 606 @Dynamicweb.Core.Encoders.HtmlEncoder.HtmlEncode(implementation) 607 </code> 608 </div> 609 </td> 610 </tr> 611 </table> 612 </div> 613 @{ 614 bool hasUsageData = Converter.ToBoolean(Dynamicweb.Context.Current.Request["auditUsages"]); 615 if (hasUsageData) 616 { 617 var themeUsage = GetThemeUsageById(themeClassName); 618 int allUsages = themeUsage?.AllUsages ?? 0; 619 string buttonAttributes = allUsages > 0 ? $"data-bs-toggle=\"modal\" data-bs-target=\"#themeUsage_{Model.ID}\"" : string.Empty; 620 string badgeStyle = allUsages > 0 ? "bg-success" : "bg-danger"; 621 622 <div class="g-col-12 g-col-lg-9 position-relative"> 623 <button type="button" class="btn btn-primary btn-sm position-absolute end-0" @buttonAttributes> 624 Usages 625 <span class="position-absolute top-0 start-100 translate-middle badge rounded-pill text-white @badgeStyle">@allUsages</span> 626 </button> 627 628 @if (allUsages > 0) 629 { 630 int websiteUsages = themeUsage?.WebsiteUsages.Count ?? 0; 631 int pageUsages = themeUsage?.PageUsages.Count ?? 0; 632 int pagePropertyUsages = themeUsage?.PagePropertyUsages.Count ?? 0; 633 int gridRowUsages = themeUsage?.GridRowUsages.Count ?? 0; 634 int paragraphUsages = themeUsage?.ParagraphUsages.Count ?? 0; 635 636 <!-- Modal --> 637 <div class="modal fade" id="themeUsage_@(Model.ID)" tabindex="-1" aria-labelledby="usagesModelLabel" aria-hidden="true"> 638 <div class="modal-dialog modal-xl modal-dialog-centered modal-dialog-scrollable"> 639 <div class="modal-content"> 640 <div class="modal-header"> 641 <h5 class="modal-title" id="usagesModelLabel">@allUsages Usages of '@(themeName)' (@(themeClassName))</h5> 642 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 643 </div> 644 <div class="modal-body"> 645 <div class="accordion" id="accordionWrapper"> 646 @if(websiteUsages > 0) 647 { 648 <div class="accordion-item"> 649 <h2 class="accordion-header" id="headingWebsites"> 650 <button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseWebsites" aria-expanded="true" aria-controls="collapseWebsites"> 651 <span class="pe-2">Websites</span><span class="badge rounded-pill bg-secondary text-light">@websiteUsages</span> 652 </button> 653 </h2> 654 <div id="collapseWebsites" class="accordion-collapse collapse show" aria-labelledby="headingWebsites" data-bs-parent="#accordionWrapper"> 655 <div class="accordion-body"> 656 <ul> 657 @foreach (var usage in themeUsage.WebsiteUsages) 658 { 659 var website = Dynamicweb.Content.Services.Areas.GetArea(usage); 660 <li><span class="fw-bold">@website.Name</span>@($" (ID: {usage})")</li> 661 } 662 </ul> 663 </div> 664 </div> 665 </div> 666 } 667 @if (pageUsages > 0) 668 { 669 var pages = Dynamicweb.Content.Services.Pages.GetPages(themeUsage.PageUsages.ToArray()); 670 671 <div class="accordion-item"> 672 <h2 class="accordion-header" id="headingPages"> 673 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapsePages" aria-expanded="false" aria-controls="collapsePages"> 674 <span class="pe-2">Pages</span><span class="badge rounded-pill bg-secondary text-light">@pageUsages</span> 675 </button> 676 </h2> 677 <div id="collapsePages" class="accordion-collapse collapse" aria-labelledby="headingPages" data-bs-parent="#accordionWrapper"> 678 <div class="accordion-body"> 679 <ul> 680 @foreach (var usage in themeUsage.PageUsages) 681 { 682 <li><span class="fw-bold">@pages.FirstOrDefault(p => p.ID == usage).GetDisplayName()</span>@($" (ID: {usage})")</li> 683 } 684 </ul> 685 </div> 686 </div> 687 </div> 688 } 689 @if (pagePropertyUsages > 0) 690 { 691 var pages = Dynamicweb.Content.Services.Pages.GetPages(themeUsage.PageUsages.ToArray()); 692 693 <div class="accordion-item"> 694 <h2 class="accordion-header" id="headingPageProperties"> 695 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapsePageProperties" aria-expanded="false" aria-controls="collapsePageProperties"> 696 <span class="pe-2">Page Properties</span><span class="badge rounded-pill bg-secondary text-light">@pagePropertyUsages</span> 697 </button> 698 </h2> 699 <div id="collapsePageProperties" class="accordion-collapse collapse" aria-labelledby="headingPageProperties" data-bs-parent="#accordionWrapper"> 700 <div class="accordion-body"> 701 <ul> 702 @foreach (var usage in themeUsage.PagePropertyUsages) 703 { 704 <li><span class="fw-bold">@pages.FirstOrDefault(p => p.ID == usage).GetDisplayName()</span>@($" (ID: {usage})")</li> 705 } 706 </ul> 707 </div> 708 </div> 709 </div> 710 } 711 @if (gridRowUsages > 0) 712 { 713 var rows = Dynamicweb.Content.Services.Grids.GetGridRows().Where(r => themeUsage.GridRowUsages.Contains(r.ID)).ToList(); 714 715 <div class="accordion-item"> 716 <h2 class="accordion-header" id="headingRows"> 717 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseRows" aria-expanded="false" aria-controls="collapseRows"> 718 <span class="pe-2">Rows</span><span class="badge rounded-pill bg-secondary text-light">@gridRowUsages</span> 719 </button> 720 </h2> 721 <div id="collapseRows" class="accordion-collapse collapse" aria-labelledby="headingRows" data-bs-parent="#accordionWrapper"> 722 <div class="accordion-body"> 723 <ul> 724 @foreach (var usage in themeUsage.GridRowUsages) 725 { 726 var row = rows.FirstOrDefault(r => r.ID == usage); 727 var page = Dynamicweb.Content.Services.Pages.GetPage(row.PageId); 728 <li>Page: <span class="fw-bold">@(page.GetDisplayName())</span>@($"( ID: {page.ID}) - Row: ")<span class="fw-bold">@(row.DefinitionId)</span>@($" (ID: {usage})")</li> 729 } 730 </ul> 731 </div> 732 </div> 733 </div> 734 } 735 @if (paragraphUsages > 0) 736 { 737 var paragraphs = Dynamicweb.Content.Services.Paragraphs.GetParagraphs().Where(p => themeUsage.ParagraphUsages.Contains(p.ID)).ToList(); 738 739 <div class="accordion-item"> 740 <h2 class="accordion-header" id="headingParagraphs"> 741 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseParagraphs" aria-expanded="false" aria-controls="collapseParagraphs"> 742 <span class="pe-2">Paragraphs</span><span class="badge rounded-pill bg-secondary text-light">@paragraphUsages</span> 743 </button> 744 </h2> 745 <div id="collapseParagraphs" class="accordion-collapse collapse" aria-labelledby="headingParagraphs" data-bs-parent="#accordionWrapper"> 746 <div class="accordion-body"> 747 <ul> 748 @foreach (var usage in themeUsage.ParagraphUsages) 749 { 750 var paragraph = paragraphs.FirstOrDefault(p => p.ID == usage); 751 var page = Dynamicweb.Content.Services.Pages.GetPage(paragraph.PageID); 752 <li>Page: <span class="fw-bold">@(page.GetDisplayName())</span>@($"( ID: {page.ID}) - Paragraph: ")<span class="fw-bold">@paragraph.Header</span>@($" (ID: {usage})")</li> 753 } 754 </ul> 755 </div> 756 </div> 757 </div> 758 } 759 </div> 760 </div> 761 </div> 762 </div> 763 </div> 764 } 765 </div> 766 } 767 } 768 </div> 769 </div> 770 } 771 <div class="g-col-12"> 772 <hr /> 773 </div> 774
Error executing template "Designs/Swift/Paragraph/Swift_Theme.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_f772bb33646d4d3c8ae681f5d1cf870b.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.ParagraphViewModel> 2 @using System.Globalization 3 @using System.Drawing 4 @using Dynamicweb.Core 5 6 @functions{ 7 // Get Hex Color from color picker and translate to RGB color 8 public static string HexToRGB(string hexString) 9 { 10 if (hexString.Equals("none") || string.IsNullOrEmpty(hexString)) 11 return hexString; 12 13 //replace # occurences 14 if (hexString.IndexOf('#') != -1) 15 hexString = hexString.Replace("#", ""); 16 try 17 { 18 int r, g, b = 0; 19 r = int.Parse(hexString.Substring(0, 2), NumberStyles.AllowHexSpecifier); 20 g = int.Parse(hexString.Substring(2, 2), NumberStyles.AllowHexSpecifier); 21 b = int.Parse(hexString.Substring(4, 2), NumberStyles.AllowHexSpecifier); 22 23 string rgb = r + "," + g + "," + b; 24 return rgb; 25 } 26 catch (System.Exception) 27 { 28 return hexString; 29 } 30 } 31 32 public static string GetColorSquare(string hex) 33 { 34 return $"<div class=\"swift-checkered d-inline-block me-1\" style=\"height:1.25rem;width:1.25rem;\"><div style=\"background-color: {hex};border:.0625rem solid grey\" class=\"w-100 h-100\"></div></div>"; 35 } 36 37 public class HSL 38 { 39 public double h { get; set; } 40 public double s { get; set; } 41 public double l { get; set; } 42 } 43 44 //Convert HEX to HSL 45 public static HSL HexToHsl(string hexString) 46 { 47 System.Drawing.Color color = System.Drawing.ColorTranslator.FromHtml(hexString); 48 49 float _R = (color.R / 255f); 50 float _G = (color.G / 255f); 51 float _B = (color.B / 255f); 52 53 float _Min = Math.Min(Math.Min(_R, _G), _B); 54 float _Max = Math.Max(Math.Max(_R, _G), _B); 55 float _Delta = _Max - _Min; 56 57 float S = 0; 58 float L = (float)((_Max + _Min) / 2.0f); 59 60 if (_Delta != 0) 61 { 62 if (L < 0.5f) 63 { 64 S = (float)(_Delta / (_Max + _Min)); 65 } 66 else 67 { 68 S = (float)(_Delta / (2.0f - _Max - _Min)); 69 } 70 } 71 72 HSL hsl = new HSL(); 73 hsl.h = Math.Round(color.GetHue(), 2); 74 hsl.s = Math.Round(S, 2) * 100; 75 hsl.l = Math.Round(L, 2) * 100; 76 77 return hsl; 78 } 79 80 //Find contrast color (white, black) 81 public static Color GetContrastColor(string hexString) 82 { 83 System.Drawing.Color bg = System.Drawing.ColorTranslator.FromHtml(hexString); 84 85 int nThreshold = 105; 86 int bgDelta = Convert.ToInt32((bg.R * 0.299) + (bg.G * 0.587) + 87 (bg.B * 0.114)); 88 89 Color foreColor = (255 - bgDelta < nThreshold) ? Color.Black : Color.White; 90 return foreColor; 91 } 92 93 public string GetColorVariation(string hexString) 94 { 95 var contrast = GetContrastColor(hexString); 96 HSL currentHslColor = HexToHsl(hexString); 97 HSL variantHslColor = currentHslColor; 98 99 if (contrast == Color.Black) 100 { 101 variantHslColor.l = 85; //The background is dark - Color should be light 102 } 103 else 104 { 105 variantHslColor.l = 40; //The background is light - Color should be less light 106 } 107 108 //Color is very light 109 if (currentHslColor.l > 85) 110 { 111 variantHslColor.l = 95; 112 } 113 114 string colorString = variantHslColor.h + ", " + variantHslColor.s + "%, " + variantHslColor.l + "%"; 115 return colorString; 116 } 117 118 private class ThemeDatabaseUsage 119 { 120 public string TableName { get; set; } 121 public string ColumnName { get; set; } 122 } 123 124 private class ThemeUsage 125 { 126 public string Id { get; set; } 127 public List<int> WebsiteUsages { get; set; } = new List<int>(); 128 public List<int> PageUsages { get; set; } = new List<int>(); 129 public List<int> PagePropertyUsages { get; set; } = new List<int>(); 130 public List<int> GridRowUsages { get; set; } = new List<int>(); 131 public List<int> ParagraphUsages { get; set; } = new List<int>(); 132 public int AllUsages { get; set; } = 0; 133 } 134 135 private List<ThemeDatabaseUsage> GetThemeColumnsFromSchema() 136 { 137 string cacheKey = "ThemesFromDatabase"; 138 if (Dynamicweb.Context.Current.Items.Contains(cacheKey)) 139 { 140 return (List<ThemeDatabaseUsage>)Dynamicweb.Context.Current.Items[cacheKey]; 141 } 142 var schema = Dynamicweb.Data.Database.CreateDataReader("SELECT table_name, column_name FROM information_schema.columns WHERE column_name LIKE '%Theme%'"); 143 144 var rawData = new List<ThemeDatabaseUsage>(); 145 while (schema.Read()) { 146 rawData.Add(new ThemeDatabaseUsage {TableName = schema["table_name"].ToString(), ColumnName = schema["column_name"].ToString() }); 147 } 148 Dynamicweb.Context.Current.Items[cacheKey] = rawData; 149 return rawData; 150 } 151 152 private List<ThemeUsage> GetAllThemeUsages() 153 { 154 string cacheKey = "ThemeUsages"; 155 if (Dynamicweb.Context.Current.Items.Contains(cacheKey)) 156 { 157 return (List<ThemeUsage>)Dynamicweb.Context.Current.Items[cacheKey]; 158 } 159 160 var rawData = GetThemeColumnsFromSchema(); 161 var themeUsages = new List<ThemeUsage>(); 162 163 var sqlQuery = ""; 164 foreach (var row in rawData) 165 { 166 if (!string.IsNullOrEmpty(sqlQuery)) { sqlQuery += " UNION ALL "; } 167 sqlQuery += $"SELECT '{row.TableName}' AS TableName, '{row.ColumnName}' AS ColumnName, {row.ColumnName} AS ColumnValue, Id, AreaId, Page.PageId, PageProperties.PageId as 'PagePropertiesId', GridRowId, ParagraphId" + 168 $" FROM [{row.TableName}]" + 169 $" LEFT JOIN Area ON AreaItemType = '{row.TableName.Replace("ItemType_","")}' AND AreaItemId = Id" + 170 $" LEFT JOIN Page ON PageItemType = '{row.TableName.Replace("ItemType_","")}' AND PageItemId = Id" + 171 $" LEFT JOIN Page as PageProperties ON AreaItemTypePageProperty = '{row.TableName.Replace("ItemType_","")}' AND PageProperties.PagePropertyItemId = Id" + 172 $" LEFT JOIN GridRow ON GridRowItemType = '{row.TableName.Replace("ItemType_","")}' AND GridRowItemId = Id" + 173 $" LEFT JOIN Paragraph ON ParagraphItemType = '{row.TableName.Replace("ItemType_","")}' AND ParagraphItemId = Id" + 174 $" WHERE {row.ColumnName} != '' AND {row.ColumnName} IS NOT NULL" + 175 $" AND (AreaId IS NULL OR AreaId = {Pageview.AreaID})" + 176 $" AND (Page.PageId IS NULL OR Page.PageAreaId = {Pageview.AreaID})" + 177 $" AND (PageProperties.PageId IS NULL OR PageProperties.PageAreaId = {Pageview.AreaID})" + 178 $" AND (GridRowId IS NULL OR GridRowPageId IN (SELECT value From string_split(@pageIds, ',')))" + 179 $" AND (ParagraphId IS NULL OR ParagraphPageId IN (SELECT value From string_split(@pageIds, ',')))"; 180 181 var maxMsSqlServerCharLimit = 60000; 182 if (sqlQuery.Length > maxMsSqlServerCharLimit) 183 { 184 UpdateThemeUsages(sqlQuery, themeUsages); 185 sqlQuery = ""; 186 } 187 } 188 189 if (!string.IsNullOrEmpty(sqlQuery)) 190 { 191 UpdateThemeUsages(sqlQuery, themeUsages); 192 } 193 UpdateAllUsages(themeUsages); 194 195 Dynamicweb.Context.Current.Items[cacheKey] = themeUsages; 196 return themeUsages; 197 } 198 199 private void UpdateThemeUsages(string sqlQuery, List<ThemeUsage> themeUsages) 200 { 201 sqlQuery = $"DECLARE @pageIds NVARCHAR(max) = (SELECT STRING_AGG(PageId,',') FROM Page WHERE PageAreaId = {Pageview.AreaID}) " + sqlQuery; 202 var rawData = Dynamicweb.Data.Database.CreateDataReader(sqlQuery); 203 while (rawData.Read()) 204 { 205 var themeId = rawData["ColumnValue"].ToString(); 206 var themeUsageExists = themeUsages.FirstOrDefault(t => t.Id == themeId) != null; 207 if (!themeUsageExists) 208 { 209 themeUsages.Add(new ThemeUsage { Id = themeId }); 210 } 211 212 var themeUsage = themeUsages.FirstOrDefault(t => t.Id == themeId); 213 if (themeUsage == null) continue; 214 215 if (Converter.ToInt32(rawData["AreaId"].ToString()) > 0) { themeUsage.WebsiteUsages.Add(Converter.ToInt32(rawData["AreaId"].ToString())); } 216 if (Converter.ToInt32(rawData["PageId"].ToString()) > 0) { themeUsage.PageUsages.Add(Converter.ToInt32(rawData["PageId"].ToString())); } 217 if (Converter.ToInt32(rawData["PagePropertiesId"].ToString()) > 0) { themeUsage.PagePropertyUsages.Add(Converter.ToInt32(rawData["PagePropertiesId"].ToString())); } 218 if (Converter.ToInt32(rawData["GridRowId"].ToString()) > 0) { themeUsage.GridRowUsages.Add(Converter.ToInt32(rawData["GridRowId"].ToString())); } 219 if (Converter.ToInt32(rawData["ParagraphId"].ToString()) > 0) { themeUsage.ParagraphUsages.Add(Converter.ToInt32(rawData["ParagraphId"].ToString())); } 220 } 221 } 222 223 private void UpdateAllUsages(List<ThemeUsage> themeUsages) 224 { 225 foreach (var themeUsage in themeUsages) 226 { 227 int websiteUsages = themeUsage?.WebsiteUsages.Count ?? 0; 228 int pageUsages = themeUsage?.PageUsages.Count ?? 0; 229 int pagePropertyUsages = themeUsage?.PagePropertyUsages.Count ?? 0; 230 int gridRowUsages = themeUsage?.GridRowUsages.Count ?? 0; 231 int paragraphUsages = themeUsage?.ParagraphUsages.Count ?? 0; 232 themeUsage.AllUsages = websiteUsages + pageUsages + pagePropertyUsages + gridRowUsages + paragraphUsages; 233 } 234 } 235 236 private ThemeUsage GetThemeUsageById(string themeId) 237 { 238 var themeUsages = GetAllThemeUsages(); 239 return themeUsages.FirstOrDefault(t => t.Id == themeId); 240 } 241 242 } 243 244 @{ 245 string themeName = !string.IsNullOrWhiteSpace(Model.Item.GetString("Name")) ? Model.Item.GetString("Name") : "Default"; 246 string themeClassName = !string.IsNullOrWhiteSpace(Model.Item.GetString("CSSClassName")) ? Model.Item.GetString("CSSClassName").Replace(" ", "").Trim().ToLower() : ""; 247 248 //Theme colors 249 string foregroundColor = Model.Item.GetString("ForegroundColor", "currentColor"); 250 string backgroundColor = Model.Item.GetString("BackgroundColor", "transparent"); 251 string accentColor = Model.Item.GetString("accentColor", "currentColor"); 252 string borderColor = Model.Item.GetString("BorderColor", "transparent"); 253 254 //Primary button 255 string buttonPrimaryForegroundColor = Model.Item.GetString("ButtonPrimaryForegroundColor", "currentColor"); 256 string buttonPrimaryBackgroundColor = Model.Item.GetString("ButtonPrimaryBackgroundColor", "transparent"); 257 string buttonPrimaryBorderColor = Model.Item.GetString("ButtonPrimaryBorderColor", "transparent"); 258 259 string buttonPrimaryHoverBackgroundColorAuto = "transparent"; 260 if (buttonPrimaryBackgroundColor != "transparent") 261 { 262 buttonPrimaryHoverBackgroundColorAuto = "hsl(" + GetColorVariation(buttonPrimaryBackgroundColor) + ")"; 263 } 264 string buttonPrimaryHoverForegroundColor = Model.Item.GetString("ButtonPrimaryHoverForegroundColor", buttonPrimaryForegroundColor); 265 string buttonPrimaryHoverBackgroundColor = Model.Item.GetString("ButtonPrimaryHoverBackgroundColor", buttonPrimaryHoverBackgroundColorAuto); 266 string buttonPrimaryHoverBorderColor = Model.Item.GetString("ButtonPrimaryHoverBorderColor", buttonPrimaryBorderColor); 267 268 //Secondary buton 269 string buttonSecondaryForegroundColor = Model.Item.GetString("ButtonSecondaryForegroundColor", "currentColor"); 270 string buttonSecondaryBackgroundColor = Model.Item.GetString("ButtonSecondaryBackgroundColor", "transparent"); 271 string buttonSecondaryBorderColor = Model.Item.GetString("ButtonSecondaryBorderColor", "transparent"); 272 273 string buttonSecondaryHoverBackgroundColorAuto = "transparent"; 274 if (buttonSecondaryBackgroundColor != "transparent") 275 { 276 buttonSecondaryHoverBackgroundColorAuto = "hsl(" + GetColorVariation(buttonSecondaryBackgroundColor) + ")"; 277 } 278 string buttonSecondaryHoverForegroundColor = Model.Item.GetString("ButtonSecondaryHoverForegroundColor", buttonSecondaryForegroundColor); 279 string buttonSecondaryHoverBackgroundColor = Model.Item.GetString("ButtonSecondaryHoverBackgroundColor", buttonSecondaryHoverBackgroundColorAuto); 280 string buttonSecondaryHoverBorderColor = Model.Item.GetString("ButtonSecondaryHoverBorderColor", buttonSecondaryBorderColor); 281 282 //Link buton 283 string buttonLinkColor = Model.Item.GetString("ButtonLinkColor", "currentColor"); 284 string buttonLinkHoverColorAuto = "currentColor"; 285 if (buttonLinkColor != "currentColor") 286 { 287 buttonLinkHoverColorAuto = "hsl(" + GetColorVariation(buttonLinkColor) + ")"; 288 } 289 string buttonLinkHoverColor = Model.Item.GetString("ButtonLinkHoverColor", buttonLinkHoverColorAuto); 290 291 string accessibilityOutlineColor = Model.Item.GetString("OutlineColor") != null ? Model.Item.GetString("OutlineColor") : ""; 292 293 if (!string.IsNullOrEmpty(themeClassName)) 294 { 295 var sb = new System.Text.StringBuilder(); 296 297 sb.AppendLine("." + themeClassName + "{"); 298 if (!string.IsNullOrEmpty(foregroundColor)) 299 { 300 sb.AppendLine($"--swift-foreground-color: {foregroundColor};"); 301 sb.AppendLine($"--swift-foreground-color-rgb: {HexToRGB(foregroundColor)};"); 302 } 303 if (!string.IsNullOrEmpty(backgroundColor)) 304 { 305 sb.AppendLine($"--swift-background-color: {backgroundColor};"); 306 sb.AppendLine($"--swift-background-color-rgb: {HexToRGB(backgroundColor)};"); 307 } 308 if (!string.IsNullOrEmpty(accentColor)) 309 { 310 sb.AppendLine($"--swift-accent-color: {accentColor};"); 311 sb.AppendLine($"--swift-accent-color-rgb: {HexToRGB(accentColor)};"); 312 } 313 if (!string.IsNullOrEmpty(borderColor)) 314 { 315 sb.AppendLine($"--swift-border-color: {borderColor};"); 316 sb.AppendLine($"--swift-border-color-rgb: {HexToRGB(borderColor)};"); 317 } 318 319 if (!string.IsNullOrEmpty(buttonPrimaryBackgroundColor)) 320 { 321 sb.AppendLine($"--swift-button-primary-background-color: {buttonPrimaryBackgroundColor};"); 322 sb.AppendLine($"--swift-button-primary-background-color-rgb: {HexToRGB(buttonPrimaryBackgroundColor)};"); 323 } 324 if (!string.IsNullOrEmpty(buttonPrimaryForegroundColor)) 325 { 326 sb.AppendLine($"--swift-button-primary-foreground-color: {buttonPrimaryForegroundColor};"); 327 sb.AppendLine($"--swift-button-primary-foreground-color-rgb: {HexToRGB(buttonPrimaryForegroundColor)};"); 328 } 329 if (!string.IsNullOrEmpty(buttonPrimaryBorderColor)) 330 { 331 sb.AppendLine($"--swift-button-primary-border-color: {buttonPrimaryBorderColor};"); 332 sb.AppendLine($"--swift-button-primary-border-color-rgb: {HexToRGB(buttonPrimaryBorderColor)};"); 333 } 334 335 if (!string.IsNullOrEmpty(buttonPrimaryHoverBackgroundColor)) 336 { 337 sb.AppendLine($"--swift-button-primary-hover-background-color: {buttonPrimaryHoverBackgroundColor};"); 338 sb.AppendLine($"--swift-button-primary-hover-background-color-rgb: {HexToRGB(buttonPrimaryHoverBackgroundColor)};"); 339 } 340 if (!string.IsNullOrEmpty(buttonPrimaryHoverForegroundColor)) 341 { 342 sb.AppendLine($"--swift-button-primary-hover-foreground-color: {buttonPrimaryHoverForegroundColor};"); 343 sb.AppendLine($"--swift-button-primary-hover-foreground-color-rgb: {HexToRGB(buttonPrimaryHoverForegroundColor)};"); 344 } 345 if (!string.IsNullOrEmpty(buttonPrimaryHoverBorderColor)) 346 { 347 sb.AppendLine($"--swift-button-primary-hover-border-color: {buttonPrimaryHoverBorderColor};"); 348 sb.AppendLine($"--swift-button-primary-hover-border-color-rgb: {HexToRGB(buttonPrimaryHoverBorderColor)};"); 349 } 350 351 if (!string.IsNullOrEmpty(buttonSecondaryBackgroundColor)) 352 { 353 sb.AppendLine($"--swift-button-secondary-background-color: {buttonSecondaryBackgroundColor};"); 354 sb.AppendLine($"--swift-button-secondary-background-color-rgb: {HexToRGB(buttonSecondaryBackgroundColor)};"); 355 } 356 if (!string.IsNullOrEmpty(buttonSecondaryForegroundColor)) 357 { 358 sb.AppendLine($"--swift-button-secondary-foreground-color: {buttonSecondaryForegroundColor};"); 359 sb.AppendLine($"--swift-button-secondary-foreground-color-rgb: {HexToRGB(buttonSecondaryForegroundColor)};"); 360 } 361 if (!string.IsNullOrEmpty(buttonSecondaryBorderColor)) 362 { 363 sb.AppendLine($"--swift-button-secondary-border-color: {buttonSecondaryBorderColor};"); 364 sb.AppendLine($"--swift-button-secondary-border-color-rgb: {HexToRGB(buttonSecondaryBorderColor)};"); 365 } 366 if (!string.IsNullOrEmpty(buttonSecondaryHoverBackgroundColor)) 367 { 368 sb.AppendLine($"--swift-button-secondary-hover-background-color: {buttonSecondaryHoverBackgroundColor};"); 369 sb.AppendLine($"--swift-button-secondary-hover-background-color-rgb: {HexToRGB(buttonSecondaryHoverBackgroundColor)};"); 370 } 371 if (!string.IsNullOrEmpty(buttonSecondaryHoverForegroundColor)) 372 { 373 sb.AppendLine($"--swift-button-secondary-hover-foreground-color: {buttonSecondaryHoverForegroundColor};"); 374 sb.AppendLine($"--swift-button-secondary-hover-foreground-color-rgb: {HexToRGB(buttonSecondaryHoverForegroundColor)};"); 375 } 376 if (!string.IsNullOrEmpty(buttonSecondaryHoverBorderColor)) 377 { 378 sb.AppendLine($"--swift-button-secondary-hover-border-color: {buttonSecondaryHoverBorderColor};"); 379 sb.AppendLine($"--swift-button-secondary-hover-border-color-rgb: {HexToRGB(buttonSecondaryHoverBorderColor)};"); 380 } 381 if (!string.IsNullOrEmpty(buttonLinkColor)) 382 { 383 sb.AppendLine($"--swift-button-link-color: {buttonLinkColor};"); 384 sb.AppendLine($"--swift-button-link-color-rgb: {HexToRGB(buttonLinkColor)};"); 385 } 386 if (!string.IsNullOrEmpty(buttonLinkHoverColor)) 387 { 388 sb.AppendLine($"--swift-button-link-hover-color: {buttonLinkHoverColor};"); 389 sb.AppendLine($"--swift-button-link-hover-color-rgb: {HexToRGB(buttonLinkHoverColor)};"); 390 } 391 if (!string.IsNullOrEmpty(accessibilityOutlineColor)) 392 { 393 sb.AppendLine($"--swift-a11y-outline-color: {accessibilityOutlineColor};"); 394 sb.AppendLine($"--swift-a11y-outline-color-rgb: {HexToRGB(accessibilityOutlineColor)};"); 395 } 396 sb.AppendLine("}"); 397 398 Dynamicweb.Core.Helpers.TextFileHelper.WriteTextFile(sb.ToString() + Environment.NewLine, Dynamicweb.Context.Current.Server.MapPath($"/Files/Templates/Designs/Swift/_parsed/Swift_css/Swift_theme_styles_{Dynamicweb.Content.Services.Pages.GetPage(Model.PageID).AreaId}.tmp"), true); 399 } 400 } 401 402 @if (themeClassName != "") 403 { 404 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 405 406 <div class="g-col-12 g-col-lg-4"> 407 <div class="swift-checkered p-3"> 408 <div class="theme-option theme @themeClassName p-lg-3"> 409 <div class="mb-2 pb-1 border-bottom d-flex align-items-center"> 410 <span class="icon-3 me-2"> 411 @ReadFile(iconPath + "sun.svg") 412 </span> 413 <h4 class="m-0 p-0">@themeName</h4> 414 </div> 415 <div class="d-flex flex-column gap-1 mb-2"> 416 <p class="mb-0">This is the body text of the theme.</p> 417 <p class="mb-0" style="color: var(--swift-accent-color)">This is the accent color of the theme.</p> 418 </div> 419 <div> 420 <button class="btn btn-primary me-1">Primary</button> 421 <button class="btn btn-secondary">Secondary</button> 422 <button class="btn btn-link">Link</button> 423 </div> 424 </div> 425 </div> 426 </div> 427 <div class="g-col-12 g-col-lg-8 mb-4 mb-lg-0"> 428 <div class="grid fs-7"> 429 <div class="g-col-12 g-col-lg-3"> 430 <table class="table table-borderless table-sm w-100"> 431 <thead> 432 <tr> 433 <th colspan="2" class="fw-bold">@Translate("Base")</th> 434 </tr> 435 </thead> 436 <tr> 437 <td>@Translate("Foreground")</td> 438 <td>@GetColorSquare(foregroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@foregroundColor');">@foregroundColor</a></td> 439 </tr> 440 <tr> 441 <td>@Translate("Background")</td> 442 <td>@GetColorSquare(backgroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@backgroundColor');">@backgroundColor</a></td> 443 </tr> 444 <tr> 445 <td>@Translate("Accent")</td> 446 <td>@GetColorSquare(accentColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@accentColor');">@accentColor</a></td> 447 </tr> 448 <tr> 449 <td>@Translate("Border")</td> 450 <td>@GetColorSquare(borderColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@borderColor');">@borderColor</a></td> 451 </tr> 452 </table> 453 </div> 454 <div class="g-col-12 g-col-lg-3"> 455 <table class="table table-borderless table-sm w-100"> 456 <thead> 457 <tr> 458 <th colspan="2" class="fw-bold">@Translate("Primary button")</th> 459 </tr> 460 </thead> 461 <tr> 462 <td>@Translate("Foreground")</td> 463 <td>@GetColorSquare(buttonPrimaryForegroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonPrimaryForegroundColor');">@buttonPrimaryForegroundColor</a></td> 464 </tr> 465 <tr> 466 <td>@Translate("Background")</td> 467 <td>@GetColorSquare(buttonPrimaryBackgroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonPrimaryBackgroundColor');">@buttonPrimaryBackgroundColor</a></td> 468 </tr> 469 @if (!string.IsNullOrEmpty(buttonPrimaryBorderColor)) 470 { 471 <tr> 472 <td>@Translate("Border")</td> 473 <td>@GetColorSquare(buttonPrimaryBorderColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonPrimaryBorderColor');">@buttonPrimaryBorderColor</a></td> 474 </tr> 475 } 476 <thead> 477 <tr> 478 <th colspan="2" class="fw-bold">@Translate("Primary button hover")</th> 479 </tr> 480 </thead> 481 @if (!string.IsNullOrEmpty(buttonPrimaryHoverForegroundColor)) 482 { 483 <tr> 484 <td>@Translate("Foreground")</td> 485 <td>@GetColorSquare(buttonPrimaryHoverForegroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonPrimaryHoverForegroundColor');">@buttonPrimaryHoverForegroundColor</a></td> 486 </tr> 487 } 488 @if (!string.IsNullOrEmpty(buttonPrimaryHoverBackgroundColor)) 489 { 490 <tr> 491 <td>@Translate("Background")</td> 492 <td>@GetColorSquare(buttonPrimaryHoverBackgroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonPrimaryHoverBackgroundColor');">@buttonPrimaryHoverBackgroundColor</a></td> 493 </tr> 494 } 495 @if (!string.IsNullOrEmpty(buttonPrimaryHoverBorderColor)) 496 { 497 <tr> 498 <td>@Translate("Border")</td> 499 <td>@GetColorSquare(buttonPrimaryHoverBorderColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonPrimaryHoverBorderColor');">@buttonPrimaryHoverBorderColor</a></td> 500 </tr> 501 } 502 </table> 503 </div> 504 <div class="g-col-12 g-col-lg-3"> 505 <table class="table table-borderless table-sm w-100"> 506 <thead> 507 <tr> 508 <th colspan="2" class="fw-bold">@Translate("Secondary button")</th> 509 </tr> 510 </thead> 511 <tr> 512 <td>@Translate("Foreground")</td> 513 <td>@GetColorSquare(buttonSecondaryForegroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonSecondaryForegroundColor');">@buttonSecondaryForegroundColor</a></td> 514 </tr> 515 <tr> 516 <td>@Translate("Background")</td> 517 <td>@GetColorSquare(buttonSecondaryBackgroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonSecondaryBackgroundColor');">@buttonSecondaryBackgroundColor</a></td> 518 </tr> 519 @if (!string.IsNullOrEmpty(buttonSecondaryBorderColor)) 520 { 521 <tr> 522 <td>@Translate("Border")</td> 523 <td>@GetColorSquare(buttonSecondaryBorderColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonSecondaryBorderColor');">@buttonSecondaryBorderColor</a></td> 524 </tr> 525 } 526 <thead> 527 <tr> 528 <th colspan="2" class="fw-bold">@Translate("Secondary button hover")</th> 529 </tr> 530 </thead> 531 @if (!string.IsNullOrEmpty(buttonSecondaryHoverForegroundColor)) 532 { 533 <tr> 534 <td>@Translate("Foreground")</td> 535 <td>@GetColorSquare(buttonSecondaryHoverForegroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonSecondaryHoverForegroundColor');">@buttonSecondaryHoverForegroundColor</a></td> 536 </tr> 537 } 538 @if (!string.IsNullOrEmpty(buttonSecondaryHoverBackgroundColor)) 539 { 540 <tr> 541 <td>@Translate("Background")</td> 542 <td>@GetColorSquare(buttonSecondaryHoverBackgroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonSecondaryHoverBackgroundColor');">@buttonSecondaryHoverBackgroundColor</a></td> 543 </tr> 544 } 545 @if (!string.IsNullOrEmpty(buttonSecondaryHoverBorderColor)) 546 { 547 <tr> 548 <td>@Translate("Border")</td> 549 <td>@GetColorSquare(buttonSecondaryHoverBorderColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonSecondaryHoverBorderColor');">@buttonSecondaryHoverBorderColor</a></td> 550 </tr> 551 } 552 @if (!string.IsNullOrEmpty(accessibilityOutlineColor)) 553 { 554 <tr> 555 <td>@Translate("Focus outline")</td> 556 <td>@GetColorSquare(accessibilityOutlineColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@accessibilityOutlineColor');">@accessibilityOutlineColor</a></td> 557 </tr> 558 } 559 </table> 560 </div> 561 <div class="g-col-12 g-col-lg-3"> 562 <table class="table table-borderless table-sm w-100"> 563 <thead> 564 <tr> 565 <th colspan="2" class="fw-bold">@Translate("Link button")</th> 566 </tr> 567 </thead> 568 <tr> 569 <td>@Translate("Link color")</td> 570 <td>@GetColorSquare(buttonLinkColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonLinkColor');">@buttonLinkColor</a></td> 571 </tr> 572 <thead> 573 <tr> 574 <th colspan="2" class="fw-bold">@Translate("Link button hover")</th> 575 </tr> 576 </thead> 577 @if (!string.IsNullOrEmpty(buttonLinkHoverColor)) 578 { 579 <tr> 580 <td>@Translate("Link hover color")</td> 581 <td>@GetColorSquare(buttonLinkHoverColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonLinkHoverColor');">@buttonLinkHoverColor</a></td> 582 </tr> 583 } 584 585 @if (!string.IsNullOrEmpty(accessibilityOutlineColor)) 586 { 587 <tr> 588 <td>@Translate("Focus outline")</td> 589 <td>@GetColorSquare(accessibilityOutlineColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@accessibilityOutlineColor');">@accessibilityOutlineColor</a></td> 590 </tr> 591 } 592 </table> 593 </div> 594 <div class="g-col-12 g-col-lg-3"> 595 <table class="table table-borderless table-sm w-100"> 596 <tr> 597 <td class="fw-bold">@Translate("Implementation")</td> 598 </tr> 599 <tr> 600 <td> 601 @{ 602 string implementation = $"<div class=\"theme {themeClassName}\"></div>"; 603 } 604 <div class="text-muted"> 605 <code class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@themeClassName');"> 606 @Dynamicweb.Core.Encoders.HtmlEncoder.HtmlEncode(implementation) 607 </code> 608 </div> 609 </td> 610 </tr> 611 </table> 612 </div> 613 @{ 614 bool hasUsageData = Converter.ToBoolean(Dynamicweb.Context.Current.Request["auditUsages"]); 615 if (hasUsageData) 616 { 617 var themeUsage = GetThemeUsageById(themeClassName); 618 int allUsages = themeUsage?.AllUsages ?? 0; 619 string buttonAttributes = allUsages > 0 ? $"data-bs-toggle=\"modal\" data-bs-target=\"#themeUsage_{Model.ID}\"" : string.Empty; 620 string badgeStyle = allUsages > 0 ? "bg-success" : "bg-danger"; 621 622 <div class="g-col-12 g-col-lg-9 position-relative"> 623 <button type="button" class="btn btn-primary btn-sm position-absolute end-0" @buttonAttributes> 624 Usages 625 <span class="position-absolute top-0 start-100 translate-middle badge rounded-pill text-white @badgeStyle">@allUsages</span> 626 </button> 627 628 @if (allUsages > 0) 629 { 630 int websiteUsages = themeUsage?.WebsiteUsages.Count ?? 0; 631 int pageUsages = themeUsage?.PageUsages.Count ?? 0; 632 int pagePropertyUsages = themeUsage?.PagePropertyUsages.Count ?? 0; 633 int gridRowUsages = themeUsage?.GridRowUsages.Count ?? 0; 634 int paragraphUsages = themeUsage?.ParagraphUsages.Count ?? 0; 635 636 <!-- Modal --> 637 <div class="modal fade" id="themeUsage_@(Model.ID)" tabindex="-1" aria-labelledby="usagesModelLabel" aria-hidden="true"> 638 <div class="modal-dialog modal-xl modal-dialog-centered modal-dialog-scrollable"> 639 <div class="modal-content"> 640 <div class="modal-header"> 641 <h5 class="modal-title" id="usagesModelLabel">@allUsages Usages of '@(themeName)' (@(themeClassName))</h5> 642 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 643 </div> 644 <div class="modal-body"> 645 <div class="accordion" id="accordionWrapper"> 646 @if(websiteUsages > 0) 647 { 648 <div class="accordion-item"> 649 <h2 class="accordion-header" id="headingWebsites"> 650 <button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseWebsites" aria-expanded="true" aria-controls="collapseWebsites"> 651 <span class="pe-2">Websites</span><span class="badge rounded-pill bg-secondary text-light">@websiteUsages</span> 652 </button> 653 </h2> 654 <div id="collapseWebsites" class="accordion-collapse collapse show" aria-labelledby="headingWebsites" data-bs-parent="#accordionWrapper"> 655 <div class="accordion-body"> 656 <ul> 657 @foreach (var usage in themeUsage.WebsiteUsages) 658 { 659 var website = Dynamicweb.Content.Services.Areas.GetArea(usage); 660 <li><span class="fw-bold">@website.Name</span>@($" (ID: {usage})")</li> 661 } 662 </ul> 663 </div> 664 </div> 665 </div> 666 } 667 @if (pageUsages > 0) 668 { 669 var pages = Dynamicweb.Content.Services.Pages.GetPages(themeUsage.PageUsages.ToArray()); 670 671 <div class="accordion-item"> 672 <h2 class="accordion-header" id="headingPages"> 673 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapsePages" aria-expanded="false" aria-controls="collapsePages"> 674 <span class="pe-2">Pages</span><span class="badge rounded-pill bg-secondary text-light">@pageUsages</span> 675 </button> 676 </h2> 677 <div id="collapsePages" class="accordion-collapse collapse" aria-labelledby="headingPages" data-bs-parent="#accordionWrapper"> 678 <div class="accordion-body"> 679 <ul> 680 @foreach (var usage in themeUsage.PageUsages) 681 { 682 <li><span class="fw-bold">@pages.FirstOrDefault(p => p.ID == usage).GetDisplayName()</span>@($" (ID: {usage})")</li> 683 } 684 </ul> 685 </div> 686 </div> 687 </div> 688 } 689 @if (pagePropertyUsages > 0) 690 { 691 var pages = Dynamicweb.Content.Services.Pages.GetPages(themeUsage.PageUsages.ToArray()); 692 693 <div class="accordion-item"> 694 <h2 class="accordion-header" id="headingPageProperties"> 695 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapsePageProperties" aria-expanded="false" aria-controls="collapsePageProperties"> 696 <span class="pe-2">Page Properties</span><span class="badge rounded-pill bg-secondary text-light">@pagePropertyUsages</span> 697 </button> 698 </h2> 699 <div id="collapsePageProperties" class="accordion-collapse collapse" aria-labelledby="headingPageProperties" data-bs-parent="#accordionWrapper"> 700 <div class="accordion-body"> 701 <ul> 702 @foreach (var usage in themeUsage.PagePropertyUsages) 703 { 704 <li><span class="fw-bold">@pages.FirstOrDefault(p => p.ID == usage).GetDisplayName()</span>@($" (ID: {usage})")</li> 705 } 706 </ul> 707 </div> 708 </div> 709 </div> 710 } 711 @if (gridRowUsages > 0) 712 { 713 var rows = Dynamicweb.Content.Services.Grids.GetGridRows().Where(r => themeUsage.GridRowUsages.Contains(r.ID)).ToList(); 714 715 <div class="accordion-item"> 716 <h2 class="accordion-header" id="headingRows"> 717 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseRows" aria-expanded="false" aria-controls="collapseRows"> 718 <span class="pe-2">Rows</span><span class="badge rounded-pill bg-secondary text-light">@gridRowUsages</span> 719 </button> 720 </h2> 721 <div id="collapseRows" class="accordion-collapse collapse" aria-labelledby="headingRows" data-bs-parent="#accordionWrapper"> 722 <div class="accordion-body"> 723 <ul> 724 @foreach (var usage in themeUsage.GridRowUsages) 725 { 726 var row = rows.FirstOrDefault(r => r.ID == usage); 727 var page = Dynamicweb.Content.Services.Pages.GetPage(row.PageId); 728 <li>Page: <span class="fw-bold">@(page.GetDisplayName())</span>@($"( ID: {page.ID}) - Row: ")<span class="fw-bold">@(row.DefinitionId)</span>@($" (ID: {usage})")</li> 729 } 730 </ul> 731 </div> 732 </div> 733 </div> 734 } 735 @if (paragraphUsages > 0) 736 { 737 var paragraphs = Dynamicweb.Content.Services.Paragraphs.GetParagraphs().Where(p => themeUsage.ParagraphUsages.Contains(p.ID)).ToList(); 738 739 <div class="accordion-item"> 740 <h2 class="accordion-header" id="headingParagraphs"> 741 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseParagraphs" aria-expanded="false" aria-controls="collapseParagraphs"> 742 <span class="pe-2">Paragraphs</span><span class="badge rounded-pill bg-secondary text-light">@paragraphUsages</span> 743 </button> 744 </h2> 745 <div id="collapseParagraphs" class="accordion-collapse collapse" aria-labelledby="headingParagraphs" data-bs-parent="#accordionWrapper"> 746 <div class="accordion-body"> 747 <ul> 748 @foreach (var usage in themeUsage.ParagraphUsages) 749 { 750 var paragraph = paragraphs.FirstOrDefault(p => p.ID == usage); 751 var page = Dynamicweb.Content.Services.Pages.GetPage(paragraph.PageID); 752 <li>Page: <span class="fw-bold">@(page.GetDisplayName())</span>@($"( ID: {page.ID}) - Paragraph: ")<span class="fw-bold">@paragraph.Header</span>@($" (ID: {usage})")</li> 753 } 754 </ul> 755 </div> 756 </div> 757 </div> 758 } 759 </div> 760 </div> 761 </div> 762 </div> 763 </div> 764 } 765 </div> 766 } 767 } 768 </div> 769 </div> 770 } 771 <div class="g-col-12"> 772 <hr /> 773 </div> 774
Error executing template "Designs/Swift/Paragraph/Swift_Theme.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_f772bb33646d4d3c8ae681f5d1cf870b.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.ParagraphViewModel> 2 @using System.Globalization 3 @using System.Drawing 4 @using Dynamicweb.Core 5 6 @functions{ 7 // Get Hex Color from color picker and translate to RGB color 8 public static string HexToRGB(string hexString) 9 { 10 if (hexString.Equals("none") || string.IsNullOrEmpty(hexString)) 11 return hexString; 12 13 //replace # occurences 14 if (hexString.IndexOf('#') != -1) 15 hexString = hexString.Replace("#", ""); 16 try 17 { 18 int r, g, b = 0; 19 r = int.Parse(hexString.Substring(0, 2), NumberStyles.AllowHexSpecifier); 20 g = int.Parse(hexString.Substring(2, 2), NumberStyles.AllowHexSpecifier); 21 b = int.Parse(hexString.Substring(4, 2), NumberStyles.AllowHexSpecifier); 22 23 string rgb = r + "," + g + "," + b; 24 return rgb; 25 } 26 catch (System.Exception) 27 { 28 return hexString; 29 } 30 } 31 32 public static string GetColorSquare(string hex) 33 { 34 return $"<div class=\"swift-checkered d-inline-block me-1\" style=\"height:1.25rem;width:1.25rem;\"><div style=\"background-color: {hex};border:.0625rem solid grey\" class=\"w-100 h-100\"></div></div>"; 35 } 36 37 public class HSL 38 { 39 public double h { get; set; } 40 public double s { get; set; } 41 public double l { get; set; } 42 } 43 44 //Convert HEX to HSL 45 public static HSL HexToHsl(string hexString) 46 { 47 System.Drawing.Color color = System.Drawing.ColorTranslator.FromHtml(hexString); 48 49 float _R = (color.R / 255f); 50 float _G = (color.G / 255f); 51 float _B = (color.B / 255f); 52 53 float _Min = Math.Min(Math.Min(_R, _G), _B); 54 float _Max = Math.Max(Math.Max(_R, _G), _B); 55 float _Delta = _Max - _Min; 56 57 float S = 0; 58 float L = (float)((_Max + _Min) / 2.0f); 59 60 if (_Delta != 0) 61 { 62 if (L < 0.5f) 63 { 64 S = (float)(_Delta / (_Max + _Min)); 65 } 66 else 67 { 68 S = (float)(_Delta / (2.0f - _Max - _Min)); 69 } 70 } 71 72 HSL hsl = new HSL(); 73 hsl.h = Math.Round(color.GetHue(), 2); 74 hsl.s = Math.Round(S, 2) * 100; 75 hsl.l = Math.Round(L, 2) * 100; 76 77 return hsl; 78 } 79 80 //Find contrast color (white, black) 81 public static Color GetContrastColor(string hexString) 82 { 83 System.Drawing.Color bg = System.Drawing.ColorTranslator.FromHtml(hexString); 84 85 int nThreshold = 105; 86 int bgDelta = Convert.ToInt32((bg.R * 0.299) + (bg.G * 0.587) + 87 (bg.B * 0.114)); 88 89 Color foreColor = (255 - bgDelta < nThreshold) ? Color.Black : Color.White; 90 return foreColor; 91 } 92 93 public string GetColorVariation(string hexString) 94 { 95 var contrast = GetContrastColor(hexString); 96 HSL currentHslColor = HexToHsl(hexString); 97 HSL variantHslColor = currentHslColor; 98 99 if (contrast == Color.Black) 100 { 101 variantHslColor.l = 85; //The background is dark - Color should be light 102 } 103 else 104 { 105 variantHslColor.l = 40; //The background is light - Color should be less light 106 } 107 108 //Color is very light 109 if (currentHslColor.l > 85) 110 { 111 variantHslColor.l = 95; 112 } 113 114 string colorString = variantHslColor.h + ", " + variantHslColor.s + "%, " + variantHslColor.l + "%"; 115 return colorString; 116 } 117 118 private class ThemeDatabaseUsage 119 { 120 public string TableName { get; set; } 121 public string ColumnName { get; set; } 122 } 123 124 private class ThemeUsage 125 { 126 public string Id { get; set; } 127 public List<int> WebsiteUsages { get; set; } = new List<int>(); 128 public List<int> PageUsages { get; set; } = new List<int>(); 129 public List<int> PagePropertyUsages { get; set; } = new List<int>(); 130 public List<int> GridRowUsages { get; set; } = new List<int>(); 131 public List<int> ParagraphUsages { get; set; } = new List<int>(); 132 public int AllUsages { get; set; } = 0; 133 } 134 135 private List<ThemeDatabaseUsage> GetThemeColumnsFromSchema() 136 { 137 string cacheKey = "ThemesFromDatabase"; 138 if (Dynamicweb.Context.Current.Items.Contains(cacheKey)) 139 { 140 return (List<ThemeDatabaseUsage>)Dynamicweb.Context.Current.Items[cacheKey]; 141 } 142 var schema = Dynamicweb.Data.Database.CreateDataReader("SELECT table_name, column_name FROM information_schema.columns WHERE column_name LIKE '%Theme%'"); 143 144 var rawData = new List<ThemeDatabaseUsage>(); 145 while (schema.Read()) { 146 rawData.Add(new ThemeDatabaseUsage {TableName = schema["table_name"].ToString(), ColumnName = schema["column_name"].ToString() }); 147 } 148 Dynamicweb.Context.Current.Items[cacheKey] = rawData; 149 return rawData; 150 } 151 152 private List<ThemeUsage> GetAllThemeUsages() 153 { 154 string cacheKey = "ThemeUsages"; 155 if (Dynamicweb.Context.Current.Items.Contains(cacheKey)) 156 { 157 return (List<ThemeUsage>)Dynamicweb.Context.Current.Items[cacheKey]; 158 } 159 160 var rawData = GetThemeColumnsFromSchema(); 161 var themeUsages = new List<ThemeUsage>(); 162 163 var sqlQuery = ""; 164 foreach (var row in rawData) 165 { 166 if (!string.IsNullOrEmpty(sqlQuery)) { sqlQuery += " UNION ALL "; } 167 sqlQuery += $"SELECT '{row.TableName}' AS TableName, '{row.ColumnName}' AS ColumnName, {row.ColumnName} AS ColumnValue, Id, AreaId, Page.PageId, PageProperties.PageId as 'PagePropertiesId', GridRowId, ParagraphId" + 168 $" FROM [{row.TableName}]" + 169 $" LEFT JOIN Area ON AreaItemType = '{row.TableName.Replace("ItemType_","")}' AND AreaItemId = Id" + 170 $" LEFT JOIN Page ON PageItemType = '{row.TableName.Replace("ItemType_","")}' AND PageItemId = Id" + 171 $" LEFT JOIN Page as PageProperties ON AreaItemTypePageProperty = '{row.TableName.Replace("ItemType_","")}' AND PageProperties.PagePropertyItemId = Id" + 172 $" LEFT JOIN GridRow ON GridRowItemType = '{row.TableName.Replace("ItemType_","")}' AND GridRowItemId = Id" + 173 $" LEFT JOIN Paragraph ON ParagraphItemType = '{row.TableName.Replace("ItemType_","")}' AND ParagraphItemId = Id" + 174 $" WHERE {row.ColumnName} != '' AND {row.ColumnName} IS NOT NULL" + 175 $" AND (AreaId IS NULL OR AreaId = {Pageview.AreaID})" + 176 $" AND (Page.PageId IS NULL OR Page.PageAreaId = {Pageview.AreaID})" + 177 $" AND (PageProperties.PageId IS NULL OR PageProperties.PageAreaId = {Pageview.AreaID})" + 178 $" AND (GridRowId IS NULL OR GridRowPageId IN (SELECT value From string_split(@pageIds, ',')))" + 179 $" AND (ParagraphId IS NULL OR ParagraphPageId IN (SELECT value From string_split(@pageIds, ',')))"; 180 181 var maxMsSqlServerCharLimit = 60000; 182 if (sqlQuery.Length > maxMsSqlServerCharLimit) 183 { 184 UpdateThemeUsages(sqlQuery, themeUsages); 185 sqlQuery = ""; 186 } 187 } 188 189 if (!string.IsNullOrEmpty(sqlQuery)) 190 { 191 UpdateThemeUsages(sqlQuery, themeUsages); 192 } 193 UpdateAllUsages(themeUsages); 194 195 Dynamicweb.Context.Current.Items[cacheKey] = themeUsages; 196 return themeUsages; 197 } 198 199 private void UpdateThemeUsages(string sqlQuery, List<ThemeUsage> themeUsages) 200 { 201 sqlQuery = $"DECLARE @pageIds NVARCHAR(max) = (SELECT STRING_AGG(PageId,',') FROM Page WHERE PageAreaId = {Pageview.AreaID}) " + sqlQuery; 202 var rawData = Dynamicweb.Data.Database.CreateDataReader(sqlQuery); 203 while (rawData.Read()) 204 { 205 var themeId = rawData["ColumnValue"].ToString(); 206 var themeUsageExists = themeUsages.FirstOrDefault(t => t.Id == themeId) != null; 207 if (!themeUsageExists) 208 { 209 themeUsages.Add(new ThemeUsage { Id = themeId }); 210 } 211 212 var themeUsage = themeUsages.FirstOrDefault(t => t.Id == themeId); 213 if (themeUsage == null) continue; 214 215 if (Converter.ToInt32(rawData["AreaId"].ToString()) > 0) { themeUsage.WebsiteUsages.Add(Converter.ToInt32(rawData["AreaId"].ToString())); } 216 if (Converter.ToInt32(rawData["PageId"].ToString()) > 0) { themeUsage.PageUsages.Add(Converter.ToInt32(rawData["PageId"].ToString())); } 217 if (Converter.ToInt32(rawData["PagePropertiesId"].ToString()) > 0) { themeUsage.PagePropertyUsages.Add(Converter.ToInt32(rawData["PagePropertiesId"].ToString())); } 218 if (Converter.ToInt32(rawData["GridRowId"].ToString()) > 0) { themeUsage.GridRowUsages.Add(Converter.ToInt32(rawData["GridRowId"].ToString())); } 219 if (Converter.ToInt32(rawData["ParagraphId"].ToString()) > 0) { themeUsage.ParagraphUsages.Add(Converter.ToInt32(rawData["ParagraphId"].ToString())); } 220 } 221 } 222 223 private void UpdateAllUsages(List<ThemeUsage> themeUsages) 224 { 225 foreach (var themeUsage in themeUsages) 226 { 227 int websiteUsages = themeUsage?.WebsiteUsages.Count ?? 0; 228 int pageUsages = themeUsage?.PageUsages.Count ?? 0; 229 int pagePropertyUsages = themeUsage?.PagePropertyUsages.Count ?? 0; 230 int gridRowUsages = themeUsage?.GridRowUsages.Count ?? 0; 231 int paragraphUsages = themeUsage?.ParagraphUsages.Count ?? 0; 232 themeUsage.AllUsages = websiteUsages + pageUsages + pagePropertyUsages + gridRowUsages + paragraphUsages; 233 } 234 } 235 236 private ThemeUsage GetThemeUsageById(string themeId) 237 { 238 var themeUsages = GetAllThemeUsages(); 239 return themeUsages.FirstOrDefault(t => t.Id == themeId); 240 } 241 242 } 243 244 @{ 245 string themeName = !string.IsNullOrWhiteSpace(Model.Item.GetString("Name")) ? Model.Item.GetString("Name") : "Default"; 246 string themeClassName = !string.IsNullOrWhiteSpace(Model.Item.GetString("CSSClassName")) ? Model.Item.GetString("CSSClassName").Replace(" ", "").Trim().ToLower() : ""; 247 248 //Theme colors 249 string foregroundColor = Model.Item.GetString("ForegroundColor", "currentColor"); 250 string backgroundColor = Model.Item.GetString("BackgroundColor", "transparent"); 251 string accentColor = Model.Item.GetString("accentColor", "currentColor"); 252 string borderColor = Model.Item.GetString("BorderColor", "transparent"); 253 254 //Primary button 255 string buttonPrimaryForegroundColor = Model.Item.GetString("ButtonPrimaryForegroundColor", "currentColor"); 256 string buttonPrimaryBackgroundColor = Model.Item.GetString("ButtonPrimaryBackgroundColor", "transparent"); 257 string buttonPrimaryBorderColor = Model.Item.GetString("ButtonPrimaryBorderColor", "transparent"); 258 259 string buttonPrimaryHoverBackgroundColorAuto = "transparent"; 260 if (buttonPrimaryBackgroundColor != "transparent") 261 { 262 buttonPrimaryHoverBackgroundColorAuto = "hsl(" + GetColorVariation(buttonPrimaryBackgroundColor) + ")"; 263 } 264 string buttonPrimaryHoverForegroundColor = Model.Item.GetString("ButtonPrimaryHoverForegroundColor", buttonPrimaryForegroundColor); 265 string buttonPrimaryHoverBackgroundColor = Model.Item.GetString("ButtonPrimaryHoverBackgroundColor", buttonPrimaryHoverBackgroundColorAuto); 266 string buttonPrimaryHoverBorderColor = Model.Item.GetString("ButtonPrimaryHoverBorderColor", buttonPrimaryBorderColor); 267 268 //Secondary buton 269 string buttonSecondaryForegroundColor = Model.Item.GetString("ButtonSecondaryForegroundColor", "currentColor"); 270 string buttonSecondaryBackgroundColor = Model.Item.GetString("ButtonSecondaryBackgroundColor", "transparent"); 271 string buttonSecondaryBorderColor = Model.Item.GetString("ButtonSecondaryBorderColor", "transparent"); 272 273 string buttonSecondaryHoverBackgroundColorAuto = "transparent"; 274 if (buttonSecondaryBackgroundColor != "transparent") 275 { 276 buttonSecondaryHoverBackgroundColorAuto = "hsl(" + GetColorVariation(buttonSecondaryBackgroundColor) + ")"; 277 } 278 string buttonSecondaryHoverForegroundColor = Model.Item.GetString("ButtonSecondaryHoverForegroundColor", buttonSecondaryForegroundColor); 279 string buttonSecondaryHoverBackgroundColor = Model.Item.GetString("ButtonSecondaryHoverBackgroundColor", buttonSecondaryHoverBackgroundColorAuto); 280 string buttonSecondaryHoverBorderColor = Model.Item.GetString("ButtonSecondaryHoverBorderColor", buttonSecondaryBorderColor); 281 282 //Link buton 283 string buttonLinkColor = Model.Item.GetString("ButtonLinkColor", "currentColor"); 284 string buttonLinkHoverColorAuto = "currentColor"; 285 if (buttonLinkColor != "currentColor") 286 { 287 buttonLinkHoverColorAuto = "hsl(" + GetColorVariation(buttonLinkColor) + ")"; 288 } 289 string buttonLinkHoverColor = Model.Item.GetString("ButtonLinkHoverColor", buttonLinkHoverColorAuto); 290 291 string accessibilityOutlineColor = Model.Item.GetString("OutlineColor") != null ? Model.Item.GetString("OutlineColor") : ""; 292 293 if (!string.IsNullOrEmpty(themeClassName)) 294 { 295 var sb = new System.Text.StringBuilder(); 296 297 sb.AppendLine("." + themeClassName + "{"); 298 if (!string.IsNullOrEmpty(foregroundColor)) 299 { 300 sb.AppendLine($"--swift-foreground-color: {foregroundColor};"); 301 sb.AppendLine($"--swift-foreground-color-rgb: {HexToRGB(foregroundColor)};"); 302 } 303 if (!string.IsNullOrEmpty(backgroundColor)) 304 { 305 sb.AppendLine($"--swift-background-color: {backgroundColor};"); 306 sb.AppendLine($"--swift-background-color-rgb: {HexToRGB(backgroundColor)};"); 307 } 308 if (!string.IsNullOrEmpty(accentColor)) 309 { 310 sb.AppendLine($"--swift-accent-color: {accentColor};"); 311 sb.AppendLine($"--swift-accent-color-rgb: {HexToRGB(accentColor)};"); 312 } 313 if (!string.IsNullOrEmpty(borderColor)) 314 { 315 sb.AppendLine($"--swift-border-color: {borderColor};"); 316 sb.AppendLine($"--swift-border-color-rgb: {HexToRGB(borderColor)};"); 317 } 318 319 if (!string.IsNullOrEmpty(buttonPrimaryBackgroundColor)) 320 { 321 sb.AppendLine($"--swift-button-primary-background-color: {buttonPrimaryBackgroundColor};"); 322 sb.AppendLine($"--swift-button-primary-background-color-rgb: {HexToRGB(buttonPrimaryBackgroundColor)};"); 323 } 324 if (!string.IsNullOrEmpty(buttonPrimaryForegroundColor)) 325 { 326 sb.AppendLine($"--swift-button-primary-foreground-color: {buttonPrimaryForegroundColor};"); 327 sb.AppendLine($"--swift-button-primary-foreground-color-rgb: {HexToRGB(buttonPrimaryForegroundColor)};"); 328 } 329 if (!string.IsNullOrEmpty(buttonPrimaryBorderColor)) 330 { 331 sb.AppendLine($"--swift-button-primary-border-color: {buttonPrimaryBorderColor};"); 332 sb.AppendLine($"--swift-button-primary-border-color-rgb: {HexToRGB(buttonPrimaryBorderColor)};"); 333 } 334 335 if (!string.IsNullOrEmpty(buttonPrimaryHoverBackgroundColor)) 336 { 337 sb.AppendLine($"--swift-button-primary-hover-background-color: {buttonPrimaryHoverBackgroundColor};"); 338 sb.AppendLine($"--swift-button-primary-hover-background-color-rgb: {HexToRGB(buttonPrimaryHoverBackgroundColor)};"); 339 } 340 if (!string.IsNullOrEmpty(buttonPrimaryHoverForegroundColor)) 341 { 342 sb.AppendLine($"--swift-button-primary-hover-foreground-color: {buttonPrimaryHoverForegroundColor};"); 343 sb.AppendLine($"--swift-button-primary-hover-foreground-color-rgb: {HexToRGB(buttonPrimaryHoverForegroundColor)};"); 344 } 345 if (!string.IsNullOrEmpty(buttonPrimaryHoverBorderColor)) 346 { 347 sb.AppendLine($"--swift-button-primary-hover-border-color: {buttonPrimaryHoverBorderColor};"); 348 sb.AppendLine($"--swift-button-primary-hover-border-color-rgb: {HexToRGB(buttonPrimaryHoverBorderColor)};"); 349 } 350 351 if (!string.IsNullOrEmpty(buttonSecondaryBackgroundColor)) 352 { 353 sb.AppendLine($"--swift-button-secondary-background-color: {buttonSecondaryBackgroundColor};"); 354 sb.AppendLine($"--swift-button-secondary-background-color-rgb: {HexToRGB(buttonSecondaryBackgroundColor)};"); 355 } 356 if (!string.IsNullOrEmpty(buttonSecondaryForegroundColor)) 357 { 358 sb.AppendLine($"--swift-button-secondary-foreground-color: {buttonSecondaryForegroundColor};"); 359 sb.AppendLine($"--swift-button-secondary-foreground-color-rgb: {HexToRGB(buttonSecondaryForegroundColor)};"); 360 } 361 if (!string.IsNullOrEmpty(buttonSecondaryBorderColor)) 362 { 363 sb.AppendLine($"--swift-button-secondary-border-color: {buttonSecondaryBorderColor};"); 364 sb.AppendLine($"--swift-button-secondary-border-color-rgb: {HexToRGB(buttonSecondaryBorderColor)};"); 365 } 366 if (!string.IsNullOrEmpty(buttonSecondaryHoverBackgroundColor)) 367 { 368 sb.AppendLine($"--swift-button-secondary-hover-background-color: {buttonSecondaryHoverBackgroundColor};"); 369 sb.AppendLine($"--swift-button-secondary-hover-background-color-rgb: {HexToRGB(buttonSecondaryHoverBackgroundColor)};"); 370 } 371 if (!string.IsNullOrEmpty(buttonSecondaryHoverForegroundColor)) 372 { 373 sb.AppendLine($"--swift-button-secondary-hover-foreground-color: {buttonSecondaryHoverForegroundColor};"); 374 sb.AppendLine($"--swift-button-secondary-hover-foreground-color-rgb: {HexToRGB(buttonSecondaryHoverForegroundColor)};"); 375 } 376 if (!string.IsNullOrEmpty(buttonSecondaryHoverBorderColor)) 377 { 378 sb.AppendLine($"--swift-button-secondary-hover-border-color: {buttonSecondaryHoverBorderColor};"); 379 sb.AppendLine($"--swift-button-secondary-hover-border-color-rgb: {HexToRGB(buttonSecondaryHoverBorderColor)};"); 380 } 381 if (!string.IsNullOrEmpty(buttonLinkColor)) 382 { 383 sb.AppendLine($"--swift-button-link-color: {buttonLinkColor};"); 384 sb.AppendLine($"--swift-button-link-color-rgb: {HexToRGB(buttonLinkColor)};"); 385 } 386 if (!string.IsNullOrEmpty(buttonLinkHoverColor)) 387 { 388 sb.AppendLine($"--swift-button-link-hover-color: {buttonLinkHoverColor};"); 389 sb.AppendLine($"--swift-button-link-hover-color-rgb: {HexToRGB(buttonLinkHoverColor)};"); 390 } 391 if (!string.IsNullOrEmpty(accessibilityOutlineColor)) 392 { 393 sb.AppendLine($"--swift-a11y-outline-color: {accessibilityOutlineColor};"); 394 sb.AppendLine($"--swift-a11y-outline-color-rgb: {HexToRGB(accessibilityOutlineColor)};"); 395 } 396 sb.AppendLine("}"); 397 398 Dynamicweb.Core.Helpers.TextFileHelper.WriteTextFile(sb.ToString() + Environment.NewLine, Dynamicweb.Context.Current.Server.MapPath($"/Files/Templates/Designs/Swift/_parsed/Swift_css/Swift_theme_styles_{Dynamicweb.Content.Services.Pages.GetPage(Model.PageID).AreaId}.tmp"), true); 399 } 400 } 401 402 @if (themeClassName != "") 403 { 404 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 405 406 <div class="g-col-12 g-col-lg-4"> 407 <div class="swift-checkered p-3"> 408 <div class="theme-option theme @themeClassName p-lg-3"> 409 <div class="mb-2 pb-1 border-bottom d-flex align-items-center"> 410 <span class="icon-3 me-2"> 411 @ReadFile(iconPath + "sun.svg") 412 </span> 413 <h4 class="m-0 p-0">@themeName</h4> 414 </div> 415 <div class="d-flex flex-column gap-1 mb-2"> 416 <p class="mb-0">This is the body text of the theme.</p> 417 <p class="mb-0" style="color: var(--swift-accent-color)">This is the accent color of the theme.</p> 418 </div> 419 <div> 420 <button class="btn btn-primary me-1">Primary</button> 421 <button class="btn btn-secondary">Secondary</button> 422 <button class="btn btn-link">Link</button> 423 </div> 424 </div> 425 </div> 426 </div> 427 <div class="g-col-12 g-col-lg-8 mb-4 mb-lg-0"> 428 <div class="grid fs-7"> 429 <div class="g-col-12 g-col-lg-3"> 430 <table class="table table-borderless table-sm w-100"> 431 <thead> 432 <tr> 433 <th colspan="2" class="fw-bold">@Translate("Base")</th> 434 </tr> 435 </thead> 436 <tr> 437 <td>@Translate("Foreground")</td> 438 <td>@GetColorSquare(foregroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@foregroundColor');">@foregroundColor</a></td> 439 </tr> 440 <tr> 441 <td>@Translate("Background")</td> 442 <td>@GetColorSquare(backgroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@backgroundColor');">@backgroundColor</a></td> 443 </tr> 444 <tr> 445 <td>@Translate("Accent")</td> 446 <td>@GetColorSquare(accentColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@accentColor');">@accentColor</a></td> 447 </tr> 448 <tr> 449 <td>@Translate("Border")</td> 450 <td>@GetColorSquare(borderColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@borderColor');">@borderColor</a></td> 451 </tr> 452 </table> 453 </div> 454 <div class="g-col-12 g-col-lg-3"> 455 <table class="table table-borderless table-sm w-100"> 456 <thead> 457 <tr> 458 <th colspan="2" class="fw-bold">@Translate("Primary button")</th> 459 </tr> 460 </thead> 461 <tr> 462 <td>@Translate("Foreground")</td> 463 <td>@GetColorSquare(buttonPrimaryForegroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonPrimaryForegroundColor');">@buttonPrimaryForegroundColor</a></td> 464 </tr> 465 <tr> 466 <td>@Translate("Background")</td> 467 <td>@GetColorSquare(buttonPrimaryBackgroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonPrimaryBackgroundColor');">@buttonPrimaryBackgroundColor</a></td> 468 </tr> 469 @if (!string.IsNullOrEmpty(buttonPrimaryBorderColor)) 470 { 471 <tr> 472 <td>@Translate("Border")</td> 473 <td>@GetColorSquare(buttonPrimaryBorderColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonPrimaryBorderColor');">@buttonPrimaryBorderColor</a></td> 474 </tr> 475 } 476 <thead> 477 <tr> 478 <th colspan="2" class="fw-bold">@Translate("Primary button hover")</th> 479 </tr> 480 </thead> 481 @if (!string.IsNullOrEmpty(buttonPrimaryHoverForegroundColor)) 482 { 483 <tr> 484 <td>@Translate("Foreground")</td> 485 <td>@GetColorSquare(buttonPrimaryHoverForegroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonPrimaryHoverForegroundColor');">@buttonPrimaryHoverForegroundColor</a></td> 486 </tr> 487 } 488 @if (!string.IsNullOrEmpty(buttonPrimaryHoverBackgroundColor)) 489 { 490 <tr> 491 <td>@Translate("Background")</td> 492 <td>@GetColorSquare(buttonPrimaryHoverBackgroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonPrimaryHoverBackgroundColor');">@buttonPrimaryHoverBackgroundColor</a></td> 493 </tr> 494 } 495 @if (!string.IsNullOrEmpty(buttonPrimaryHoverBorderColor)) 496 { 497 <tr> 498 <td>@Translate("Border")</td> 499 <td>@GetColorSquare(buttonPrimaryHoverBorderColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonPrimaryHoverBorderColor');">@buttonPrimaryHoverBorderColor</a></td> 500 </tr> 501 } 502 </table> 503 </div> 504 <div class="g-col-12 g-col-lg-3"> 505 <table class="table table-borderless table-sm w-100"> 506 <thead> 507 <tr> 508 <th colspan="2" class="fw-bold">@Translate("Secondary button")</th> 509 </tr> 510 </thead> 511 <tr> 512 <td>@Translate("Foreground")</td> 513 <td>@GetColorSquare(buttonSecondaryForegroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonSecondaryForegroundColor');">@buttonSecondaryForegroundColor</a></td> 514 </tr> 515 <tr> 516 <td>@Translate("Background")</td> 517 <td>@GetColorSquare(buttonSecondaryBackgroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonSecondaryBackgroundColor');">@buttonSecondaryBackgroundColor</a></td> 518 </tr> 519 @if (!string.IsNullOrEmpty(buttonSecondaryBorderColor)) 520 { 521 <tr> 522 <td>@Translate("Border")</td> 523 <td>@GetColorSquare(buttonSecondaryBorderColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonSecondaryBorderColor');">@buttonSecondaryBorderColor</a></td> 524 </tr> 525 } 526 <thead> 527 <tr> 528 <th colspan="2" class="fw-bold">@Translate("Secondary button hover")</th> 529 </tr> 530 </thead> 531 @if (!string.IsNullOrEmpty(buttonSecondaryHoverForegroundColor)) 532 { 533 <tr> 534 <td>@Translate("Foreground")</td> 535 <td>@GetColorSquare(buttonSecondaryHoverForegroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonSecondaryHoverForegroundColor');">@buttonSecondaryHoverForegroundColor</a></td> 536 </tr> 537 } 538 @if (!string.IsNullOrEmpty(buttonSecondaryHoverBackgroundColor)) 539 { 540 <tr> 541 <td>@Translate("Background")</td> 542 <td>@GetColorSquare(buttonSecondaryHoverBackgroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonSecondaryHoverBackgroundColor');">@buttonSecondaryHoverBackgroundColor</a></td> 543 </tr> 544 } 545 @if (!string.IsNullOrEmpty(buttonSecondaryHoverBorderColor)) 546 { 547 <tr> 548 <td>@Translate("Border")</td> 549 <td>@GetColorSquare(buttonSecondaryHoverBorderColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonSecondaryHoverBorderColor');">@buttonSecondaryHoverBorderColor</a></td> 550 </tr> 551 } 552 @if (!string.IsNullOrEmpty(accessibilityOutlineColor)) 553 { 554 <tr> 555 <td>@Translate("Focus outline")</td> 556 <td>@GetColorSquare(accessibilityOutlineColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@accessibilityOutlineColor');">@accessibilityOutlineColor</a></td> 557 </tr> 558 } 559 </table> 560 </div> 561 <div class="g-col-12 g-col-lg-3"> 562 <table class="table table-borderless table-sm w-100"> 563 <thead> 564 <tr> 565 <th colspan="2" class="fw-bold">@Translate("Link button")</th> 566 </tr> 567 </thead> 568 <tr> 569 <td>@Translate("Link color")</td> 570 <td>@GetColorSquare(buttonLinkColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonLinkColor');">@buttonLinkColor</a></td> 571 </tr> 572 <thead> 573 <tr> 574 <th colspan="2" class="fw-bold">@Translate("Link button hover")</th> 575 </tr> 576 </thead> 577 @if (!string.IsNullOrEmpty(buttonLinkHoverColor)) 578 { 579 <tr> 580 <td>@Translate("Link hover color")</td> 581 <td>@GetColorSquare(buttonLinkHoverColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonLinkHoverColor');">@buttonLinkHoverColor</a></td> 582 </tr> 583 } 584 585 @if (!string.IsNullOrEmpty(accessibilityOutlineColor)) 586 { 587 <tr> 588 <td>@Translate("Focus outline")</td> 589 <td>@GetColorSquare(accessibilityOutlineColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@accessibilityOutlineColor');">@accessibilityOutlineColor</a></td> 590 </tr> 591 } 592 </table> 593 </div> 594 <div class="g-col-12 g-col-lg-3"> 595 <table class="table table-borderless table-sm w-100"> 596 <tr> 597 <td class="fw-bold">@Translate("Implementation")</td> 598 </tr> 599 <tr> 600 <td> 601 @{ 602 string implementation = $"<div class=\"theme {themeClassName}\"></div>"; 603 } 604 <div class="text-muted"> 605 <code class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@themeClassName');"> 606 @Dynamicweb.Core.Encoders.HtmlEncoder.HtmlEncode(implementation) 607 </code> 608 </div> 609 </td> 610 </tr> 611 </table> 612 </div> 613 @{ 614 bool hasUsageData = Converter.ToBoolean(Dynamicweb.Context.Current.Request["auditUsages"]); 615 if (hasUsageData) 616 { 617 var themeUsage = GetThemeUsageById(themeClassName); 618 int allUsages = themeUsage?.AllUsages ?? 0; 619 string buttonAttributes = allUsages > 0 ? $"data-bs-toggle=\"modal\" data-bs-target=\"#themeUsage_{Model.ID}\"" : string.Empty; 620 string badgeStyle = allUsages > 0 ? "bg-success" : "bg-danger"; 621 622 <div class="g-col-12 g-col-lg-9 position-relative"> 623 <button type="button" class="btn btn-primary btn-sm position-absolute end-0" @buttonAttributes> 624 Usages 625 <span class="position-absolute top-0 start-100 translate-middle badge rounded-pill text-white @badgeStyle">@allUsages</span> 626 </button> 627 628 @if (allUsages > 0) 629 { 630 int websiteUsages = themeUsage?.WebsiteUsages.Count ?? 0; 631 int pageUsages = themeUsage?.PageUsages.Count ?? 0; 632 int pagePropertyUsages = themeUsage?.PagePropertyUsages.Count ?? 0; 633 int gridRowUsages = themeUsage?.GridRowUsages.Count ?? 0; 634 int paragraphUsages = themeUsage?.ParagraphUsages.Count ?? 0; 635 636 <!-- Modal --> 637 <div class="modal fade" id="themeUsage_@(Model.ID)" tabindex="-1" aria-labelledby="usagesModelLabel" aria-hidden="true"> 638 <div class="modal-dialog modal-xl modal-dialog-centered modal-dialog-scrollable"> 639 <div class="modal-content"> 640 <div class="modal-header"> 641 <h5 class="modal-title" id="usagesModelLabel">@allUsages Usages of '@(themeName)' (@(themeClassName))</h5> 642 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 643 </div> 644 <div class="modal-body"> 645 <div class="accordion" id="accordionWrapper"> 646 @if(websiteUsages > 0) 647 { 648 <div class="accordion-item"> 649 <h2 class="accordion-header" id="headingWebsites"> 650 <button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseWebsites" aria-expanded="true" aria-controls="collapseWebsites"> 651 <span class="pe-2">Websites</span><span class="badge rounded-pill bg-secondary text-light">@websiteUsages</span> 652 </button> 653 </h2> 654 <div id="collapseWebsites" class="accordion-collapse collapse show" aria-labelledby="headingWebsites" data-bs-parent="#accordionWrapper"> 655 <div class="accordion-body"> 656 <ul> 657 @foreach (var usage in themeUsage.WebsiteUsages) 658 { 659 var website = Dynamicweb.Content.Services.Areas.GetArea(usage); 660 <li><span class="fw-bold">@website.Name</span>@($" (ID: {usage})")</li> 661 } 662 </ul> 663 </div> 664 </div> 665 </div> 666 } 667 @if (pageUsages > 0) 668 { 669 var pages = Dynamicweb.Content.Services.Pages.GetPages(themeUsage.PageUsages.ToArray()); 670 671 <div class="accordion-item"> 672 <h2 class="accordion-header" id="headingPages"> 673 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapsePages" aria-expanded="false" aria-controls="collapsePages"> 674 <span class="pe-2">Pages</span><span class="badge rounded-pill bg-secondary text-light">@pageUsages</span> 675 </button> 676 </h2> 677 <div id="collapsePages" class="accordion-collapse collapse" aria-labelledby="headingPages" data-bs-parent="#accordionWrapper"> 678 <div class="accordion-body"> 679 <ul> 680 @foreach (var usage in themeUsage.PageUsages) 681 { 682 <li><span class="fw-bold">@pages.FirstOrDefault(p => p.ID == usage).GetDisplayName()</span>@($" (ID: {usage})")</li> 683 } 684 </ul> 685 </div> 686 </div> 687 </div> 688 } 689 @if (pagePropertyUsages > 0) 690 { 691 var pages = Dynamicweb.Content.Services.Pages.GetPages(themeUsage.PageUsages.ToArray()); 692 693 <div class="accordion-item"> 694 <h2 class="accordion-header" id="headingPageProperties"> 695 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapsePageProperties" aria-expanded="false" aria-controls="collapsePageProperties"> 696 <span class="pe-2">Page Properties</span><span class="badge rounded-pill bg-secondary text-light">@pagePropertyUsages</span> 697 </button> 698 </h2> 699 <div id="collapsePageProperties" class="accordion-collapse collapse" aria-labelledby="headingPageProperties" data-bs-parent="#accordionWrapper"> 700 <div class="accordion-body"> 701 <ul> 702 @foreach (var usage in themeUsage.PagePropertyUsages) 703 { 704 <li><span class="fw-bold">@pages.FirstOrDefault(p => p.ID == usage).GetDisplayName()</span>@($" (ID: {usage})")</li> 705 } 706 </ul> 707 </div> 708 </div> 709 </div> 710 } 711 @if (gridRowUsages > 0) 712 { 713 var rows = Dynamicweb.Content.Services.Grids.GetGridRows().Where(r => themeUsage.GridRowUsages.Contains(r.ID)).ToList(); 714 715 <div class="accordion-item"> 716 <h2 class="accordion-header" id="headingRows"> 717 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseRows" aria-expanded="false" aria-controls="collapseRows"> 718 <span class="pe-2">Rows</span><span class="badge rounded-pill bg-secondary text-light">@gridRowUsages</span> 719 </button> 720 </h2> 721 <div id="collapseRows" class="accordion-collapse collapse" aria-labelledby="headingRows" data-bs-parent="#accordionWrapper"> 722 <div class="accordion-body"> 723 <ul> 724 @foreach (var usage in themeUsage.GridRowUsages) 725 { 726 var row = rows.FirstOrDefault(r => r.ID == usage); 727 var page = Dynamicweb.Content.Services.Pages.GetPage(row.PageId); 728 <li>Page: <span class="fw-bold">@(page.GetDisplayName())</span>@($"( ID: {page.ID}) - Row: ")<span class="fw-bold">@(row.DefinitionId)</span>@($" (ID: {usage})")</li> 729 } 730 </ul> 731 </div> 732 </div> 733 </div> 734 } 735 @if (paragraphUsages > 0) 736 { 737 var paragraphs = Dynamicweb.Content.Services.Paragraphs.GetParagraphs().Where(p => themeUsage.ParagraphUsages.Contains(p.ID)).ToList(); 738 739 <div class="accordion-item"> 740 <h2 class="accordion-header" id="headingParagraphs"> 741 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseParagraphs" aria-expanded="false" aria-controls="collapseParagraphs"> 742 <span class="pe-2">Paragraphs</span><span class="badge rounded-pill bg-secondary text-light">@paragraphUsages</span> 743 </button> 744 </h2> 745 <div id="collapseParagraphs" class="accordion-collapse collapse" aria-labelledby="headingParagraphs" data-bs-parent="#accordionWrapper"> 746 <div class="accordion-body"> 747 <ul> 748 @foreach (var usage in themeUsage.ParagraphUsages) 749 { 750 var paragraph = paragraphs.FirstOrDefault(p => p.ID == usage); 751 var page = Dynamicweb.Content.Services.Pages.GetPage(paragraph.PageID); 752 <li>Page: <span class="fw-bold">@(page.GetDisplayName())</span>@($"( ID: {page.ID}) - Paragraph: ")<span class="fw-bold">@paragraph.Header</span>@($" (ID: {usage})")</li> 753 } 754 </ul> 755 </div> 756 </div> 757 </div> 758 } 759 </div> 760 </div> 761 </div> 762 </div> 763 </div> 764 } 765 </div> 766 } 767 } 768 </div> 769 </div> 770 } 771 <div class="g-col-12"> 772 <hr /> 773 </div> 774

Green background

This is the body text of the theme.

This is the accent color of the theme.

Base
Foreground
#ffffff
Background
#304f44
Accent
currentColor
Border
transparent
Primary button
Foreground
currentColor
Background
transparent
Border
transparent
Primary button hover
Foreground
currentColor
Background
transparent
Border
transparent
Secondary button
Foreground
currentColor
Background
transparent
Border
transparent
Secondary button hover
Foreground
currentColor
Background
transparent
Border
transparent
Link button
Link color
currentColor
Link button hover
Link hover color
currentColor
Implementation
<div class="theme oo-green-bg"></div>

Error executing template "Designs/Swift/Paragraph/Swift_Theme.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_f772bb33646d4d3c8ae681f5d1cf870b.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.ParagraphViewModel> 2 @using System.Globalization 3 @using System.Drawing 4 @using Dynamicweb.Core 5 6 @functions{ 7 // Get Hex Color from color picker and translate to RGB color 8 public static string HexToRGB(string hexString) 9 { 10 if (hexString.Equals("none") || string.IsNullOrEmpty(hexString)) 11 return hexString; 12 13 //replace # occurences 14 if (hexString.IndexOf('#') != -1) 15 hexString = hexString.Replace("#", ""); 16 try 17 { 18 int r, g, b = 0; 19 r = int.Parse(hexString.Substring(0, 2), NumberStyles.AllowHexSpecifier); 20 g = int.Parse(hexString.Substring(2, 2), NumberStyles.AllowHexSpecifier); 21 b = int.Parse(hexString.Substring(4, 2), NumberStyles.AllowHexSpecifier); 22 23 string rgb = r + "," + g + "," + b; 24 return rgb; 25 } 26 catch (System.Exception) 27 { 28 return hexString; 29 } 30 } 31 32 public static string GetColorSquare(string hex) 33 { 34 return $"<div class=\"swift-checkered d-inline-block me-1\" style=\"height:1.25rem;width:1.25rem;\"><div style=\"background-color: {hex};border:.0625rem solid grey\" class=\"w-100 h-100\"></div></div>"; 35 } 36 37 public class HSL 38 { 39 public double h { get; set; } 40 public double s { get; set; } 41 public double l { get; set; } 42 } 43 44 //Convert HEX to HSL 45 public static HSL HexToHsl(string hexString) 46 { 47 System.Drawing.Color color = System.Drawing.ColorTranslator.FromHtml(hexString); 48 49 float _R = (color.R / 255f); 50 float _G = (color.G / 255f); 51 float _B = (color.B / 255f); 52 53 float _Min = Math.Min(Math.Min(_R, _G), _B); 54 float _Max = Math.Max(Math.Max(_R, _G), _B); 55 float _Delta = _Max - _Min; 56 57 float S = 0; 58 float L = (float)((_Max + _Min) / 2.0f); 59 60 if (_Delta != 0) 61 { 62 if (L < 0.5f) 63 { 64 S = (float)(_Delta / (_Max + _Min)); 65 } 66 else 67 { 68 S = (float)(_Delta / (2.0f - _Max - _Min)); 69 } 70 } 71 72 HSL hsl = new HSL(); 73 hsl.h = Math.Round(color.GetHue(), 2); 74 hsl.s = Math.Round(S, 2) * 100; 75 hsl.l = Math.Round(L, 2) * 100; 76 77 return hsl; 78 } 79 80 //Find contrast color (white, black) 81 public static Color GetContrastColor(string hexString) 82 { 83 System.Drawing.Color bg = System.Drawing.ColorTranslator.FromHtml(hexString); 84 85 int nThreshold = 105; 86 int bgDelta = Convert.ToInt32((bg.R * 0.299) + (bg.G * 0.587) + 87 (bg.B * 0.114)); 88 89 Color foreColor = (255 - bgDelta < nThreshold) ? Color.Black : Color.White; 90 return foreColor; 91 } 92 93 public string GetColorVariation(string hexString) 94 { 95 var contrast = GetContrastColor(hexString); 96 HSL currentHslColor = HexToHsl(hexString); 97 HSL variantHslColor = currentHslColor; 98 99 if (contrast == Color.Black) 100 { 101 variantHslColor.l = 85; //The background is dark - Color should be light 102 } 103 else 104 { 105 variantHslColor.l = 40; //The background is light - Color should be less light 106 } 107 108 //Color is very light 109 if (currentHslColor.l > 85) 110 { 111 variantHslColor.l = 95; 112 } 113 114 string colorString = variantHslColor.h + ", " + variantHslColor.s + "%, " + variantHslColor.l + "%"; 115 return colorString; 116 } 117 118 private class ThemeDatabaseUsage 119 { 120 public string TableName { get; set; } 121 public string ColumnName { get; set; } 122 } 123 124 private class ThemeUsage 125 { 126 public string Id { get; set; } 127 public List<int> WebsiteUsages { get; set; } = new List<int>(); 128 public List<int> PageUsages { get; set; } = new List<int>(); 129 public List<int> PagePropertyUsages { get; set; } = new List<int>(); 130 public List<int> GridRowUsages { get; set; } = new List<int>(); 131 public List<int> ParagraphUsages { get; set; } = new List<int>(); 132 public int AllUsages { get; set; } = 0; 133 } 134 135 private List<ThemeDatabaseUsage> GetThemeColumnsFromSchema() 136 { 137 string cacheKey = "ThemesFromDatabase"; 138 if (Dynamicweb.Context.Current.Items.Contains(cacheKey)) 139 { 140 return (List<ThemeDatabaseUsage>)Dynamicweb.Context.Current.Items[cacheKey]; 141 } 142 var schema = Dynamicweb.Data.Database.CreateDataReader("SELECT table_name, column_name FROM information_schema.columns WHERE column_name LIKE '%Theme%'"); 143 144 var rawData = new List<ThemeDatabaseUsage>(); 145 while (schema.Read()) { 146 rawData.Add(new ThemeDatabaseUsage {TableName = schema["table_name"].ToString(), ColumnName = schema["column_name"].ToString() }); 147 } 148 Dynamicweb.Context.Current.Items[cacheKey] = rawData; 149 return rawData; 150 } 151 152 private List<ThemeUsage> GetAllThemeUsages() 153 { 154 string cacheKey = "ThemeUsages"; 155 if (Dynamicweb.Context.Current.Items.Contains(cacheKey)) 156 { 157 return (List<ThemeUsage>)Dynamicweb.Context.Current.Items[cacheKey]; 158 } 159 160 var rawData = GetThemeColumnsFromSchema(); 161 var themeUsages = new List<ThemeUsage>(); 162 163 var sqlQuery = ""; 164 foreach (var row in rawData) 165 { 166 if (!string.IsNullOrEmpty(sqlQuery)) { sqlQuery += " UNION ALL "; } 167 sqlQuery += $"SELECT '{row.TableName}' AS TableName, '{row.ColumnName}' AS ColumnName, {row.ColumnName} AS ColumnValue, Id, AreaId, Page.PageId, PageProperties.PageId as 'PagePropertiesId', GridRowId, ParagraphId" + 168 $" FROM [{row.TableName}]" + 169 $" LEFT JOIN Area ON AreaItemType = '{row.TableName.Replace("ItemType_","")}' AND AreaItemId = Id" + 170 $" LEFT JOIN Page ON PageItemType = '{row.TableName.Replace("ItemType_","")}' AND PageItemId = Id" + 171 $" LEFT JOIN Page as PageProperties ON AreaItemTypePageProperty = '{row.TableName.Replace("ItemType_","")}' AND PageProperties.PagePropertyItemId = Id" + 172 $" LEFT JOIN GridRow ON GridRowItemType = '{row.TableName.Replace("ItemType_","")}' AND GridRowItemId = Id" + 173 $" LEFT JOIN Paragraph ON ParagraphItemType = '{row.TableName.Replace("ItemType_","")}' AND ParagraphItemId = Id" + 174 $" WHERE {row.ColumnName} != '' AND {row.ColumnName} IS NOT NULL" + 175 $" AND (AreaId IS NULL OR AreaId = {Pageview.AreaID})" + 176 $" AND (Page.PageId IS NULL OR Page.PageAreaId = {Pageview.AreaID})" + 177 $" AND (PageProperties.PageId IS NULL OR PageProperties.PageAreaId = {Pageview.AreaID})" + 178 $" AND (GridRowId IS NULL OR GridRowPageId IN (SELECT value From string_split(@pageIds, ',')))" + 179 $" AND (ParagraphId IS NULL OR ParagraphPageId IN (SELECT value From string_split(@pageIds, ',')))"; 180 181 var maxMsSqlServerCharLimit = 60000; 182 if (sqlQuery.Length > maxMsSqlServerCharLimit) 183 { 184 UpdateThemeUsages(sqlQuery, themeUsages); 185 sqlQuery = ""; 186 } 187 } 188 189 if (!string.IsNullOrEmpty(sqlQuery)) 190 { 191 UpdateThemeUsages(sqlQuery, themeUsages); 192 } 193 UpdateAllUsages(themeUsages); 194 195 Dynamicweb.Context.Current.Items[cacheKey] = themeUsages; 196 return themeUsages; 197 } 198 199 private void UpdateThemeUsages(string sqlQuery, List<ThemeUsage> themeUsages) 200 { 201 sqlQuery = $"DECLARE @pageIds NVARCHAR(max) = (SELECT STRING_AGG(PageId,',') FROM Page WHERE PageAreaId = {Pageview.AreaID}) " + sqlQuery; 202 var rawData = Dynamicweb.Data.Database.CreateDataReader(sqlQuery); 203 while (rawData.Read()) 204 { 205 var themeId = rawData["ColumnValue"].ToString(); 206 var themeUsageExists = themeUsages.FirstOrDefault(t => t.Id == themeId) != null; 207 if (!themeUsageExists) 208 { 209 themeUsages.Add(new ThemeUsage { Id = themeId }); 210 } 211 212 var themeUsage = themeUsages.FirstOrDefault(t => t.Id == themeId); 213 if (themeUsage == null) continue; 214 215 if (Converter.ToInt32(rawData["AreaId"].ToString()) > 0) { themeUsage.WebsiteUsages.Add(Converter.ToInt32(rawData["AreaId"].ToString())); } 216 if (Converter.ToInt32(rawData["PageId"].ToString()) > 0) { themeUsage.PageUsages.Add(Converter.ToInt32(rawData["PageId"].ToString())); } 217 if (Converter.ToInt32(rawData["PagePropertiesId"].ToString()) > 0) { themeUsage.PagePropertyUsages.Add(Converter.ToInt32(rawData["PagePropertiesId"].ToString())); } 218 if (Converter.ToInt32(rawData["GridRowId"].ToString()) > 0) { themeUsage.GridRowUsages.Add(Converter.ToInt32(rawData["GridRowId"].ToString())); } 219 if (Converter.ToInt32(rawData["ParagraphId"].ToString()) > 0) { themeUsage.ParagraphUsages.Add(Converter.ToInt32(rawData["ParagraphId"].ToString())); } 220 } 221 } 222 223 private void UpdateAllUsages(List<ThemeUsage> themeUsages) 224 { 225 foreach (var themeUsage in themeUsages) 226 { 227 int websiteUsages = themeUsage?.WebsiteUsages.Count ?? 0; 228 int pageUsages = themeUsage?.PageUsages.Count ?? 0; 229 int pagePropertyUsages = themeUsage?.PagePropertyUsages.Count ?? 0; 230 int gridRowUsages = themeUsage?.GridRowUsages.Count ?? 0; 231 int paragraphUsages = themeUsage?.ParagraphUsages.Count ?? 0; 232 themeUsage.AllUsages = websiteUsages + pageUsages + pagePropertyUsages + gridRowUsages + paragraphUsages; 233 } 234 } 235 236 private ThemeUsage GetThemeUsageById(string themeId) 237 { 238 var themeUsages = GetAllThemeUsages(); 239 return themeUsages.FirstOrDefault(t => t.Id == themeId); 240 } 241 242 } 243 244 @{ 245 string themeName = !string.IsNullOrWhiteSpace(Model.Item.GetString("Name")) ? Model.Item.GetString("Name") : "Default"; 246 string themeClassName = !string.IsNullOrWhiteSpace(Model.Item.GetString("CSSClassName")) ? Model.Item.GetString("CSSClassName").Replace(" ", "").Trim().ToLower() : ""; 247 248 //Theme colors 249 string foregroundColor = Model.Item.GetString("ForegroundColor", "currentColor"); 250 string backgroundColor = Model.Item.GetString("BackgroundColor", "transparent"); 251 string accentColor = Model.Item.GetString("accentColor", "currentColor"); 252 string borderColor = Model.Item.GetString("BorderColor", "transparent"); 253 254 //Primary button 255 string buttonPrimaryForegroundColor = Model.Item.GetString("ButtonPrimaryForegroundColor", "currentColor"); 256 string buttonPrimaryBackgroundColor = Model.Item.GetString("ButtonPrimaryBackgroundColor", "transparent"); 257 string buttonPrimaryBorderColor = Model.Item.GetString("ButtonPrimaryBorderColor", "transparent"); 258 259 string buttonPrimaryHoverBackgroundColorAuto = "transparent"; 260 if (buttonPrimaryBackgroundColor != "transparent") 261 { 262 buttonPrimaryHoverBackgroundColorAuto = "hsl(" + GetColorVariation(buttonPrimaryBackgroundColor) + ")"; 263 } 264 string buttonPrimaryHoverForegroundColor = Model.Item.GetString("ButtonPrimaryHoverForegroundColor", buttonPrimaryForegroundColor); 265 string buttonPrimaryHoverBackgroundColor = Model.Item.GetString("ButtonPrimaryHoverBackgroundColor", buttonPrimaryHoverBackgroundColorAuto); 266 string buttonPrimaryHoverBorderColor = Model.Item.GetString("ButtonPrimaryHoverBorderColor", buttonPrimaryBorderColor); 267 268 //Secondary buton 269 string buttonSecondaryForegroundColor = Model.Item.GetString("ButtonSecondaryForegroundColor", "currentColor"); 270 string buttonSecondaryBackgroundColor = Model.Item.GetString("ButtonSecondaryBackgroundColor", "transparent"); 271 string buttonSecondaryBorderColor = Model.Item.GetString("ButtonSecondaryBorderColor", "transparent"); 272 273 string buttonSecondaryHoverBackgroundColorAuto = "transparent"; 274 if (buttonSecondaryBackgroundColor != "transparent") 275 { 276 buttonSecondaryHoverBackgroundColorAuto = "hsl(" + GetColorVariation(buttonSecondaryBackgroundColor) + ")"; 277 } 278 string buttonSecondaryHoverForegroundColor = Model.Item.GetString("ButtonSecondaryHoverForegroundColor", buttonSecondaryForegroundColor); 279 string buttonSecondaryHoverBackgroundColor = Model.Item.GetString("ButtonSecondaryHoverBackgroundColor", buttonSecondaryHoverBackgroundColorAuto); 280 string buttonSecondaryHoverBorderColor = Model.Item.GetString("ButtonSecondaryHoverBorderColor", buttonSecondaryBorderColor); 281 282 //Link buton 283 string buttonLinkColor = Model.Item.GetString("ButtonLinkColor", "currentColor"); 284 string buttonLinkHoverColorAuto = "currentColor"; 285 if (buttonLinkColor != "currentColor") 286 { 287 buttonLinkHoverColorAuto = "hsl(" + GetColorVariation(buttonLinkColor) + ")"; 288 } 289 string buttonLinkHoverColor = Model.Item.GetString("ButtonLinkHoverColor", buttonLinkHoverColorAuto); 290 291 string accessibilityOutlineColor = Model.Item.GetString("OutlineColor") != null ? Model.Item.GetString("OutlineColor") : ""; 292 293 if (!string.IsNullOrEmpty(themeClassName)) 294 { 295 var sb = new System.Text.StringBuilder(); 296 297 sb.AppendLine("." + themeClassName + "{"); 298 if (!string.IsNullOrEmpty(foregroundColor)) 299 { 300 sb.AppendLine($"--swift-foreground-color: {foregroundColor};"); 301 sb.AppendLine($"--swift-foreground-color-rgb: {HexToRGB(foregroundColor)};"); 302 } 303 if (!string.IsNullOrEmpty(backgroundColor)) 304 { 305 sb.AppendLine($"--swift-background-color: {backgroundColor};"); 306 sb.AppendLine($"--swift-background-color-rgb: {HexToRGB(backgroundColor)};"); 307 } 308 if (!string.IsNullOrEmpty(accentColor)) 309 { 310 sb.AppendLine($"--swift-accent-color: {accentColor};"); 311 sb.AppendLine($"--swift-accent-color-rgb: {HexToRGB(accentColor)};"); 312 } 313 if (!string.IsNullOrEmpty(borderColor)) 314 { 315 sb.AppendLine($"--swift-border-color: {borderColor};"); 316 sb.AppendLine($"--swift-border-color-rgb: {HexToRGB(borderColor)};"); 317 } 318 319 if (!string.IsNullOrEmpty(buttonPrimaryBackgroundColor)) 320 { 321 sb.AppendLine($"--swift-button-primary-background-color: {buttonPrimaryBackgroundColor};"); 322 sb.AppendLine($"--swift-button-primary-background-color-rgb: {HexToRGB(buttonPrimaryBackgroundColor)};"); 323 } 324 if (!string.IsNullOrEmpty(buttonPrimaryForegroundColor)) 325 { 326 sb.AppendLine($"--swift-button-primary-foreground-color: {buttonPrimaryForegroundColor};"); 327 sb.AppendLine($"--swift-button-primary-foreground-color-rgb: {HexToRGB(buttonPrimaryForegroundColor)};"); 328 } 329 if (!string.IsNullOrEmpty(buttonPrimaryBorderColor)) 330 { 331 sb.AppendLine($"--swift-button-primary-border-color: {buttonPrimaryBorderColor};"); 332 sb.AppendLine($"--swift-button-primary-border-color-rgb: {HexToRGB(buttonPrimaryBorderColor)};"); 333 } 334 335 if (!string.IsNullOrEmpty(buttonPrimaryHoverBackgroundColor)) 336 { 337 sb.AppendLine($"--swift-button-primary-hover-background-color: {buttonPrimaryHoverBackgroundColor};"); 338 sb.AppendLine($"--swift-button-primary-hover-background-color-rgb: {HexToRGB(buttonPrimaryHoverBackgroundColor)};"); 339 } 340 if (!string.IsNullOrEmpty(buttonPrimaryHoverForegroundColor)) 341 { 342 sb.AppendLine($"--swift-button-primary-hover-foreground-color: {buttonPrimaryHoverForegroundColor};"); 343 sb.AppendLine($"--swift-button-primary-hover-foreground-color-rgb: {HexToRGB(buttonPrimaryHoverForegroundColor)};"); 344 } 345 if (!string.IsNullOrEmpty(buttonPrimaryHoverBorderColor)) 346 { 347 sb.AppendLine($"--swift-button-primary-hover-border-color: {buttonPrimaryHoverBorderColor};"); 348 sb.AppendLine($"--swift-button-primary-hover-border-color-rgb: {HexToRGB(buttonPrimaryHoverBorderColor)};"); 349 } 350 351 if (!string.IsNullOrEmpty(buttonSecondaryBackgroundColor)) 352 { 353 sb.AppendLine($"--swift-button-secondary-background-color: {buttonSecondaryBackgroundColor};"); 354 sb.AppendLine($"--swift-button-secondary-background-color-rgb: {HexToRGB(buttonSecondaryBackgroundColor)};"); 355 } 356 if (!string.IsNullOrEmpty(buttonSecondaryForegroundColor)) 357 { 358 sb.AppendLine($"--swift-button-secondary-foreground-color: {buttonSecondaryForegroundColor};"); 359 sb.AppendLine($"--swift-button-secondary-foreground-color-rgb: {HexToRGB(buttonSecondaryForegroundColor)};"); 360 } 361 if (!string.IsNullOrEmpty(buttonSecondaryBorderColor)) 362 { 363 sb.AppendLine($"--swift-button-secondary-border-color: {buttonSecondaryBorderColor};"); 364 sb.AppendLine($"--swift-button-secondary-border-color-rgb: {HexToRGB(buttonSecondaryBorderColor)};"); 365 } 366 if (!string.IsNullOrEmpty(buttonSecondaryHoverBackgroundColor)) 367 { 368 sb.AppendLine($"--swift-button-secondary-hover-background-color: {buttonSecondaryHoverBackgroundColor};"); 369 sb.AppendLine($"--swift-button-secondary-hover-background-color-rgb: {HexToRGB(buttonSecondaryHoverBackgroundColor)};"); 370 } 371 if (!string.IsNullOrEmpty(buttonSecondaryHoverForegroundColor)) 372 { 373 sb.AppendLine($"--swift-button-secondary-hover-foreground-color: {buttonSecondaryHoverForegroundColor};"); 374 sb.AppendLine($"--swift-button-secondary-hover-foreground-color-rgb: {HexToRGB(buttonSecondaryHoverForegroundColor)};"); 375 } 376 if (!string.IsNullOrEmpty(buttonSecondaryHoverBorderColor)) 377 { 378 sb.AppendLine($"--swift-button-secondary-hover-border-color: {buttonSecondaryHoverBorderColor};"); 379 sb.AppendLine($"--swift-button-secondary-hover-border-color-rgb: {HexToRGB(buttonSecondaryHoverBorderColor)};"); 380 } 381 if (!string.IsNullOrEmpty(buttonLinkColor)) 382 { 383 sb.AppendLine($"--swift-button-link-color: {buttonLinkColor};"); 384 sb.AppendLine($"--swift-button-link-color-rgb: {HexToRGB(buttonLinkColor)};"); 385 } 386 if (!string.IsNullOrEmpty(buttonLinkHoverColor)) 387 { 388 sb.AppendLine($"--swift-button-link-hover-color: {buttonLinkHoverColor};"); 389 sb.AppendLine($"--swift-button-link-hover-color-rgb: {HexToRGB(buttonLinkHoverColor)};"); 390 } 391 if (!string.IsNullOrEmpty(accessibilityOutlineColor)) 392 { 393 sb.AppendLine($"--swift-a11y-outline-color: {accessibilityOutlineColor};"); 394 sb.AppendLine($"--swift-a11y-outline-color-rgb: {HexToRGB(accessibilityOutlineColor)};"); 395 } 396 sb.AppendLine("}"); 397 398 Dynamicweb.Core.Helpers.TextFileHelper.WriteTextFile(sb.ToString() + Environment.NewLine, Dynamicweb.Context.Current.Server.MapPath($"/Files/Templates/Designs/Swift/_parsed/Swift_css/Swift_theme_styles_{Dynamicweb.Content.Services.Pages.GetPage(Model.PageID).AreaId}.tmp"), true); 399 } 400 } 401 402 @if (themeClassName != "") 403 { 404 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 405 406 <div class="g-col-12 g-col-lg-4"> 407 <div class="swift-checkered p-3"> 408 <div class="theme-option theme @themeClassName p-lg-3"> 409 <div class="mb-2 pb-1 border-bottom d-flex align-items-center"> 410 <span class="icon-3 me-2"> 411 @ReadFile(iconPath + "sun.svg") 412 </span> 413 <h4 class="m-0 p-0">@themeName</h4> 414 </div> 415 <div class="d-flex flex-column gap-1 mb-2"> 416 <p class="mb-0">This is the body text of the theme.</p> 417 <p class="mb-0" style="color: var(--swift-accent-color)">This is the accent color of the theme.</p> 418 </div> 419 <div> 420 <button class="btn btn-primary me-1">Primary</button> 421 <button class="btn btn-secondary">Secondary</button> 422 <button class="btn btn-link">Link</button> 423 </div> 424 </div> 425 </div> 426 </div> 427 <div class="g-col-12 g-col-lg-8 mb-4 mb-lg-0"> 428 <div class="grid fs-7"> 429 <div class="g-col-12 g-col-lg-3"> 430 <table class="table table-borderless table-sm w-100"> 431 <thead> 432 <tr> 433 <th colspan="2" class="fw-bold">@Translate("Base")</th> 434 </tr> 435 </thead> 436 <tr> 437 <td>@Translate("Foreground")</td> 438 <td>@GetColorSquare(foregroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@foregroundColor');">@foregroundColor</a></td> 439 </tr> 440 <tr> 441 <td>@Translate("Background")</td> 442 <td>@GetColorSquare(backgroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@backgroundColor');">@backgroundColor</a></td> 443 </tr> 444 <tr> 445 <td>@Translate("Accent")</td> 446 <td>@GetColorSquare(accentColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@accentColor');">@accentColor</a></td> 447 </tr> 448 <tr> 449 <td>@Translate("Border")</td> 450 <td>@GetColorSquare(borderColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@borderColor');">@borderColor</a></td> 451 </tr> 452 </table> 453 </div> 454 <div class="g-col-12 g-col-lg-3"> 455 <table class="table table-borderless table-sm w-100"> 456 <thead> 457 <tr> 458 <th colspan="2" class="fw-bold">@Translate("Primary button")</th> 459 </tr> 460 </thead> 461 <tr> 462 <td>@Translate("Foreground")</td> 463 <td>@GetColorSquare(buttonPrimaryForegroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonPrimaryForegroundColor');">@buttonPrimaryForegroundColor</a></td> 464 </tr> 465 <tr> 466 <td>@Translate("Background")</td> 467 <td>@GetColorSquare(buttonPrimaryBackgroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonPrimaryBackgroundColor');">@buttonPrimaryBackgroundColor</a></td> 468 </tr> 469 @if (!string.IsNullOrEmpty(buttonPrimaryBorderColor)) 470 { 471 <tr> 472 <td>@Translate("Border")</td> 473 <td>@GetColorSquare(buttonPrimaryBorderColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonPrimaryBorderColor');">@buttonPrimaryBorderColor</a></td> 474 </tr> 475 } 476 <thead> 477 <tr> 478 <th colspan="2" class="fw-bold">@Translate("Primary button hover")</th> 479 </tr> 480 </thead> 481 @if (!string.IsNullOrEmpty(buttonPrimaryHoverForegroundColor)) 482 { 483 <tr> 484 <td>@Translate("Foreground")</td> 485 <td>@GetColorSquare(buttonPrimaryHoverForegroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonPrimaryHoverForegroundColor');">@buttonPrimaryHoverForegroundColor</a></td> 486 </tr> 487 } 488 @if (!string.IsNullOrEmpty(buttonPrimaryHoverBackgroundColor)) 489 { 490 <tr> 491 <td>@Translate("Background")</td> 492 <td>@GetColorSquare(buttonPrimaryHoverBackgroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonPrimaryHoverBackgroundColor');">@buttonPrimaryHoverBackgroundColor</a></td> 493 </tr> 494 } 495 @if (!string.IsNullOrEmpty(buttonPrimaryHoverBorderColor)) 496 { 497 <tr> 498 <td>@Translate("Border")</td> 499 <td>@GetColorSquare(buttonPrimaryHoverBorderColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonPrimaryHoverBorderColor');">@buttonPrimaryHoverBorderColor</a></td> 500 </tr> 501 } 502 </table> 503 </div> 504 <div class="g-col-12 g-col-lg-3"> 505 <table class="table table-borderless table-sm w-100"> 506 <thead> 507 <tr> 508 <th colspan="2" class="fw-bold">@Translate("Secondary button")</th> 509 </tr> 510 </thead> 511 <tr> 512 <td>@Translate("Foreground")</td> 513 <td>@GetColorSquare(buttonSecondaryForegroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonSecondaryForegroundColor');">@buttonSecondaryForegroundColor</a></td> 514 </tr> 515 <tr> 516 <td>@Translate("Background")</td> 517 <td>@GetColorSquare(buttonSecondaryBackgroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonSecondaryBackgroundColor');">@buttonSecondaryBackgroundColor</a></td> 518 </tr> 519 @if (!string.IsNullOrEmpty(buttonSecondaryBorderColor)) 520 { 521 <tr> 522 <td>@Translate("Border")</td> 523 <td>@GetColorSquare(buttonSecondaryBorderColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonSecondaryBorderColor');">@buttonSecondaryBorderColor</a></td> 524 </tr> 525 } 526 <thead> 527 <tr> 528 <th colspan="2" class="fw-bold">@Translate("Secondary button hover")</th> 529 </tr> 530 </thead> 531 @if (!string.IsNullOrEmpty(buttonSecondaryHoverForegroundColor)) 532 { 533 <tr> 534 <td>@Translate("Foreground")</td> 535 <td>@GetColorSquare(buttonSecondaryHoverForegroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonSecondaryHoverForegroundColor');">@buttonSecondaryHoverForegroundColor</a></td> 536 </tr> 537 } 538 @if (!string.IsNullOrEmpty(buttonSecondaryHoverBackgroundColor)) 539 { 540 <tr> 541 <td>@Translate("Background")</td> 542 <td>@GetColorSquare(buttonSecondaryHoverBackgroundColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonSecondaryHoverBackgroundColor');">@buttonSecondaryHoverBackgroundColor</a></td> 543 </tr> 544 } 545 @if (!string.IsNullOrEmpty(buttonSecondaryHoverBorderColor)) 546 { 547 <tr> 548 <td>@Translate("Border")</td> 549 <td>@GetColorSquare(buttonSecondaryHoverBorderColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonSecondaryHoverBorderColor');">@buttonSecondaryHoverBorderColor</a></td> 550 </tr> 551 } 552 @if (!string.IsNullOrEmpty(accessibilityOutlineColor)) 553 { 554 <tr> 555 <td>@Translate("Focus outline")</td> 556 <td>@GetColorSquare(accessibilityOutlineColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@accessibilityOutlineColor');">@accessibilityOutlineColor</a></td> 557 </tr> 558 } 559 </table> 560 </div> 561 <div class="g-col-12 g-col-lg-3"> 562 <table class="table table-borderless table-sm w-100"> 563 <thead> 564 <tr> 565 <th colspan="2" class="fw-bold">@Translate("Link button")</th> 566 </tr> 567 </thead> 568 <tr> 569 <td>@Translate("Link color")</td> 570 <td>@GetColorSquare(buttonLinkColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonLinkColor');">@buttonLinkColor</a></td> 571 </tr> 572 <thead> 573 <tr> 574 <th colspan="2" class="fw-bold">@Translate("Link button hover")</th> 575 </tr> 576 </thead> 577 @if (!string.IsNullOrEmpty(buttonLinkHoverColor)) 578 { 579 <tr> 580 <td>@Translate("Link hover color")</td> 581 <td>@GetColorSquare(buttonLinkHoverColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@buttonLinkHoverColor');">@buttonLinkHoverColor</a></td> 582 </tr> 583 } 584 585 @if (!string.IsNullOrEmpty(accessibilityOutlineColor)) 586 { 587 <tr> 588 <td>@Translate("Focus outline")</td> 589 <td>@GetColorSquare(accessibilityOutlineColor)<a class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@accessibilityOutlineColor');">@accessibilityOutlineColor</a></td> 590 </tr> 591 } 592 </table> 593 </div> 594 <div class="g-col-12 g-col-lg-3"> 595 <table class="table table-borderless table-sm w-100"> 596 <tr> 597 <td class="fw-bold">@Translate("Implementation")</td> 598 </tr> 599 <tr> 600 <td> 601 @{ 602 string implementation = $"<div class=\"theme {themeClassName}\"></div>"; 603 } 604 <div class="text-muted"> 605 <code class="align-top" role="button" title="@Translate("Click to copy")" onclick="copyTextToClipboard('@themeClassName');"> 606 @Dynamicweb.Core.Encoders.HtmlEncoder.HtmlEncode(implementation) 607 </code> 608 </div> 609 </td> 610 </tr> 611 </table> 612 </div> 613 @{ 614 bool hasUsageData = Converter.ToBoolean(Dynamicweb.Context.Current.Request["auditUsages"]); 615 if (hasUsageData) 616 { 617 var themeUsage = GetThemeUsageById(themeClassName); 618 int allUsages = themeUsage?.AllUsages ?? 0; 619 string buttonAttributes = allUsages > 0 ? $"data-bs-toggle=\"modal\" data-bs-target=\"#themeUsage_{Model.ID}\"" : string.Empty; 620 string badgeStyle = allUsages > 0 ? "bg-success" : "bg-danger"; 621 622 <div class="g-col-12 g-col-lg-9 position-relative"> 623 <button type="button" class="btn btn-primary btn-sm position-absolute end-0" @buttonAttributes> 624 Usages 625 <span class="position-absolute top-0 start-100 translate-middle badge rounded-pill text-white @badgeStyle">@allUsages</span> 626 </button> 627 628 @if (allUsages > 0) 629 { 630 int websiteUsages = themeUsage?.WebsiteUsages.Count ?? 0; 631 int pageUsages = themeUsage?.PageUsages.Count ?? 0; 632 int pagePropertyUsages = themeUsage?.PagePropertyUsages.Count ?? 0; 633 int gridRowUsages = themeUsage?.GridRowUsages.Count ?? 0; 634 int paragraphUsages = themeUsage?.ParagraphUsages.Count ?? 0; 635 636 <!-- Modal --> 637 <div class="modal fade" id="themeUsage_@(Model.ID)" tabindex="-1" aria-labelledby="usagesModelLabel" aria-hidden="true"> 638 <div class="modal-dialog modal-xl modal-dialog-centered modal-dialog-scrollable"> 639 <div class="modal-content"> 640 <div class="modal-header"> 641 <h5 class="modal-title" id="usagesModelLabel">@allUsages Usages of '@(themeName)' (@(themeClassName))</h5> 642 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 643 </div> 644 <div class="modal-body"> 645 <div class="accordion" id="accordionWrapper"> 646 @if(websiteUsages > 0) 647 { 648 <div class="accordion-item"> 649 <h2 class="accordion-header" id="headingWebsites"> 650 <button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseWebsites" aria-expanded="true" aria-controls="collapseWebsites"> 651 <span class="pe-2">Websites</span><span class="badge rounded-pill bg-secondary text-light">@websiteUsages</span> 652 </button> 653 </h2> 654 <div id="collapseWebsites" class="accordion-collapse collapse show" aria-labelledby="headingWebsites" data-bs-parent="#accordionWrapper"> 655 <div class="accordion-body"> 656 <ul> 657 @foreach (var usage in themeUsage.WebsiteUsages) 658 { 659 var website = Dynamicweb.Content.Services.Areas.GetArea(usage); 660 <li><span class="fw-bold">@website.Name</span>@($" (ID: {usage})")</li> 661 } 662 </ul> 663 </div> 664 </div> 665 </div> 666 } 667 @if (pageUsages > 0) 668 { 669 var pages = Dynamicweb.Content.Services.Pages.GetPages(themeUsage.PageUsages.ToArray()); 670 671 <div class="accordion-item"> 672 <h2 class="accordion-header" id="headingPages"> 673 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapsePages" aria-expanded="false" aria-controls="collapsePages"> 674 <span class="pe-2">Pages</span><span class="badge rounded-pill bg-secondary text-light">@pageUsages</span> 675 </button> 676 </h2> 677 <div id="collapsePages" class="accordion-collapse collapse" aria-labelledby="headingPages" data-bs-parent="#accordionWrapper"> 678 <div class="accordion-body"> 679 <ul> 680 @foreach (var usage in themeUsage.PageUsages) 681 { 682 <li><span class="fw-bold">@pages.FirstOrDefault(p => p.ID == usage).GetDisplayName()</span>@($" (ID: {usage})")</li> 683 } 684 </ul> 685 </div> 686 </div> 687 </div> 688 } 689 @if (pagePropertyUsages > 0) 690 { 691 var pages = Dynamicweb.Content.Services.Pages.GetPages(themeUsage.PageUsages.ToArray()); 692 693 <div class="accordion-item"> 694 <h2 class="accordion-header" id="headingPageProperties"> 695 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapsePageProperties" aria-expanded="false" aria-controls="collapsePageProperties"> 696 <span class="pe-2">Page Properties</span><span class="badge rounded-pill bg-secondary text-light">@pagePropertyUsages</span> 697 </button> 698 </h2> 699 <div id="collapsePageProperties" class="accordion-collapse collapse" aria-labelledby="headingPageProperties" data-bs-parent="#accordionWrapper"> 700 <div class="accordion-body"> 701 <ul> 702 @foreach (var usage in themeUsage.PagePropertyUsages) 703 { 704 <li><span class="fw-bold">@pages.FirstOrDefault(p => p.ID == usage).GetDisplayName()</span>@($" (ID: {usage})")</li> 705 } 706 </ul> 707 </div> 708 </div> 709 </div> 710 } 711 @if (gridRowUsages > 0) 712 { 713 var rows = Dynamicweb.Content.Services.Grids.GetGridRows().Where(r => themeUsage.GridRowUsages.Contains(r.ID)).ToList(); 714 715 <div class="accordion-item"> 716 <h2 class="accordion-header" id="headingRows"> 717 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseRows" aria-expanded="false" aria-controls="collapseRows"> 718 <span class="pe-2">Rows</span><span class="badge rounded-pill bg-secondary text-light">@gridRowUsages</span> 719 </button> 720 </h2> 721 <div id="collapseRows" class="accordion-collapse collapse" aria-labelledby="headingRows" data-bs-parent="#accordionWrapper"> 722 <div class="accordion-body"> 723 <ul> 724 @foreach (var usage in themeUsage.GridRowUsages) 725 { 726 var row = rows.FirstOrDefault(r => r.ID == usage); 727 var page = Dynamicweb.Content.Services.Pages.GetPage(row.PageId); 728 <li>Page: <span class="fw-bold">@(page.GetDisplayName())</span>@($"( ID: {page.ID}) - Row: ")<span class="fw-bold">@(row.DefinitionId)</span>@($" (ID: {usage})")</li> 729 } 730 </ul> 731 </div> 732 </div> 733 </div> 734 } 735 @if (paragraphUsages > 0) 736 { 737 var paragraphs = Dynamicweb.Content.Services.Paragraphs.GetParagraphs().Where(p => themeUsage.ParagraphUsages.Contains(p.ID)).ToList(); 738 739 <div class="accordion-item"> 740 <h2 class="accordion-header" id="headingParagraphs"> 741 <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseParagraphs" aria-expanded="false" aria-controls="collapseParagraphs"> 742 <span class="pe-2">Paragraphs</span><span class="badge rounded-pill bg-secondary text-light">@paragraphUsages</span> 743 </button> 744 </h2> 745 <div id="collapseParagraphs" class="accordion-collapse collapse" aria-labelledby="headingParagraphs" data-bs-parent="#accordionWrapper"> 746 <div class="accordion-body"> 747 <ul> 748 @foreach (var usage in themeUsage.ParagraphUsages) 749 { 750 var paragraph = paragraphs.FirstOrDefault(p => p.ID == usage); 751 var page = Dynamicweb.Content.Services.Pages.GetPage(paragraph.PageID); 752 <li>Page: <span class="fw-bold">@(page.GetDisplayName())</span>@($"( ID: {page.ID}) - Paragraph: ")<span class="fw-bold">@paragraph.Header</span>@($" (ID: {usage})")</li> 753 } 754 </ul> 755 </div> 756 </div> 757 </div> 758 } 759 </div> 760 </div> 761 </div> 762 </div> 763 </div> 764 } 765 </div> 766 } 767 } 768 </div> 769 </div> 770 } 771 <div class="g-col-12"> 772 <hr /> 773 </div> 774