v1.3.0 Restructure Saxonite workspace and upgrade to .NET 10 (#1)

Move Saxonite’s projects into a dedicated `Workspace` folder and align the solution with .NET 10 and the current Common workspace. Update encryption integration, test project structure and coverage, and repository guidance and upgrade notes.

- Relocate application and library projects while retaining Git rename history.
- Split tests into Admin, Online, and Shared projects; add representative behavioral and serialization tests.
- Update Common integration, application version, and repository documentation.

Application version: 1.3.0

Reviewed-on: #1
Co-authored-by: andy <andy@sarand.com>
This commit was merged in pull request #1.
This commit is contained in:
2026-10-16 18:24:16 +01:00
committed by andy
parent 2c3fb146ab
commit 9f22b522df
1996 changed files with 1992 additions and 1179 deletions
@@ -0,0 +1,71 @@
using Microsoft.Extensions.Configuration;
using Sarand.Net.Standard.Base.Core;
using Saxonite.Net.Standard.Base.App;
using Saxonite.Net.Core.Web.Base.App;
using System;
using System.Collections.Generic;
using System.Text;
using Sarand.Net.Standard.Base.Utils;
namespace Saxonite.Net.Core.Web.Base.Helpers
{
//=======================================================================
public static class SaxAppConfigHelper
{
//-------------------------------------------------------------------
public static Dictionary<String, SaxAppConfig> ExtractAppConfigs(IConfigurationSection appConfigsSection)
{
// NB The set of configurations will only allow one per context.
// Only the last configuration found for each context will be stored (NB usually only one of each apart from 'Defaults').
Dictionary<String, SaxAppConfig> appConfigs = new Dictionary<String, SaxAppConfig>();
// Start the list with a default 'Defaults' configuration - values set to application defaults (defined in Sax).
// NB This can be overridden in each level of configuration.
// Settings from the 'Defaults' configuration provide values where they are missing from other configurations.
// NB Coverage may not be 100% - just where it's proven needed.
SaxAppConfig defaultsAppConfig = new SaxAppConfig(Sax.Config.Context_Defaults, null, null);
appConfigs.Add(defaultsAppConfig.Context, defaultsAppConfig);
// Load session configurations.
foreach (ConfigurationSection appConfigSection in appConfigsSection.GetChildren())
{
if (StringUtil.IsEmpty(appConfigSection.Key)) continue;
// Extract the configuration, supplying current 'Defaults' to supply missing values, and add to the list.
SaxAppConfig appConfig = new SaxAppConfig(appConfigSection.Key, appConfigSection, defaultsAppConfig);
appConfigs.Remove(appConfigSection.Key);
appConfigs.Add(appConfigSection.Key, appConfig);
// Replace the 'Defaults' configuration if this one matches the context.
if (appConfig.Context.EqualsCI(Sax.Config.Context_Defaults))
defaultsAppConfig = appConfig;
}
return appConfigs;
}
//-------------------------------------------------------------------
}
//=======================================================================
public static class AppConfigList
{
public static SaxAppConfig getConfigForApplication(this List<SaxAppConfig> appConfigs)
{
SaxAppConfig defaultsAppConfig = new SaxAppConfig("", null, null);
SaxApplication_Base app = SaxGlobals.App;
if (app != null)
{
foreach (SaxAppConfig appConfig in appConfigs)
{
if (appConfig.Environments.ContainsCI(app.EnvironmentName))
return appConfig;
if (appConfig.Context.EqualsCI(Sax.Config.Context_Defaults))
defaultsAppConfig = appConfig;
}
}
return defaultsAppConfig;
}
}
//=======================================================================
}
@@ -0,0 +1,94 @@
using Microsoft.Extensions.Configuration;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Email;
using Saxonite.Net.Standard.Base.App;
using Saxonite.Net.Standard.Base.Bus;
using Saxonite.Net.Core.Web.Base.App;
using System;
using System.Collections.Generic;
using System.Text;
using Saxonite.Net.Standard.Base.Consoles;
using Sarand.Net.Standard.Base.Utils;
namespace Saxonite.Net.Core.Web.Base.Helpers
{
//=======================================================================
public static class SaxEmailConfigHelper
{
//-------------------------------------------------------------------
/// <summary>Extract the email configurations from the application settings.</summary>
public static Dictionary<String, SaxEmailConfig> ExtractEmailConfigs(IConfigurationSection emailConfigsSection)
{
Dictionary<String, SaxEmailConfig> emailConfigs = new Dictionary<String, SaxEmailConfig>();
foreach (ConfigurationSection emailConfigSection in emailConfigsSection.GetChildren())
{
if (!StringUtil.IsEmpty(emailConfigSection.Key))
emailConfigs.Add(emailConfigSection.Key, CreateEmailConfig(emailConfigSection.Key, emailConfigSection));
}
return emailConfigs;
}
private static SaxEmailConfig CreateEmailConfig(String context, ConfigurationSection emailConfigSection)
{
SaxEmailConfig emailConfig = new SaxEmailConfig(context);
if (emailConfigSection != null)
{
IConfigurationSection message = emailConfigSection.GetSection("Message");
emailConfig.Message.AddressFrom = message["AddressFrom"];
foreach (var addressBcc in message.GetSection("AddressesBcc").GetChildren())
emailConfig.Message.AddressesBcc.Add(StringUtil.TrimAll(addressBcc.Value));
IConfigurationSection smtp = emailConfigSection.GetSection("SMTP");
emailConfig.SMTP.Server = smtp["Server"];
emailConfig.SMTP.ServerPort = StringUtil.ToInt(smtp["ServerPort"]);
emailConfig.SMTP.UseSSL = StringUtil.ToBool(smtp["UseSSL"]);
IConfigurationSection send = emailConfigSection.GetSection("Send");
emailConfig.Send.Using = send["Using"];
emailConfig.Send.UserName = send["UserName"];
emailConfig.Send.Password = send["Password"];
}
return emailConfig;
}
//-------------------------------------------------------------------
// Send email.
public static bool SendEmail(SaEmailData emailData)
{
// ToDo return EmailConsole.SendEmail(emailData);
throw new NotSupportedException();
}
//-------------------------------------------------------------------
/// <summary>
/// Replace dynamic tags in email message body.
/// <para></para>
/// </summary>
public static void ReplaceDynamicEmailBodyTags(SaEmailData emailData, SaxSessionData sessionData, SaxUserAccount userAccount)
{
// Replace core tags, eg. main stylesheet.
// ToDo. EmailConsole.ReplaceDynamicEmailTags(emailData);
// Replace tags known to the web application.
}
//-------------------------------------------------------------------
}
//=======================================================================
public static class EmailConfigList
{
public static SaxEmailConfig getConfigForSession(this List<SaxEmailConfig> emailConfigs, SaxAppConfig appConfig)
{
if (appConfig != null && !StringUtil.IsEmpty(appConfig.Email.Context))
{
foreach (SaxEmailConfig emailConfig in emailConfigs)
{
if (emailConfig.Context.EqualsCI(appConfig.Email.Context))
return emailConfig;
}
}
return new SaxEmailConfig("");
}
}
//=======================================================================
}
@@ -0,0 +1,56 @@
using Sarand.Net.Standard.Base.Helpers;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Base.Helpers
{
//=======================================================================
public static class SaxResourceHelper_Web
{
//-------------------------------------------------------------------
// Definitions.
//-------------------------------------------------------------------
// Properties.
public static Assembly MyAssembly { get; } = typeof(SaxResourceHelper_Web).GetTypeInfo().Assembly;
//-------------------------------------------------------------------
// Text resources.
/// <summary> Retrieve contents of text resource in this assembly by name.</summary>
public static String GetResourceText(String resourceFilename, String partialPath = "")
{
if (!StringUtil.IsBlank(resourceFilename))
{
String resourceText = SacsResourceHelper.GetManifestResourceAsText(MyAssembly, partialPath, resourceFilename);
if (!StringUtil.IsBlank(resourceText))
return resourceText;
}
return "";
}
//-------------------------------------------------------------------
// Resource stream.
/// <summary>
/// Retrieve stream to resource in this assembly.
/// </summary>
public static Stream GetResourceStream(String resourceFilename, String partialPath = "")
{
if (!StringUtil.IsBlank(resourceFilename))
{
return SacsResourceHelper.GetManifestResourceStream(MyAssembly, partialPath, resourceFilename);
}
return null;
}
//-------------------------------------------------------------------
}
//=======================================================================
}