Add project files.

This commit is contained in:
2021-05-11 19:38:43 +01:00
parent d316c1898d
commit ae64e14da4
1401 changed files with 283421 additions and 0 deletions
@@ -0,0 +1,95 @@
using Microsoft.Extensions.Configuration;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Dbas;
using Sarand.Net.Standard.Base.Email;
using Sarand.Net.Standard.Base.IO;
using Sarand.Net.Standard.Base.Utils;
using Sarand.Net.Standard.Dbas.Data;
using Saxonite.Net.Core.Web.Base.Consoles;
using Saxonite.Net.Standard.Base.App;
using Saxonite.Net.Standard.Base.Consoles;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Saxonite.Net.Core.Web.Base.App
{
//=======================================================================
public abstract class SaxApplication_Web : SaxApplication_Base
{
//-------------------------------------------------------------------
// Data members.
protected Sax.InstanceType instanceType;
protected String environmentName = "";
//-------------------------------------------------------------------
// Protected construction.
protected SaxApplication_Web()
{
WebApp = this;
}
//-------------------------------------------------------------------
// Properties.
public override SaxSystem System { get { return System_Web; } }
public SaxSystem_Web System_Web { get; protected set; }
public static SaxApplication_Web WebApp { get; private set; }
// Application instance.
public override Sax.InstanceType InstanceType { get { return instanceType; } }
public override String EnvironmentName { get { return environmentName; } }
public bool Development { get; protected set; }
public String LoggingRootFolder { get; protected set; } = "";
// Application components.
public abstract String SessionCookieName { get; }
public virtual SaxSessionContext SessionContext { get; protected set; }
public abstract SaxSessionConfig SessionConfig { get; }
public override SaxConsoleHelper ConsoleHelper { get { return ConsoleHelper_Web; } }
public SaxConsoleHelper_Web ConsoleHelper_Web { get; protected set; }
public override IDatabaseInfo CreateCoreDbInfo(SaxSessionContext sessionContext) { return new DbDataItem.DatabaseInfo(GetCoreDatabase(sessionContext)); }
// Configuration.
public IConfiguration Configuration { get; protected set; }
public IConfiguration SaxConfiguration { get; protected set; }
//-------------------------------------------------------------------
// Logging.
protected void LogApplicationStart(String fn)
{
// Log application start.
List<StringPair> applicationParams = new List<StringPair>();
applicationParams.Add("Host", SaxSystem.ComputerName);
applicationParams.Add("Version", Sax.Version.GetFullVersion());
applicationParams.Add("Instance", InstanceType.ToString() + (!StringUtil.IsEmpty(SessionConfig.Instance) ? " [" + SessionConfig.Instance + "]" : ""));
applicationParams.Add("Configuration", SaxSystem.DebugMode ? "Debug" : "Release");
applicationParams.Add("Environment", EnvironmentName);
applicationParams.Add("Database", SessionConfig.CoreDatabase.Location);
applicationParams.Add("Application root", SaxSystem.ApplicationFolder);
if (!StringUtil.IsEmpty(SessionConfig.Documents.RootFolder))
applicationParams.Add("Documents root", SessionConfig.Documents.RootFolder);
applicationParams.Add("Email context", SessionConfig.Email.Context + (!StringUtil.IsEmpty(EmailConfig.SMTP.Server) ? " [" + EmailConfig.SMTP.Server + "]" : ""));
SaLogger.Info(fn + " Application start." + applicationParams.Select(item => StringUtil.PadRight(item.Item1 + ":", 20, ' ') + item.Item2).Concat("", "\n\t"), true);
}
//-------------------------------------------------------------------
protected void InitialiseTemporaryLoggingFolder()
{
// Default logging folder - in case of startup failure.
try
{
foreach (String folder in new String[] { @"D:\Sarand\Projects\User\AHoward\VS2017\Saxonite\Logs" })
{
if (DirectoryUtil.DirectoryExists(folder))
{
SaLogger.LogFolder = folder;
break;
}
}
}
catch (Exception) { }
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,21 @@
using Saxonite.Net.Standard.Base.Consoles;
using System;
using System.Collections.Generic;
using System.Text;
namespace Saxonite.Net.Core.Web.Base.App
{
//=======================================================================
public static class SaxGlobals_Web
{
//-------------------------------------------------------------------
// System.
public static SaxSystem_Web System { get { return SaxGlobals_Web.System as SaxSystem_Web; } }
//-------------------------------------------------------------------
// Application.
//-------------------------------------------------------------------
}
//=======================================================================
}
+186
View File
@@ -0,0 +1,186 @@
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc.Localization;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Core;
using Saxonite.Net.Standard.Base.App;
using Saxonite.Net.Standard.Base.Bus;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Resources;
using System.Web;
namespace Saxonite.Net.Core.Web.Base.App
{
//=======================================================================
public abstract class SaxLocaliser
{
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
/// <summary>
/// Localisers are supplied by derived object.
/// </summary>
protected SaxLocaliser()
{
}
//-------------------------------------------------------------------
// Properties.
public abstract IStringLocalizer MainProperties { get; }
public virtual List<IStringLocalizer> AlternativeProperties { get; } = new List<IStringLocalizer>();
//-------------------------------------------------------------------
// Generic phrase translation.
//-------------------------------------------------------------------
/// <summary>Retrieve localised text.</summary>
public LocalizedString GetText(string key)
{
return GetText(key, new object[] { });
}
public LocalizedString GetText(string key, object param)
{
return GetText(key, new object[] { param });
}
public LocalizedString GetText(string key, object param1, object param2)
{
return GetText(key, new object[] { param1, param2 });
}
public LocalizedString GetText(string key, object[] parameters)
{
LocalizedString text = MainProperties.GetString(key, parameters);
if (text.ResourceNotFound)
{
SaLogger.Debug(GetType().Name + ".GetText Key not found: " + key);
// Look up key in alternative property files.
foreach (IStringLocalizer properties in AlternativeProperties ?? new List<IStringLocalizer>())
{
text = properties.GetString(key, parameters);
if (!text.ResourceNotFound)
break;
}
}
return text;
}
// Square bracket indexing.
public LocalizedString this[String key]
{
get { return GetText(key); }
}
public LocalizedString this[String key, object param]
{
get { return GetText(key, param); }
}
public LocalizedString this[String key, object param1, object param2]
{
get { return GetText(key, param1, param2); }
}
//-------------------------------------------------------------------
/// <summary>
/// Localised HTML text.
/// <para>Line delimiters changed to '<br/>'.</para>
/// </summary>
public HtmlString GetHtmlText(string key, String param1 = "", String param2 = "")
{
return GetHtmlText(key, new Object[] { param1, param2 });
}
/// <summary>
/// Localised HTML text.
/// <para>Line delimiters changed to '<br/>'.</para>
/// </summary>
public HtmlString GetHtmlText(string key, object[] parameters)
{
LocalizedString localisedText = GetText(key, parameters);
String value = localisedText.Value;
// Replace all line delimiters.
value = value.Replace("\r\n", "\n").Replace("\n", "<br/>").Replace("\r", "<br/>");
return new HtmlString(value);
}
//-------------------------------------------------------------------
/// <summary>
/// Localised javascript string.
/// <para>All quotes are escaped.</para>
/// </summary>
public LocalizedString GetJavascriptText(string key, String param1 = "", String param2 = "")
{
LocalizedString localisedText = GetText(key, param1, param2);
return new LocalizedString(localisedText, EscapeJavascriptText(localisedText.Value), localisedText.ResourceNotFound);
}
public String EscapeJavascriptText(string value)
{
// Escape all quotes.
value = value.Replace("'", "\\'").Replace("\"", "\\\"");
return value;
}
//-------------------------------------------------------------------
// Specific phrase translation.
//-------------------------------------------------------------------
// Status.
public String GetStatusCaption(SaStatus status)
{
// ToDo Status description localisation.
return SacsSystem.GetStatusDesc(status);
}
// Region type.
private List<TypeCodeDesc> regionTypes;
public String GetRegionTypeCaption(Sax.RegionType regionType)
{
if ((int)regionType < 1) return "";
if (regionTypes == null)
regionTypes = SaxSystem.GetCodesOfType(Sax.CodeType.RegionType);
// ToDo Region type localisation.
return regionTypes.GetDescForType(regionType);
}
// User role.
private List<TypeCodeDesc> userRoles;
public String GetUserRoleCaption(Sax.UserRole userRole)
{
if ((int)userRole < 1) return "";
if (userRoles == null)
userRoles = SaxSystem.GetCodesOfType(Sax.CodeType.UserRole);
// ToDo User Role localisation.
return userRoles.GetDescForType(userRole);
}
// Organisation role.
private List<TypeCodeDesc> organisationRoles;
public String GetOrganisationRoleCaption(Sax.OrganisationRole organisationRole)
{
if ((int)organisationRole < 1) return "";
if (organisationRoles == null)
organisationRoles = SaxSystem.GetCodesOfType(Sax.CodeType.OrganisationRole);
// ToDo Organisation Role localisation.
return organisationRoles.GetDescForType(organisationRole);
}
//-------------------------------------------------------------------
// Region.
public String GetRegionName(SaxRegion region)
{
if (region == null) return "";
// ToDo Region name localisation.
return region.Name;
}
//-------------------------------------------------------------------
// Sport.
public String GetSportName(SaxSport sport)
{
if (sport == null) return "";
// ToDo Sport name localisation.
return sport.Name;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,200 @@
using Microsoft.Extensions.Configuration;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
using Saxonite.Net.Standard.Base.App;
using System;
using System.Collections.Generic;
using System.Text;
namespace Saxonite.Net.Core.Web.Base.App
{
//=======================================================================
public class SaxSessionConfig
{
//---------------------------------------------------------------
// Data members.
private readonly List<String> environments = new List<string>();
//---------------------------------------------------------------
// Construction.
public SaxSessionConfig (String context, ConfigurationSection configSection, SaxSessionConfig defaultConfig)
{
Context = context;
populateDefaults(defaultConfig);
PopulateConfig(configSection);
}
//---------------------------------------------------------------
private void populateDefaults(SaxSessionConfig defaultConfig)
{
if (defaultConfig == null) return;
Security.Password.MinLength = defaultConfig.Security.Password.MinLength;
Security.SignIn.KeepLoggedIn.Allowed = defaultConfig.Security.SignIn.KeepLoggedIn.Allowed;
Security.SignIn.KeepLoggedIn.CookieExpiresDays = defaultConfig.Security.SignIn.KeepLoggedIn.CookieExpiresDays;
}
private void PopulateConfig(ConfigurationSection configSection)
{
if (configSection != null)
{
// Environments.
foreach (ConfigurationSection environment in configSection.GetSection("Environments").GetChildren())
environments.Add(StringUtil.TrimAll(environment.Value));
Instance = StringUtil.OnEmpty(configSection["Instance"], Instance);
// Security.
IConfigurationSection security = configSection.GetSection("Security");
if (security.Exists())
{
// Security - Password.
IConfigurationSection securityPassword = security.GetSection("Password");
if (securityPassword.Exists())
{
Security.Password.MinLength = StringUtil.ToInt(securityPassword["MinLength"], Security.Password.MinLength);
}
// Security - Sign-Up.
IConfigurationSection securitySignUp = security.GetSection("SignUp");
if (securitySignUp.Exists())
{
}
// Security - Sign-In.
IConfigurationSection securitySignIn = security.GetSection("SignIn");
if (securitySignIn.Exists())
{
IConfigurationSection keepLoggedIn = securitySignIn.GetSection("SignIn");
if (keepLoggedIn.Exists())
{
Security.SignIn.KeepLoggedIn.Allowed = StringUtil.ToBool(StringUtil.OnEmpty(keepLoggedIn["Allowed"], Security.SignIn.KeepLoggedIn.Allowed.ToString()));
Security.SignIn.KeepLoggedIn.CookieExpiresDays = StringUtil.ToInt(keepLoggedIn["CookieExpiresDays"], Security.SignIn.KeepLoggedIn.CookieExpiresDays);
}
}
}
// Email.
IConfigurationSection email = configSection.GetSection("Email");
if (email.Exists())
{
Email.Context = email["Context"];
PopulateEmailConfig(Email.ContactUs, email.GetSection("ContactUs"));
PopulateEmailConfig(Email.Signup_Activate, email.GetSection("Signup").GetSection("Activate"));
PopulateEmailConfig(Email.Signup_Welcome, email.GetSection("Signup").GetSection("Welcome"));
}
// Documents.
IConfigurationSection documents = configSection.GetSection("Documents");
if (documents.Exists())
{
Documents.RootFolder = StringUtil.OnEmpty(documents["RootFolder"], Documents.RootFolder);
}
// Logging.
IConfigurationSection logging = configSection.GetSection("Logging");
if (logging.Exists())
{
Logging.RootFolder = StringUtil.OnEmpty(logging["RootFolder"], Logging.RootFolder);
}
// Database.
extractDatabaseConfig(CoreDatabase, configSection.GetSection("CoreDatabase"));
// Variants.
foreach (ConfigurationSection variantSection in configSection.GetSection("Variants").GetChildren())
{
// Variants override selected values but must match the application root folder to identify a specific instance.
String applicationRootPath = variantSection["ApplicationRootFolder"];
if (StringUtil.IsEmpty(applicationRootPath)) continue;
if (!SaxSystem.ApplicationFolder.EqualsCI(applicationRootPath)) continue;
PopulateConfig(variantSection);
}
}
}
private void PopulateEmailConfig(EmailConfig.SpecificEmailConfig config, IConfigurationSection emailSection)
{
if (!emailSection.Exists()) return;
foreach (var addressTo in emailSection.GetSection("AddressesTo").GetChildren())
config.AddressesTo.Add(StringUtil.TrimAll(addressTo.Value));
foreach (var addressCc in emailSection.GetSection("AddressesCc").GetChildren())
config.AddressesCc.Add(StringUtil.TrimAll(addressCc.Value));
foreach (var addressBcc in emailSection.GetSection("AddressesBcc").GetChildren())
config.AddressesBcc.Add(StringUtil.TrimAll(addressBcc.Value));
}
private void extractDatabaseConfig(DatabaseConfig database, IConfigurationSection databaseSection)
{
if (!databaseSection.Exists()) return;
database.Location = StringUtil.OnEmpty(databaseSection["Location"], database.Location);
database.User = StringUtil.OnEmpty(databaseSection["User"], database.User);
database.Password = StringUtil.OnEmpty(databaseSection["Password"], database.Password);
}
//---------------------------------------------------------------
// Properties.
public String Context { get; }
public List<String> Environments { get { return new List<String>(environments); } }
public String Instance { get; set; } = "";
// Security.
public class SecurityConfig
{
public class PasswordConfig
{
public int MinLength { get; set; }
}
public PasswordConfig Password { get; } = new PasswordConfig();
public class SignUpConfig
{
public class ActivationConfig
{
public int EmailLinkExpiresMins = Sax.Config.Security.Activation.EmailLinkExpiresMins_Default;
}
public ActivationConfig Activation { get; } = new ActivationConfig();
}
public SignUpConfig SignUp { get; } = new SignUpConfig();
public class SignInConfig
{
public class PasswordRequestConfig
{
public int EmailLinkExpiresMins = Sax.Config.Security.Activation.EmailLinkExpiresMins_Default;
}
public PasswordRequestConfig PasswordRequest { get; } = new PasswordRequestConfig();
public class KeepLoggedInConfig
{
public bool Allowed { get; set; } = Sax.Config.Security.SignIn.KeepLoggedIn.Allowed_Default;
public int CookieExpiresDays { get; set; } = Sax.Config.Security.SignIn.KeepLoggedIn.CookieExpiresDays_Default;
}
public KeepLoggedInConfig KeepLoggedIn { get; } = new KeepLoggedInConfig();
}
public SignInConfig SignIn { get; } = new SignInConfig();
}
public SecurityConfig Security { get; } = new SecurityConfig();
// Email.
public class EmailConfig
{
public class SpecificEmailConfig
{
public List<String> AddressesTo { get; } = new List<string>();
public List<String> AddressesCc { get; } = new List<string>();
public List<String> AddressesBcc { get; } = new List<string>();
}
public SpecificEmailConfig ContactUs { get; } = new SpecificEmailConfig();
public SpecificEmailConfig Signup_Activate { get; } = new SpecificEmailConfig();
public SpecificEmailConfig Signup_Welcome { get; } = new SpecificEmailConfig();
public String Context { get; internal set; }
}
public EmailConfig Email { get; } = new EmailConfig();
// Databases.
public class DatabaseConfig
{
public String Location { get; internal set; }
public String User { get; internal set; }
public String Password { get; internal set; }
}
public DatabaseConfig CoreDatabase { get; } = new DatabaseConfig();
// Documents.
public class DocumentsConfig
{
public String RootFolder { get; internal set; }
}
public DocumentsConfig Documents { get; } = new DocumentsConfig();
// Logging.
public class LoggingConfig
{
public String RootFolder { get; internal set; }
}
public LoggingConfig Logging { get; } = new LoggingConfig();
//---------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,23 @@
using Saxonite.Net.Standard.Base.App;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Base.App
{
//=======================================================================
public class SaxSessionContext_Web : SaxSessionContext
{
//-------------------------------------------------------------------
/// <summary>Blank construction - used for deserilisation of session data.</summary>
protected SaxSessionContext_Web () : base() { }
public SaxSessionContext_Web (ushort sesionContextIndex, Sax.Application application) :
base(sesionContextIndex, application)
{
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,22 @@
using Saxonite.Net.Standard.Base.App;
using Saxonite.Net.Standard.Base.Bus;
using System;
using System.Collections.Generic;
using System.Text;
namespace Saxonite.Net.Core.Web.Base.App
{
//=======================================================================
public class SaxSessionData_Web : SaxSessionData
{
//-------------------------------------------------------------------
// Protected construction.
protected SaxSessionData_Web(SaxSessionContext sessionContext, SaxUserAccount userAccount, List<SaxUserRole> userRoles) :
base(sessionContext, userAccount, userRoles)
{
}
//-------------------------------------------------------------------
}
//=======================================================================
}
+199
View File
@@ -0,0 +1,199 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Net.Http.Headers;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Core;
using Saxonite.Net.Core.Web.Base.Middleware;
using Saxonite.Net.Standard.Base.App;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Net.Http;
using System.Text;
namespace Saxonite.Net.Core.Web.Base.App
{
//=======================================================================
public class SaxStartup
{
//-------------------------------------------------------------------
// Data members.
// Localisation.
private static RequestCulture defaultRequestCulture = new RequestCulture("en-GB", "en-GB");
private static List<CultureInfo> supportedCultures = new List<CultureInfo>
{
new CultureInfo("en-GB"),
new CultureInfo("en-US"),
new CultureInfo("en"),
new CultureInfo("fr-FR"),
new CultureInfo("fr"),
};
//-------------------------------------------------------------------
// Protected construction.
protected SaxStartup(IConfiguration configuration)
{
Configuration = configuration;
}
//-------------------------------------------------------------------
// Properties.
public IConfiguration Configuration { get; }
public static RequestCulture DefaultRequestCulture { get { return defaultRequestCulture; } }
public static List<CultureInfo> SupportedCultures { get { return new List<CultureInfo>(supportedCultures); } }
//-------------------------------------------------------------------
// This method gets called by the runtime. Use this method to add services to the container.
protected class ConfigureServicesConfig
{
public IServiceCollection services;
public String sessionCookieName;
public bool sessionCookieHttpOnly;
public TimeSpan sessionIdleTimeout;
}
protected void ConfigureServices(ConfigureServicesConfig config)
{
// Added to support memory caching of sessions.
config.services.AddMemoryCache();
//config.services.AddDistributedMemoryCache(); - for multiple web servers.
// Online web application cookie definition.
config.services.AddSession
(options =>
{
options.Cookie.Name = config.sessionCookieName;
options.Cookie.HttpOnly = config.sessionCookieHttpOnly;
options.IdleTimeout = config.sessionIdleTimeout;
// Override cookie policy for session cookie. Ref. config.app.UseCookiePolicy()
options.Cookie.IsEssential = true;
}
);
// Localisation properties folder.
String resourcesPath = "Localisation";
config.services.AddLocalization(opts => { opts.ResourcesPath = resourcesPath; });
// Modify pages during debug.
#if DEBUG
if (SaxSystem.DebugMode)
// If the following line throws an error such as:
// 'imvcbuilder' does not contain a definition for 'addrazorruntimecompilation'
// Revert the NuGet Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation package
// to a previous version and then reinstal the latest version.
config.services.AddRazorPages().AddRazorRuntimeCompilation();
#endif
config.services.AddMvc
(options =>
{
// Model binding - intercepting form values being bound to models.
options.ModelBinderProviders.Insert(0, new SaxModelBinderProvider());
// Blank form values to be populated to model as empty strings rather than null values.
options.ModelMetadataDetailsProviders.Add(new SaxMetadataProvider());
}
)
// View and data annotation localisations.
.AddViewLocalization(
LanguageViewLocationExpanderFormat.Suffix,
opts => { opts.ResourcesPath = resourcesPath; })
.AddDataAnnotationsLocalization();
// Configure localisation middleware.
config.services.Configure<RequestLocalizationOptions>
(opts =>
{
// Default culture to use for requests.
opts.DefaultRequestCulture = DefaultRequestCulture;
// Cultures supported by the application. Formatting numbers, dates, etc.
opts.SupportedCultures = SupportedCultures;
// UI cultures supported by the application. UI strings that have been localized.
opts.SupportedUICultures = SupportedCultures;
}
);
// Allows access to the HttpContext inside a service.
config.services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
//-------------------------------------------------------------------
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
protected class ConfigureConfig
{
public IApplicationBuilder app;
public IHostApplicationLifetime hostApplicationLifetime;
public IWebHostEnvironment env;
}
protected void Configure(ConfigureConfig config)
{
if (config.env.IsDevelopment())
{
config.app.UseDeveloperExceptionPage();
}
else
{
config.app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
config.app.UseHsts();
}
config.app.UseHttpsRedirection();
config.app.UseStaticFiles();
config.app.UseCookiePolicy();
config.app.UseSession();
config.app.UseRouting();
//config.app.UseAuthorization();
config.app.UseAntiforgeryTokens();
config.app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
// Application events.
// NB The shutdown events only fire if he application stops 'gracefully'.
config.hostApplicationLifetime.ApplicationStarted.Register(OnApplicationStarted);
config.hostApplicationLifetime.ApplicationStopping.Register(OnApplicationStopping);
config.hostApplicationLifetime.ApplicationStopped.Register(OnApplicationStopped);
config.app.Use(async (context, next) =>
{
if (context.Request.Method.Equals(HttpMethod.Get.Method))
{
context.Response.GetTypedHeaders().CacheControl = new CacheControlHeaderValue()
{
Public = true,
MaxAge = TimeSpan.FromSeconds(10)
};
//context.Response.Headers[HeaderNames.Vary] = new string[] { "Accept-Encoding" };
}
await next();
});
}
//-------------------------------------------------------------------
// Application events.
private readonly static String ApplicationEventLogPrefix = "### ";
private void OnApplicationStarted()
{
SaLogger.Info(ApplicationEventLogPrefix + "Application started");
}
private void OnApplicationStopping()
{
SaLogger.Info(ApplicationEventLogPrefix + "Application stopping");
}
private void OnApplicationStopped()
{
SaLogger.Info(ApplicationEventLogPrefix + "Application stopped");
}
//-------------------------------------------------------------------
}
//=======================================================================
}
+135
View File
@@ -0,0 +1,135 @@
using Sarand.Net.Standard.Base.Core;
using Saxonite.Net.Standard.Dbas.App;
using Saxonite.Net.Standard.Base.App;
using Saxonite.Net.Standard.Base.Mgr;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Utils;
namespace Saxonite.Net.Core.Web.Base.App
{
//=======================================================================
public class SaxSystem_Web : SaxSystem_Dbas
{
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
/// <summary>Static initialisation.</summary>
static SaxSystem_Web()
{
InitComputerName();
InitUserName();
InitApplicationPath();
InitApplicationFolder();
InitAssemblyBuildAt();
}
//-------------------------------------------------------------------
// Construction.
public SaxSystem_Web() : base()
{
}
//-------------------------------------------------------------------
/// <summary>
/// Computer name
/// <para>Windows: System.Environment.MachineName</para>
/// <para>Web: HttpContext.Current.Server.MachineName</para>
/// <para>FQDN: System.Net.Dns.GetHostName()</para>
/// </summary>
private static void InitComputerName()
{
try
{
ComputerName = System.Environment.MachineName;
}
catch (Exception exc)
{
SaLogger.Warn("SaxSystem.InitComputerName Failed to initialise computer name from environment. " + ExceptionUtil.GetErrorStateMessage(exc));
}
try
{
if (StringUtil.IsEmpty(ComputerName))
ComputerName = Environment.GetEnvironmentVariable("CUMPUTERNAME") ?? Environment.GetEnvironmentVariable("HOSTNAME");
}
catch (Exception exc)
{
SaLogger.Warn("SaxSystem.InitComputerName Failed to initialise computer name from environment variable. " + ExceptionUtil.GetErrorStateMessage(exc));
}
}
//-------------------------------------------------------------------
/// <summary>To be populated by application<para>Windows: Environment.UserName</para></summary>
private static void InitUserName()
{
try
{
UserName = Environment.UserName;
}
catch (Exception exc)
{
SaLogger.Warn("SaxSystem.InitUserName Failed to initialise user name. " + ExceptionUtil.GetErrorStateMessage(exc));
}
}
//-------------------------------------------------------------------
/// <summary>
/// Application Path.
/// <para>Windows: System.Reflection.Assembly.GetEntryAssembly().Location;</para>
/// </summary>
private static void InitApplicationPath()
{
try
{
ApplicationPath = Assembly.GetEntryAssembly().Location;
}
catch (Exception exc)
{
SaLogger.Warn("SaxSystem.InitApplicationPath Failed to initialise application path. " + ExceptionUtil.GetErrorStateMessage(exc));
}
}
//-------------------------------------------------------------------
/// <summary>
/// Application folder.
/// <para>Windows: System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location);</para>
/// </summary>
private static void InitApplicationFolder()
{
try
{
ApplicationFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
}
catch (Exception exc)
{
SaLogger.Warn("SaxSystem.InitApplicationFolder Failed to initialise application folder. " + ExceptionUtil.GetErrorStateMessage(exc));
}
}
//-------------------------------------------------------------------
/// <summary>
/// DateTime when the executing assembly was built.
/// </summary>
private static void InitAssemblyBuildAt()
{
try
{
// Assembly build date.
Assembly assembly = Assembly.GetExecutingAssembly();
AssemblyBuildAt = File.GetLastWriteTime(assembly.Location);
}
catch (Exception exc)
{
SaLogger.Warn("SaxSystem.InitAssemblyBuildAt Failed to initialise assembly build date. " + ExceptionUtil.GetErrorStateMessage(exc));
}
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,20 @@
using Saxonite.Net.Standard.Base.Consoles;
using System;
using System.Collections.Generic;
using System.Text;
namespace Saxonite.Net.Core.Web.Base.Consoles
{
//=======================================================================
public abstract class SaxConsoleHelper_Web : SaxConsoleHelper
{
//-------------------------------------------------------------------
// Protected construction.
protected SaxConsoleHelper_Web()
{
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,184 @@
using Microsoft.IdentityModel.Tokens;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Utils;
using Saxonite.Net.Standard.Base.App;
using Saxonite.Net.Standard.Base.Bus;
using Saxonite.Net.Standard.Base.Consoles;
using System;
using System.Collections.Generic;
using System.Text;
namespace Saxonite.Net.Core.Web.Base.Consoles
{
//=======================================================================
public class SaxDataCacheConsole_Web : SaxDataCacheConsole
{
//-------------------------------------------------------------------
// Data members.
private readonly SaxInternals internals;
//-------------------------------------------------------------------
// Protected construction.
protected SaxDataCacheConsole_Web(SaxSessionContext sessionContext) :
base(sessionContext)
{
internals = new SaxInternals(sessionContext);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
internals.Dispose();
}
base.Dispose(disposing);
}
//-------------------------------------------------------------------
// Regions.
public bool RegionsSupported { get; protected set; } = false;
private class RegionsData
{
public Dictionary<SysId, SaxRegion> regions = null;
public List<SaxRegion> activeRegions = null;
}
private readonly RegionsData regionsData = new RegionsData();
public override List<SaxRegion> GetActiveRegions()
{
String fn = GetType().Name + ".ActiveRegions";
if (!RegionsSupported) throw new NotSupportedException(fn + " Regions not supported.");
List<SaxRegion> activeRegions = new List<SaxRegion>();
lock (regionsData)
{
if (regionsData.regions == null) throw new NotSupportedException(fn + " Regions not populated.");
activeRegions = new List<SaxRegion>(regionsData.activeRegions);
}
return activeRegions;
}
private bool IsRegionActive(SaxRegion region)
{
if (region == null) return false;
if (!region.IsStatusActive) return false;
if (SysId.IsValid(region.RegionIdParent))
return IsRegionActive(regionsData.regions.GetValue(region.RegionIdParent, null));
return true;
}
protected void RefreshRegions()
{
String fn = GetType().Name + ".RefreshRegions";
if (!RegionsSupported) throw new NotSupportedException(fn + " Regions not supported.");
SaLogger.Info(fn + " START.", false);
DateTime startedAt = DateTime.Now;
List<SaxRegion> oldRegions = null;
Dictionary<SysId, SaxRegion> newRegions = new Dictionary<SysId, SaxRegion>();
// Query Regions.
SaxRegion.Params prmRegion = new SaxRegion.Params();
prmRegion.StatusCat = SaxRegion.StatusCat.NotDeleted;
foreach (SaxRegion region in internals.ReferenceMgr.GetRegionList(prmRegion))
newRegions[region.Id] = region;
// Replace the list of Regions.
lock (regionsData)
{
oldRegions = regionsData.regions?.GetValues() ?? new List<SaxRegion>();
regionsData.regions = newRegions;
// Populate list of active regions.
regionsData.activeRegions = new List<SaxRegion>();
foreach (SaxRegion region in regionsData.regions.GetValues())
{
if (IsRegionActive(region))
{
// Clone region to ensure internal list isn't modified.
SaxRegion activeRegion = internals.ReferenceMgr.CreateRegion();
activeRegion.SetData(region.GetData());
regionsData.activeRegions.Add(activeRegion);
}
}
SaLogger.Info(fn + " END. Regions found: " + regionsData.regions.Count + ". Elapsed: " + DateUtil.FormatNumberOfSeconds((DateTime.Now - startedAt).TotalSeconds), true);
}
// Dispose old regions.
oldRegions.ClearAndDispose<SaxRegion>();
oldRegions = null;
}
//-------------------------------------------------------------------
// Sports.
public bool SportsSupported { get; protected set; } = false;
private class SportsData
{
public Dictionary<SysId, SaxSport> sports = null;
public List<SaxSport> activeSports = null;
}
private readonly SportsData sportsData = new SportsData();
public override List<SaxSport> GetActiveSports()
{
String fn = GetType().Name + ".ActiveSports";
if (!SportsSupported) throw new NotSupportedException(fn + " Sports not supported.");
List<SaxSport> activeSports = new List<SaxSport>();
lock (sportsData)
{
if (sportsData.sports == null) throw new NotSupportedException(fn + " Sports not populated.");
foreach (SaxSport sport in sportsData.sports.GetValues())
{
if (IsSportActive(sport))
{
// Clone sport to ensure internal list isn't modified.
SaxSport activeSport = internals.ReferenceMgr.CreateSport();
activeSport.SetData(sport.GetData());
activeSports.Add(activeSport);
}
}
}
return activeSports;
}
private bool IsSportActive(SaxSport sport)
{
if (sport == null) return false;
if (!sport.IsStatusActive) return false;
return true;
}
protected void RefreshSports()
{
String fn = GetType().Name + ".RefreshSports";
if (!SportsSupported) throw new NotSupportedException(fn + " Sports not supported.");
SaLogger.Info(fn + " START.", false);
DateTime startedAt = DateTime.Now;
List<SaxSport> oldSports = null;
Dictionary<SysId, SaxSport> newSports = new Dictionary<SysId, SaxSport>();
// Query Sports.
SaxSport.Params prmSport = new SaxSport.Params();
prmSport.StatusCat = SaxSport.StatusCat.NotDeleted;
foreach (SaxSport sport in internals.ReferenceMgr.GetSportList(prmSport))
newSports[sport.Id] = sport;
// Replace the list of Sports.
lock (sportsData)
{
oldSports = sportsData.sports?.GetValues() ?? new List<SaxSport>();
sportsData.sports = newSports;
// Populate list of active sports.
sportsData.activeSports = new List<SaxSport>();
foreach (SaxSport sport in sportsData.sports.GetValues())
{
if (IsSportActive(sport))
sportsData.activeSports.Add(sport);
}
SaLogger.Info(fn + " END. Sports found: " + sportsData.sports.Count + ". Elapsed: " + DateUtil.FormatNumberOfSeconds((DateTime.Now - startedAt).TotalSeconds), true);
}
// Dispose old sports.
oldSports.ClearAndDispose<SaxSport>();
oldSports = null;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,21 @@
using Saxonite.Net.Standard.Base.Consoles;
using System;
using System.Collections.Generic;
using System.Text;
namespace Saxonite.Net.Core.Web.Base.Consoles
{
//=======================================================================
public class SaxEmailConsole_Web : SaxEmailConsole
{
//-------------------------------------------------------------------
// Protected construction.
public SaxEmailConsole_Web(SaxEmailConfig emailConfig) :
base(emailConfig)
{
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,497 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Routing;
using Microsoft.Net.Http.Headers;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Dbas;
using Sarand.Net.Standard.Base.Email;
using Sarand.Net.Standard.Base.Helpers;
using Sarand.Net.Standard.Base.Utils;
using Saxonite.Net.Core.Web.Base.App;
using Saxonite.Net.Core.Web.Base.Consoles;
using Saxonite.Net.Core.Web.Base.Models;
using Saxonite.Net.Standard.Base.App;
using Saxonite.Net.Standard.Base.Bus;
using Saxonite.Net.Standard.Base.Consoles;
using Saxonite.Net.Standard.Base.Mgr;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
namespace Saxonite.Net.Core.Web.Base.Controllers
{
//=======================================================================
public abstract class SaxController_Base : Controller
{
//-------------------------------------------------------------------
// Data members.
private SaxInternals internals;
//-------------------------------------------------------------------
// Protected construction.
protected SaxController_Base(SaxLocaliser localiser)
{
Localiser = localiser;
// Initialise console (debug) logging listener.
//if (Directory.Exists(App.LoggingRootFolder) && SaxSystem.DebugMode)
// SaxConsoleListener.ConfigureDebugListener(App.LoggingRootFolder);
if (DebugMode) SaLogger.Trace(GetType().Name + ".CTR");
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
}
base.Dispose(disposing);
}
//-------------------------------------------------------------------
// Properties.
public bool DebugMode { get; } = SacsSystem.DebugMode;
protected SaxApplication_Web App { get { return SaxApplication_Web.WebApp; } }
public SaxSessionConfig SessionConfig { get { return App.SessionConfig; } }
public SaxSessionContext SessionContext { get { return App.SessionContext; } }
protected SaxLocaliser Localiser { get; }
/// <summary>
/// Indicate whether session data should be stored by action.
/// <para>Any action that might modify the session data must store it.</para>
/// <para>Some actions such as scoring a password's complexity don't need to.</para>
/// </summary>
public virtual bool StoreSessionData { get; protected set; } = true;
// Culture.
private string LanguageIso2
{
get
{
var feature = HttpContext.Features.Get<IRequestCultureFeature>();
return feature.RequestCulture.UICulture.TwoLetterISOLanguageName.ToLower();
}
}
// Business components.
public SaxInternals Internals { get { if (internals == null) internals = new SaxInternals(SessionContext); return internals; } }
// Consoles.
public SaxDataCacheConsole DataCacheConsole { get { return Internals.DataCacheConsole; } }
// Managers.
public SaxReferenceMgr ReferenceMgr { get { return Internals.ReferenceMgr; } }
public SaxAccountMgr AccountMgr { get { return Internals.AccountMgr; } }
public SaxActivityMgr ActivityMgr { get { return Internals.ActivityMgr; } }
public SaxItemDataMgr ItemDataMgr { get { return Internals.ItemDataMgr; } }
// System codes.
public List<TypeCodeDesc> RegionTypes { get { return Internals.RegionTypes; } }
public List<TypeCodeDesc> UserRoles { get { return Internals.UserRoles; } }
public List<TypeCodeDesc> OrganisationTypes { get { return Internals.OrganisationTypes; } }
public List<TypeCodeDesc> OrganisationCategories { get { return Internals.OrganisationCategories; } }
public List<TypeCodeDesc> OrganisationRoles { get { return Internals.OrganisationRoles; } }
// Email.
public SaEmailConfig EmailConfig { get { return App.EmailConfig; } }
public SaxEmailConsole EmailConsole { get { return Internals.EmailConsole; } }
//-------------------------------------------------------------------
// Redirection locations.
//-------------------------------------------------------------------
public static readonly String ParamName_SessionExpired = "SessionExpired";
protected enum RedirectLocation
{
WebAppBase,
WebAppHome,
WebAppHome_SessionExpired,
}
protected String GetRedirectLocation(RedirectLocation redirectLocation)
{
string protocol = Request.Scheme;
var parameters = new RouteValueDictionary();
String homeAction = Url.Action("Index", "Home", parameters, protocol);
switch (redirectLocation)
{
case RedirectLocation.WebAppBase:
return Regex.Replace(homeAction, "\\/$", "") + "/";
case RedirectLocation.WebAppHome:
return homeAction;
case RedirectLocation.WebAppHome_SessionExpired:
// Set the session expired parameter so that the page can report it.
// NB Paramater must have a value to be included in the URL query string.
parameters.Add(ParamName_SessionExpired, "1");
return homeAction;
}
return "";
}
//-------------------------------------------------------------------
// Action execution events.
//-------------------------------------------------------------------
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (DebugMode) SaLogger.Debug(GetType().Name + ".OnActionExecuting" + (ViewData.Model != null ? " Model: " + ViewData.Model : ""));
// Refreh model properties.
SaxModel_Base baseModel = ViewData.Model as SaxModel_Base;
RefreshModel(baseModel);
base.OnActionExecuting(filterContext);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (DebugMode) SaLogger.Debug(GetType().Name + ".OnActionExecuted" + (ViewData.Model != null ? " Model: " + ViewData.Model : ""));
// Refreh model properties.
SaxModel_Base baseModel = ViewData.Model as SaxModel_Base;
RefreshModel(baseModel);
// Store session data if present - as potentially modfied.
if (SessionData != null)
{
// Most actions will store the session data in case it's been modified.
// Some actions (eg. score password) don't need to.
if (StoreSessionData)
{
String sessionDataJson = JsonConvert.SerializeObject(SessionData);
HttpContext.Session.Set(SessionStorage.SessionKey_SessionConfig, SessionStorage.sessionDataEncoding.GetBytes(sessionDataJson));
}
}
else HttpContext.Session.Remove(SessionStorage.SessionKey_SessionConfig);
// Response headers.
if (baseModel != null && baseModel.ProhibitCache)
{
if (Response.Headers.ContainsKey(HeaderNames.CacheControl))
Response.Headers.Remove(HeaderNames.CacheControl);
Response.Headers.Add(HeaderNames.CacheControl, "no-cache, no-store, must-revalidate");
Response.Headers.Add(HeaderNames.Pragma, "no-cache");
Response.Headers.Add(HeaderNames.Expires, "0");
}
base.OnActionExecuted(filterContext);
}
//-------------------------------------------------------------------
// Model.
//-------------------------------------------------------------------
protected void RefreshModel(SaxModel_Base model)
{
if (model == null) return;
// Session data.
model.SessionData = SessionData;
// Redirect locations.
model.RedirectLocation.Base = GetRedirectLocation(RedirectLocation.WebAppBase);
model.RedirectLocation.Home = GetRedirectLocation(RedirectLocation.WebAppHome);
model.RedirectLocation.Home_SessionExpired = GetRedirectLocation(RedirectLocation.WebAppHome_SessionExpired);
}
//-------------------------------------------------------------------
// Parameters.
//-------------------------------------------------------------------
public String EncryptParameters(NameValueCollection parameters, String base64IV, String base64Key)
{
String fn = nameof(ControllerBase) + ".EncryptParameters";
try
{
// Copy the list of parameters, adding a random name/value pair to randomise the encryption.
NameValueCollection parametersToEncrypt = new NameValueCollection(parameters);
// NB Range 10-189 intended to have equal chance of 2 or 3 digit number.
parametersToEncrypt.Add("!" + SacsRandomHelper.GetInt32(10, 190).ToString(), SacsRandomHelper.GetInt32(10, 190).ToString());
// Sort the keys to ensure that the random '!' param is first in the query string.
String[] paramKeys = parametersToEncrypt.AllKeys;
Array.Sort(paramKeys);
// Serialise parameters as if in a URL query string - so that they can be deserialised in DecryptParameters() below.
string queryString = String.Join("&", paramKeys.Select(a => a + "=" + HttpUtility.UrlEncode(parametersToEncrypt[a])));
String encrypted = SacsEncryptionHelper.Encrypt(queryString, base64IV, base64Key);
if (DebugMode)
{
SaLogger.Debug(fn + " Encrypted: " + encrypted);
NameValueCollection decryptedParams = DecryptParameters(encrypted, base64IV, base64Key);
}
return encrypted;
}
catch (Exception exc) { SaLogger.Warn(fn + " " + ExceptionUtil.GetErrorStateMessage(exc)); }
return "";
}
public NameValueCollection DecryptParameters(String encrypted, String base64IV, String base64Key)
{
String fn = nameof(ControllerBase) + ".DecryptParameters";
if (DebugMode) SaLogger.Debug(fn + " \n\tEncrypted: " + encrypted + "\n\tIV: " + base64IV + "\n\tKey: " + base64Key);
try
{
String queryString = SacsEncryptionHelper.Decrypt(encrypted, base64IV, base64Key);
return HttpUtility.ParseQueryString(queryString);
}
catch (Exception exc) { SaLogger.Warn(fn + " " + ExceptionUtil.GetErrorStateMessage(exc)); }
return new NameValueCollection();
}
//-------------------------------------------------------------------
// Cookies.
//-------------------------------------------------------------------
/// <summary>Retrieve the value from the named request cookie.</summary>
public String GetRequestCookie(String key)
{
if (Request.Cookies.ContainsKey(key))
return Request.Cookies[key];
return null;
}
/// <summary>Set the value against the named response cookie.</summary>
public void SetResponseCookie(string key, string value, DateTime? expiresAt = null, bool isEssential = false)
{
CookieOptions options = new CookieOptions();
options.HttpOnly = true;
options.Secure = true;
options.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Strict;
if (expiresAt.HasValue)
options.Expires = expiresAt;
if (isEssential)
options.IsEssential = true;
Response.Cookies.Append(key, value, options);
}
//-------------------------------------------------------------------
// Session.
//-------------------------------------------------------------------
public bool ValidUserSession { get { return SessionData?.ValidUserSession ?? false; } }
public DbUpdateInfo DbUpdateInfo { get { return SessionData?.DbUpdateInfo ?? new DbUpdateInfo(null, "", SaxSystem.ComputerName); } }
// Referrer.
public String Referer { get { return Request.Headers["Referer"].ToString(); } }
public bool ValidReferer { get { return !StringUtil.IsEmpty(Referer); } }
/// <summary>Store session data, eg. to start a new user session on sign-in.</summary>
protected bool SetSessionData(SaxSessionData sessionData)
{
if (sessionStorage.SessionData != null) return false;
sessionStorage.SessionData = sessionData;
return true;
}
protected void ClearSessionData()
{
if (HttpContext.Session.IsAvailable)
HttpContext.Session.Clear();
sessionStorage.SessionData = null;
}
//-------------------------------------------------------------------
// Session storage.
public class SessionStorage
{
public SaxSessionData SessionData { get; set; }
public static String SessionKey_SessionConfig { get; } = "SaxSessionData";
public static Encoding sessionDataEncoding { get; } = Encoding.UTF8;
//JSON Serialisation.
public class SaxSessionDataContractResolver : DefaultContractResolver
{
// To allow private properties to be deserialised.
// NB Only applies to top level properties in the serialised object.
protected override List<MemberInfo> GetSerializableMembers(Type objectType)
{
var flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
MemberInfo[] fields = objectType.GetFields(flags);
return fields
.Concat(objectType.GetProperties(flags).Where(propInfo => propInfo.CanWrite))
.ToList();
}
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
return base.CreateProperties(type, MemberSerialization.Fields);
}
};
public JsonSerializerSettings SaxSessionDataJsonSerializerSettings = new JsonSerializerSettings
{
ContractResolver = new SaxSessionDataContractResolver()
};
}
private readonly SessionStorage sessionStorage = new SessionStorage();
protected JsonSerializerSettings SaxSessionDataJsonSerializerSettings { get { return sessionStorage.SaxSessionDataJsonSerializerSettings; } }
//-------------------------------------------------------------------
public SaxSessionData SessionData
{
get
{
try
{
// Retrieve session data if not already in controller.
if (sessionStorage.SessionData == null)
{
byte[] sessionDataBytes = null;
HttpContext.Session.TryGetValue(SessionStorage.SessionKey_SessionConfig, out sessionDataBytes);
if (sessionDataBytes != null)
{
String sessionDataJson = SessionStorage.sessionDataEncoding.GetString(sessionDataBytes);
sessionStorage.SessionData = DeserializeSessionData(sessionDataJson);
}
}
return sessionStorage.SessionData;
}
catch (Exception exc)
{
SaLogger.Warn(GetType().Name + ".SessionData " + exc.GetType().Name + " - " + exc.Message + exc.GetStackEntries().Concat("", "\n\t"));
}
return null;
}
}
protected abstract SaxSessionData DeserializeSessionData(String sessionDataJson);
//-------------------------------------------------------------------
// Ajax actions.
//-------------------------------------------------------------------
protected static readonly Encoding ajaxResponseEncoding = Encoding.UTF8;
protected IActionResult GetAjaxResult_GoHome(AjaxResponse_Base responseData)
{
responseData.SetGoHome();
return Json(responseData);
}
protected IActionResult GetAjaxResult_InvalidSession(AjaxResponse_Base responseData)
{
responseData.SetGoHome();
return Json(responseData);
}
//-------------------------------------------------------------------
public class AjaxResponse_Base
{
// Response status.
public bool Success { get; set; } = false;
public bool Error { get; set; } = false;
public bool SessionExpired { get; protected set; } = false;
public bool GoHome { get; protected set; } = false;
public AjaxResponse_Base SetSessionExpired(String title, String text)
{
SessionExpired = true;
return SetMessage(title, text);
}
public AjaxResponse_Base SetGoHome()
{
GoHome = true;
return this;
}
public AjaxResponse_Base SetGoHome(String title, String text)
{
GoHome = true;
return SetMessage(title, text);
}
// Message - displays alert popup.
public class MessageData
{
public String Title { get; set; }
public String Text { get; set; }
}
public MessageData Message { get; private set; }
public AjaxResponse_Base SetMessage(String title, String text)
{
if (!StringUtil.IsEmpty(text))
Message = new MessageData() { Title = title, Text = text };
return this;
}
}
//-------------------------------------------------------------------
public class AjaxResponse_Data : AjaxResponse_Base
{
// Form data.
public class FormControlValue
{
public FormControlValue(String controlName, String value, String error)
{
ControlName = controlName;
Value = value;
Error = error;
}
public String ControlName { get; }
public String Value { get; set; }
public String Error { get; set; }
}
private readonly Dictionary<String, FormControlValue> controlValues = new Dictionary<String, FormControlValue>();
public List<FormControlValue> ControlValues { get { return controlValues.Values.ToList(); } }
public void SetControlValue(String controlName, String value, String error = null)
{
if (controlValues.ContainsKey(controlName))
{
// Modify existing entry in the list.
FormControlValue controlValue = controlValues[controlName];
if (value != null) controlValue.Value = value;
if (error != null) controlValue.Error = error;
}
else
{
// Add new control to list.
controlValues.Add(controlName, new FormControlValue(controlName, value, error));
}
}
/// <summary>Assign the error to the control by name and return this response data.</summary>
public AjaxResponse_Data SetControlError(String controlName, String error)
{
return SetControlError(controlName, error, this);
}
/// <summary>
/// Assign the error to the control by name and return the specified value.
/// <para>- Allows alternative return values to be specified (eg. 'false') to avoid separate return statement in calling routine.</para>
/// </summary>
public T SetControlError<T>(String controlName, String error, T returnValue)
{
SetControlValue(controlName, null, error);
return returnValue;
}
public bool HasControlErrors()
{
foreach (FormControlValue controlValue in controlValues.Values)
{
if (!StringUtil.IsEmpty(controlValue.Error)) return true;
}
return false;
}
// Notification - briefly displays after response received.
public class NotificationData
{
public String Title { get; set; }
public String Message { get; set; }
}
public NotificationData Notification { get; private set; }
public bool HasNotification { get { return Notification != null; } }
public AjaxResponse_Data SetNotification(String title, String message)
{
Notification = new NotificationData() { Title = title, Message = message };
return this;
}
// Paged data.
public class PagingData
{
public PagingData(SaxModel_Base model) :
this(model.DataPageNo, model.DataPageLength, model.DataItemCount, model.DataItemTotal)
{ }
public PagingData(uint pageNo, ushort pageLength, ushort itemCount, ulong itemTotal)
{
PageNo = pageNo;
PageLength = pageLength;
ItemCount = itemCount;
ItemTotal = itemTotal;
}
public bool Paged { get { return (PageNo > 0 && PageLength > 0 && ItemTotal > PageLength); } }
public uint PageNo { get; } = 0;
public ushort PageLength { get; } = 0;
public uint PageCount { get { return (PageLength > 0 ? (uint)Math.Ceiling((decimal)ItemTotal / PageLength) : 0); } }
public ushort ItemCount { get; } = 0;
public ulong ItemTotal { get; } = 0;
}
public PagingData Paging { get; private set; }
public void SetPaging(SaxModel_Base model)
{
Paging = new PagingData(model);
}
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -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, SaxSessionConfig sessionConfig)
{
if (sessionConfig != null && !StringUtil.IsEmpty(sessionConfig.Email.Context))
{
foreach (SaxEmailConfig emailConfig in emailConfigs)
{
if (emailConfig.Context.EqualsCI(sessionConfig.Email.Context))
return emailConfig;
}
}
return new SaxEmailConfig("");
}
}
//=======================================================================
}
@@ -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 SaxSessionConfigHelper
{
//-------------------------------------------------------------------
public static Dictionary<String, SaxSessionConfig> ExtractSessionConfigs(IConfigurationSection sessionConfigsSection)
{
// 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, SaxSessionConfig> sessionConfigs = new Dictionary<String, SaxSessionConfig>();
// 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.
SaxSessionConfig defaultsSessionConfig = new SaxSessionConfig(Sax.Config.Context_Defaults, null, null);
sessionConfigs.Add(defaultsSessionConfig.Context, defaultsSessionConfig);
// Load session configurations.
foreach (ConfigurationSection sessionConfigSection in sessionConfigsSection.GetChildren())
{
if (StringUtil.IsEmpty(sessionConfigSection.Key)) continue;
// Extract the configuration, supplying current 'Defaults' to supply missing values, and add to the list.
SaxSessionConfig sessionConfig = new SaxSessionConfig(sessionConfigSection.Key, sessionConfigSection, defaultsSessionConfig);
sessionConfigs.Remove(sessionConfigSection.Key);
sessionConfigs.Add(sessionConfigSection.Key, sessionConfig);
// Replace the 'Defaults' configuration if this one matches the context.
if (sessionConfig.Context.EqualsCI(Sax.Config.Context_Defaults))
defaultsSessionConfig = sessionConfig;
}
return sessionConfigs;
}
//-------------------------------------------------------------------
}
//=======================================================================
public static class SessionConfigList
{
public static SaxSessionConfig getConfigForApplication(this List<SaxSessionConfig> sessionConfigs)
{
SaxSessionConfig defaultsSessionConfig = new SaxSessionConfig("", null, null);
SaxApplication_Base app = SaxGlobals.App;
if (app != null)
{
foreach (SaxSessionConfig sessionConfig in sessionConfigs)
{
if (sessionConfig.Environments.ContainsCI(app.EnvironmentName))
return sessionConfig;
if (sessionConfig.Context.EqualsCI(Sax.Config.Context_Defaults))
defaultsSessionConfig = sessionConfig;
}
}
return defaultsSessionConfig;
}
}
//=======================================================================
}
@@ -0,0 +1,23 @@
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
using System;
using System.Collections.Generic;
using System.Text;
namespace Saxonite.Net.Core.Web.Base.Middleware
{
//=======================================================================
/// <summary>
/// Force blank form values to be populated to the model as empty strings rather than null values.
/// </summary>
public class SaxMetadataProvider : IMetadataDetailsProvider, IDisplayMetadataProvider
{
public void CreateDisplayMetadata(DisplayMetadataProviderContext context)
{
if (context.Key.MetadataKind == ModelMetadataKind.Property)
{
context.DisplayMetadata.ConvertEmptyStringToNull = false;
}
}
}
//=======================================================================
}
@@ -0,0 +1,102 @@
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
using Saxonite.Net.Core.Web.Base.App;
using Saxonite.Net.Standard.Base.App;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace Saxonite.Net.Core.Web.Base.Middleware
{
//=======================================================================
/// <summary>
/// Maps a browser request to a data object. Default class provides a base implementation of a model binder.
/// <para>Overridden to trim whitespace from input values.</para>
/// </summary>
public class SaxModelBinder : ComplexTypeModelBinder
{
//-------------------------------------------------------------------
// Construction.
public SaxModelBinder(IDictionary<ModelMetadata, IModelBinder> propertyBinders, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) :
base(propertyBinders, loggerFactory)
{
}
//-------------------------------------------------------------------
/// <summary>
/// Creates the model with one (or more) injected service parameters.
/// </summary>
protected override object CreateModel(ModelBindingContext bindingContext)
{
var services = bindingContext.HttpContext.RequestServices;
var modelType = bindingContext.ModelType;
var ctors = modelType.GetConstructors();
foreach (var ctor in ctors)
{
var paramTypes = ctor.GetParameters().Select(p => p.ParameterType).ToList();
var parameters = paramTypes.Select(p => services.GetService(p)).ToArray();
if (parameters.All(p => p != null))
{
var model = ctor.Invoke(parameters);
return model;
}
}
return null;
}
//-------------------------------------------------------------------
/// <summary>
/// Intercept String properties and trim whitespace.
/// </summary>
protected override void SetProperty(ModelBindingContext bindingContext, string modelName, ModelMetadata propertyMetadata, ModelBindingResult result)
{
string fn = GetType().Name + ".SetProperty";
//if (SaxSystem.DebugMode) Logger.Debug(fn + " Form property " + modelName + ": '" + result.Model.ToString() + ".");
object originalValue = result.Model;
// Exclude properties that retain leading/trailing whitespace.
if (result.Model is string)
{
if (!modelName.ContainsCI("Password"))
{
string value = result.Model as string;
// Trim leading and trailing whitespace.
value = StringUtil.TrimAll(value);
// Check for multi-line.
if (value.Contains('\n'))
{
// Simplify line delimiters.
value = Regex.Replace(value, "\\r\\n", "\n");
// Remove white space in front of newlines.
value = Regex.Replace(value, "\\s+\\n", "\n");
// Remove multiple newlines.
value = Regex.Replace(value, "\\n+", "\n");
}
// Email addresses must be lowercase to ensure match to encrypted data.
if (modelName.EqualsCI("EmailAddress") || modelName.EqualsCI("AccountEmailAddress"))
value = value.ToLower();
// Uppercase selected values.
if (modelName.EqualsCI("Postcode") || modelName.EqualsCI("AccountPostcode") || modelName.EqualsCI("RegisteredAddressPostcode"))
value = value.ToUpper();
// Assign resulting value.
result = ModelBindingResult.Success(value);
}
}
if (result.Model != null && SaxSystem.DebugMode)
SaLogger.Debug(fn + " Form property " + modelName + ": '" + result.Model.ToString() + "'." + (originalValue != null && !originalValue.Equals(result.Model) ? " Original value: '" + originalValue.ToString() + "'" : ""));
base.SetProperty(bindingContext, modelName, propertyMetadata, result);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,43 @@
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Saxonite.Net.Core.Web.Base.Middleware
{
//=======================================================================
/// <summary>
/// Maps a browser request to a data object. Default class provides a base implementation of a model binder.
/// <para>Overridden to trim whitespace from input values.</para>
/// </summary>
public class SaxModelBinderProvider : IModelBinderProvider
{
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
// Construction.
public SaxModelBinderProvider()
{
}
//-------------------------------------------------------------------
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null) throw new ArgumentNullException(nameof(context));
if (context.Metadata.IsComplexType && !context.Metadata.IsCollectionType)
{
var propertyBinders = context.Metadata.Properties.ToDictionary(property => property, context.CreateBinder);
ILoggerFactory loggerFactory = (ILoggerFactory)context.Services.GetService(typeof(ILoggerFactory));
return new SaxModelBinder(propertyBinders, loggerFactory);
}
// Default - return null so that default binding provider is used.
return null;
}
}
//=======================================================================
}
@@ -0,0 +1,71 @@
using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
using Saxonite.Net.Standard.Base.App;
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Base.Middleware
{
//=======================================================================
/// <summary>
/// Extend IApplicationBuilder class to use the anit-forgery token validation middleware.
/// </summary>
public static class ApplicationBuilderExtensions
{
public static IApplicationBuilder UseAntiforgeryTokens(this IApplicationBuilder app)
{
return app.UseMiddleware<ValidateAntiForgeryTokenMiddleware>();
}
}
//=======================================================================
/// <summary>
/// Ensure relevant actions validate the anti-forgery token, eg. post requests.
/// </summary>
public class ValidateAntiForgeryTokenMiddleware
{
private readonly RequestDelegate _next;
private readonly IAntiforgery _antiforgery;
public ValidateAntiForgeryTokenMiddleware(RequestDelegate next, IAntiforgery antiforgery)
{
_next = next;
_antiforgery = antiforgery;
}
public async Task Invoke(HttpContext context)
{
String fn = GetType().Name + ".Invoke";
// Log action.
//String rawTarget = context.Features.Get<IHttpRequestFeature>().RawTarget;
if (SaxSystem.DebugMode) SaLogger.Debug(fn + " Action: '" + Regex.Replace(context.Request.Path.ToString(), "^/", "") + "'. Method: " + context.Request.Method);
// Intercept POST requests and apply anti-forgery test.
if (HttpMethods.IsPost(context.Request.Method))
{
await _antiforgery.ValidateRequestAsync(context);
}
try
{
await _next(context);
}
catch (Exception exc)
{
SaLogger.Warn(fn + " " + ExceptionUtil.GetLogMessage(exc));
throw exc;
}
}
}
//=======================================================================
}
@@ -0,0 +1,275 @@
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc.Rendering;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Dbas;
using Sarand.Net.Standard.Base.Utils;
using Saxonite.Base.App;
using Saxonite.Net.Core.Web.Base.App;
using Saxonite.Net.Standard.Base.App;
using Saxonite.Net.Standard.Base.Consoles;
using Saxonite.Net.Standard.Base.Mgr;
using System;
using System.Collections.Generic;
namespace Saxonite.Net.Core.Web.Base.Models
{
//=======================================================================
public class SaxModel_Base
{
//-------------------------------------------------------------------
// Data members.
private SaxInternals internals;
private readonly List<String> infoMessages = new List<string>();
private readonly List<String> warningMessages = new List<string>();
private readonly List<String> errorMessages = new List<string>();
//-------------------------------------------------------------------
// Protected construction.
protected SaxModel_Base(SaxLocaliser localiser)
{
Localiser = localiser;
}
//-------------------------------------------------------------------
// Application properties.
private SaxApplication_Web App { get { return SaxApplication_Web.WebApp; } }
protected SaxSessionConfig SessionConfig { get { return App.SessionConfig; } }
protected SaxSystem_Web System { get { return App.System_Web; } }
public String SessionCookieName { get { return App.SessionCookieName; } }
public SaxSessionData SessionData { get; set; }
public SaxSessionContext SessionContext { get { return SessionData?.SessionContext ?? App.SessionContext; } }
public DbUpdateInfo DbUpdateInfo { get { return SessionData?.DbUpdateInfo ?? new DbUpdateInfo(null, "", SaxSystem.ComputerName); } }
public bool DebugMode { get { return SaxSystem.DebugMode; } }
public SaxLocaliser Localiser { get; }
//-------------------------------------------------------------------
// Business components.
protected SaxInternals Internals { get { if (internals == null) internals = new SaxInternals(SessionContext); return internals; } }
// Consoles.
protected SaxDataCacheConsole DataCacheConsole { get { return Internals.DataCacheConsole; } }
// Managers.
protected SaxReferenceMgr ReferenceMgr { get { return Internals.ReferenceMgr; } }
protected SaxAccountMgr AccountMgr { get { return Internals.AccountMgr; } }
protected SaxActivityMgr ActivityMgr { get { return Internals.ActivityMgr; } }
protected SaxItemDataMgr ItemDataMgr { get { return Internals.ItemDataMgr; } }
// System codes.
public List<TypeCodeDesc> RegionTypes { get { return Internals.RegionTypes; } }
public List<TypeCodeDesc> UserRoles { get { return Internals.UserRoles; } }
public List<TypeCodeDesc> OrganisationTypes { get { return Internals.OrganisationTypes; } }
public List<TypeCodeDesc> OrganisationCategories { get { return Internals.OrganisationCategories; } }
public List<TypeCodeDesc> OrganisationRoles { get { return Internals.OrganisationRoles; } }
//-------------------------------------------------------------------
// Page configuration.
public bool OK { get { return !HasMessages(); } }
public virtual bool ProhibitCache { get; set; } = true;
public String PageCaption { get; set; }
public String InstanceVersion { get { return !StringUtil.IsEmpty(App.SessionConfig.Instance) ? App.SessionConfig.Instance + " v" + Sax.Version.GetFullVersion() : "v" + Sax.Version.GetAppVersion(); } }
public bool IncludeGoogleAnalytics { get; set; } = false;
//-------------------------------------------------------------------
// Security.
public bool ValidUserSession { get { return SessionData?.ValidUserSession ?? false; } }
public bool AwaitingLogin { get { return !ValidUserSession; } }
public bool KeepLoggedIn { get { return !AwaitingLogin && SessionData.User.KeepLoggedIn; } }
/// <summary>Maximum length allowed for password entry: sign-up, change password etc.</summary>
public int PasswordMaxLength { get; } = 32;
// Account actions.
// NB The form proerty will never be used - account actions processed within page.
public String AccountAction { get; set; }
public IEnumerable<SelectListItem> GetAccountActions()
{
List<CodeDesc> items = new List<CodeDesc>();
if (SessionData?.ValidUserSession ?? false)
{
items.Add("Profile", Localiser["label.view.profile"]);
items.Add("ChangePassword", Localiser["label.change.password"]);
items.Add("SignOut", Localiser["label.sign.out"]);
}
return new SelectList(items, "Code", "Desc");
}
//-------------------------------------------------------------------
/// <summary>
/// Form control value max length.
/// <para>In many cases the database columns allow plenty of extra space for the value, in case they need to be encrypted.</para>
/// <para>Hence, several cases where the value entered in the control isn't allowed the full database length.</para>
/// </summary>
public int GetControlMaxLength(SaxDbDfn.ColumnIdx column)
{
int maxLength = (int)SaxDbDfn.GetColTextLen(column);
switch (column)
{
case SaxDbDfn.ColumnIdx.UserAccount_EmailAddress:
return Math.Min(maxLength, 60);
case SaxDbDfn.ColumnIdx.UserAccount_FirstName:
case SaxDbDfn.ColumnIdx.UserAccount_LastName:
return Math.Min(maxLength, 40);
case SaxDbDfn.ColumnIdx.Organisation_Name:
return Math.Min(maxLength, 80);
case SaxDbDfn.ColumnIdx.UserAccount_PhoneMain:
case SaxDbDfn.ColumnIdx.UserAccount_PhoneMobile:
case SaxDbDfn.ColumnIdx.Organisation_PhoneMain:
case SaxDbDfn.ColumnIdx.Organisation_PhoneAlt:
return Math.Min(maxLength, 20);
//case SaxDbDfn.ColumnIdx.UserAccount_Postcode:
//case SaxDbDfn.ColumnIdx.ItemAddress_Postcode:
// return Math.Min(maxLength, 16);
}
return maxLength;
}
//-------------------------------------------------------------------
// Copyright notice.
public HtmlString CopyrightNotice
{
get
{
int initialYear = 2020;
int currentYear = DateTime.Today.Year;
return Localiser.GetHtmlText("copyright.symbol.year.html", (initialYear != currentYear ? initialYear + "-" : "") + currentYear.ToString());
}
}
//-------------------------------------------------------------------
// Link URLs.
public class LinksData
{
public String Facebook { get; } = "https://www.facebook.com/saxonite/";
public String LinkedIn { get; } = "https://www.linkedin.com/company/saxonite/about/";
}
public LinksData Links { get; } = new LinksData();
//-------------------------------------------------------------------
// Form data.
/// <summary>List of Yes/No options for dropdown.</summary>
public IEnumerable<SelectListItem> YesNoOptionsForSelect
{
get { return new SelectList(SaxSystem.GetCodesOfType(Sax.CodeType.YesNo), "Code", "Desc"); }
}
/// <summary>List of titles for dropdown.</summary>
public IEnumerable<SelectListItem> TitlesForSelect
{
get { return new SelectList(SaxSystem.GetCodesOfType(Sax.CodeType.Title), "Code", "Desc"); }
}
/// <summary>List of Region Types for dropdown.</summary>
public IEnumerable<SelectListItem> RegionTypesForSelect
{
get { return new SelectList(RegionTypes, "Code", "Desc"); }
}
/// <summary>List of Organisation Types for dropdown.</summary>
public IEnumerable<SelectListItem> OrganisationTypesForSelect
{
get { return new SelectList(OrganisationTypes, "Code", "Desc"); }
}
/// <summary>List of Organisation Categories for dropdown.</summary>
public IEnumerable<SelectListItem> OrganisationCategoriesForSelect
{
get { return new SelectList(OrganisationCategories, "Code", "Desc"); }
}
//-------------------------------------------------------------------
// Version parameter for includes.
public String HrefVersion
{
get
{
DateTime versionDate = SaxSystem.AssemblyBuildAt;
if (!DateUtil.IsValid(versionDate) || SaxSystem.DebugMode)
versionDate = DateTime.Now;
return DateUtil.Format(versionDate, "yyyyMMdd" /*+ (SacsSystem.Debug ? "HHmm" : "")*/);
}
}
// Additional includes.
public List<String> AdditionalFiles_CSS { get; set; } = new List<string>();
public List<String> AdditionalFiles_JS { get; set; } = new List<string>();
//-------------------------------------------------------------------
// Redirection locations.
public class RedirectLocationData
{
// Properties populated in Controller_Base.OnActionExecuted.
public String Base { get; internal set; }
public String Home { get; internal set; }
public String Home_SessionExpired { get; internal set; }
}
public RedirectLocationData RedirectLocation { get; } = new RedirectLocationData();
//-------------------------------------------------------------------
// Paging properties.
private class DataPaging
{
private DbQuery.Paging paging { get; } = new DbQuery.Paging();
public void PopulatePagingTo(DbQuery.Paging resultsPaging)
{
resultsPaging.PageLength = paging.PageLength;
resultsPaging.PageNo = paging.PageNo;
}
public bool IsPaged { get { return paging.Paged; } }
public uint PageNo { get { return paging.PageNo; } set { paging.PageNo = value; } }
public ushort PageLength { get { return paging.PageLength; } set { paging.PageLength = value; } }
public uint PageCount { get { return (PageLength > 0 ? (uint)Math.Ceiling((decimal)ItemTotal / PageLength) : 0); } }
public ushort ItemCount { get; set; }
public ulong ItemTotal { get; set; }
}
private DataPaging Paging { get; } = new DataPaging();
public bool DataPaged { get { return Paging.IsPaged; } }
public uint DataPageNo { get { return Paging.PageNo; } set { Paging.PageNo = value; } }
public ushort DataPageLength { get { return Paging.PageLength; } set { Paging.PageLength = value; } }
public uint DataPageCount { get { return (DataPageLength > 0 ? (uint)Math.Ceiling((decimal)DataItemTotal / DataPageLength) : 0); } }
public ushort DataItemCount { get; set; }
public ulong DataItemTotal { get; set; }
public void PopulatePagingTo(DbQuery.Paging resultsPaging)
{
Paging.PopulatePagingTo(resultsPaging);
}
//-------------------------------------------------------------------
// Form properties.
/// <summary>
/// User session index to identify the site resources, eg. database.
/// <para>Equivalent to the Site index on the login form.</para>
/// <para>Populates value on logout form to identify default site for next login.</para>
/// </summary>
public ushort SessionIdx { get { return SessionContext.Index; } }
public String FormAction { get; set; }
//-------------------------------------------------------------------
// Messages.
//-------------------------------------------------------------------
public bool HasMessages()
{
return infoMessages.Count > 0 || warningMessages.Count > 0 || errorMessages.Count > 0;
}
public List<String> InfoMessages { get { return new List<string>(infoMessages); } }
public List<String> WarningMessages { get { return new List<string>(warningMessages); } }
public List<String> ErrorMessages { get { return new List<string>(errorMessages); } }
//-------------------------------------------------------------------
// Form messages.
public void AddInfoMessage(String message)
{
if (StringUtil.IsEmpty(StringUtil.TrimAll(message))) return;
infoMessages.Add(message);
}
public void AddWarningMessage(String message)
{
if (StringUtil.IsEmpty(StringUtil.TrimAll(message))) return;
warningMessages.Add(message);
}
public void AddErrorMessage(String message)
{
if (StringUtil.IsEmpty(StringUtil.TrimAll(message))) return;
errorMessages.Add(message);
}
//-------------------------------------------------------------------
}
//=======================================================================
}