diff --git a/Saxonite.sln b/Saxonite.sln index 64d1dac..997f4ec 100644 --- a/Saxonite.sln +++ b/Saxonite.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.31019.35 +# Visual Studio Version 17 +VisualStudioVersion = 17.7.34202.233 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SaxoniteCoreWebAdmin", "SaxoniteCoreWebAdmin\SaxoniteCoreWebAdmin.csproj", "{F646283D-F7C2-42DD-AB1E-E741455664BD}" EndProject @@ -32,6 +32,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution files", "Solution EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SaxoniteCoreUnitTestsOnline", "SaxoniteCoreUnitTestsOnline\SaxoniteCoreUnitTestsOnline.csproj", "{303BA8E9-1B95-41AD-9F51-32037A22C908}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SacsStandardProc", "..\..\Common-VS\Workspace\SacsStandardProc\SacsStandardProc.csproj", "{E012B3D3-CF4D-4C2E-ABC6-9E3A3DAE03C5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -86,6 +88,10 @@ Global {303BA8E9-1B95-41AD-9F51-32037A22C908}.Debug|Any CPU.Build.0 = Debug|Any CPU {303BA8E9-1B95-41AD-9F51-32037A22C908}.Release|Any CPU.ActiveCfg = Release|Any CPU {303BA8E9-1B95-41AD-9F51-32037A22C908}.Release|Any CPU.Build.0 = Release|Any CPU + {E012B3D3-CF4D-4C2E-ABC6-9E3A3DAE03C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E012B3D3-CF4D-4C2E-ABC6-9E3A3DAE03C5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E012B3D3-CF4D-4C2E-ABC6-9E3A3DAE03C5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E012B3D3-CF4D-4C2E-ABC6-9E3A3DAE03C5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/SaxoniteCoreUnitTests/SaxoniteCoreUnitTests.csproj b/SaxoniteCoreUnitTests/SaxoniteCoreUnitTests.csproj index e2694b2..f9569c5 100644 --- a/SaxoniteCoreUnitTests/SaxoniteCoreUnitTests.csproj +++ b/SaxoniteCoreUnitTests/SaxoniteCoreUnitTests.csproj @@ -1,7 +1,7 @@ - netcoreapp3.1 + net6.0 false @@ -10,10 +10,13 @@ - - - - + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/SaxoniteCoreUnitTestsOnline/SaxoniteCoreUnitTestsOnline.csproj b/SaxoniteCoreUnitTestsOnline/SaxoniteCoreUnitTestsOnline.csproj index 213a718..2ba9a0b 100644 --- a/SaxoniteCoreUnitTestsOnline/SaxoniteCoreUnitTestsOnline.csproj +++ b/SaxoniteCoreUnitTestsOnline/SaxoniteCoreUnitTestsOnline.csproj @@ -1,7 +1,7 @@ - netcoreapp3.1 + net6.0 false @@ -10,10 +10,13 @@ - - - - + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/SaxoniteCoreUnitTestsOnline/UnitTests/Online/App/SaxSessionData_Online_Tester.cs b/SaxoniteCoreUnitTestsOnline/UnitTests/Online/App/SaxSessionData_Online_Tester.cs index 9c75bf8..a1fdd7a 100644 --- a/SaxoniteCoreUnitTestsOnline/UnitTests/Online/App/SaxSessionData_Online_Tester.cs +++ b/SaxoniteCoreUnitTestsOnline/UnitTests/Online/App/SaxSessionData_Online_Tester.cs @@ -121,7 +121,7 @@ namespace Saxonite.Net.Core.UnitTests.Web.Online.App Assert.AreEqual(userRoles.Count, sessionDataDeserialised.User.Roles.Count); foreach (var userRoleDeserialised in sessionDataDeserialised.User.Roles) { - Assert.AreNotEqual(0, userRoleDeserialised.Role); + Assert.AreNotEqual(0, (int)userRoleDeserialised.Role); } // User configuration. diff --git a/SaxoniteCoreWeb/Base/App/SaxApplication_Web.cs b/SaxoniteCoreWeb/Base/App/SaxApplication_Web.cs index 7b6763a..aab65c1 100644 --- a/SaxoniteCoreWeb/Base/App/SaxApplication_Web.cs +++ b/SaxoniteCoreWeb/Base/App/SaxApplication_Web.cs @@ -41,13 +41,23 @@ namespace Saxonite.Net.Core.Web.Base.App protected SaxApplication_Web() { WebApp = this; + System_Web = new SaxSystem_Web(); + } + protected override void Dispose(bool disposing) + { + if (disposing) + { + WebApp = null; + System_Web = null; + } + base.Dispose(disposing); } //------------------------------------------------------------------- - // Properties. - public override SaxSystem System { get { return System_Web; } } - public SaxSystem_Web System_Web { get; protected set; } + // Globals. public static SaxApplication_Web WebApp { get; private set; } + public override SaxSystem System { get { return System_Web; } } + public SaxSystem_Web System_Web { get; private set; } // Application instance. public override Sax.InstanceType InstanceType { get { return instanceType; } } public override String EnvironmentName { get { return environmentName; } } @@ -57,7 +67,6 @@ namespace Saxonite.Net.Core.Web.Base.App public abstract String SessionCookieName { get; } public virtual SaxSessionContext SessionContext { get; protected set; } public abstract SaxAppConfig AppConfig { get; } - public override SaxConsoleHelper ConsoleHelper { get { return ConsoleHelper_Web; } } public SaxConsoleHelper_Web ConsoleHelper_Web { get; protected set; } public override IDatabaseInfo CreateDbInfo(SaxSessionContext sessionContext, Sax.Database databaseType) { return new DbDataItem.DatabaseInfo(GetDatabase(sessionContext, databaseType)); } // Configuration. diff --git a/SaxoniteCoreWeb/Base/App/SaxStartup.cs b/SaxoniteCoreWeb/Base/App/SaxStartup.cs index 40c1540..0f2475e 100644 --- a/SaxoniteCoreWeb/Base/App/SaxStartup.cs +++ b/SaxoniteCoreWeb/Base/App/SaxStartup.cs @@ -113,7 +113,7 @@ namespace Saxonite.Net.Core.Web.Base.App opts => { opts.ResourcesPath = resourcesPath; }) .AddDataAnnotationsLocalization() // Ignore null values during object serisliation. - .AddJsonOptions(options => { options.JsonSerializerOptions.IgnoreNullValues = true; }); + .AddJsonOptions(options => { options.JsonSerializerOptions.DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull; }); // Configure localisation middleware. config.services.Configure diff --git a/SaxoniteCoreWeb/Base/Consoles/SaxConsoleHelper_Web.cs b/SaxoniteCoreWeb/Base/Consoles/SaxConsoleHelper_Web.cs index 35d5b28..71c43ae 100644 --- a/SaxoniteCoreWeb/Base/Consoles/SaxConsoleHelper_Web.cs +++ b/SaxoniteCoreWeb/Base/Consoles/SaxConsoleHelper_Web.cs @@ -13,6 +13,10 @@ namespace Saxonite.Net.Core.Web.Base.Consoles protected SaxConsoleHelper_Web() { } + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + } //------------------------------------------------------------------- } diff --git a/SaxoniteCoreWeb/Base/Controllers/SaxController_Base.cs b/SaxoniteCoreWeb/Base/Controllers/SaxController_Base.cs index 28de7d5..b6bfa5f 100644 --- a/SaxoniteCoreWeb/Base/Controllers/SaxController_Base.cs +++ b/SaxoniteCoreWeb/Base/Controllers/SaxController_Base.cs @@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Routing; using Microsoft.Net.Http.Headers; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; +using Sacs.Web.Components; using Sarand.Net.Standard.Base.App; using Sarand.Net.Standard.Base.Bus; using Sarand.Net.Standard.Base.Core; @@ -34,7 +35,7 @@ using System.Web; namespace Saxonite.Net.Core.Web.Base.Controllers { //======================================================================= - public abstract class SaxController_Base : Controller + public abstract class SaxController_Base : SaController { //------------------------------------------------------------------- // Data members. @@ -44,7 +45,6 @@ namespace Saxonite.Net.Core.Web.Base.Controllers // Protected construction. protected SaxController_Base(SaxLocaliser localiser) { - CreatedAt = DateTime.Now; Localiser = localiser; // Initialise console (debug) logging listener. @@ -62,43 +62,14 @@ namespace Saxonite.Net.Core.Web.Base.Controllers //------------------------------------------------------------------- // Properties. - public DateTime CreatedAt { get; } - public bool DebugMode { get; } = SacsSystem.DebugMode; protected SaxApplication_Web App { get { return SaxApplication_Web.WebApp; } } public SaxAppConfig AppConfig { get { return App.AppConfig; } } public SaxSessionContext SessionContext { get { return App.SessionContext; } } public SaxLocaliser Localiser { get; } - /// - /// Indicate whether session data should be stored by action. - /// Any action that might modify the session data must store it. - /// Some actions such as scoring a password's complexity don't need to. - /// - public virtual bool StoreSessionData { get; protected set; } = true; //------------------------------------------------------------------- - // Culture. - private CultureInfo sessionCulture; - private CultureInfo RequestCulture { get { return Request.HttpContext.Features.Get().RequestCulture.Culture ?? CultureInfo.InvariantCulture; } } - public CultureInfo SessionCulture - { - get - { - if (sessionCulture == null) - { - sessionCulture = RequestCulture; - // ToDo Adjust culture if needed. - } - return sessionCulture; - } - } - private string LanguageIso2 - { - get - { - var feature = HttpContext.Features.Get(); - return feature.RequestCulture.UICulture.TwoLetterISOLanguageName.ToLower(); - } - } + // Globals. + protected SaxConsoleHelper ConsoleHelper { get { return SaxGlobals.ConsoleHelper; } } //------------------------------------------------------------------- // Business components. @@ -138,36 +109,6 @@ namespace Saxonite.Net.Core.Web.Base.Controllers public SaEmailConfig EmailConfig { get { return App.EmailConfig; } } public SaxEmailConsole EmailConsole { get { return Internals.EmailConsole; } } - //------------------------------------------------------------------- - // Redirection locations. - //------------------------------------------------------------------- - public static readonly String ParamName_SessionExpired = "SessionExpired"; - public enum RedirectLocation - { - WebAppBase, - WebAppHome, - WebAppHome_SessionExpired, - } - public 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. //------------------------------------------------------------------- @@ -212,7 +153,6 @@ namespace Saxonite.Net.Core.Web.Base.Controllers Response.Headers.Add(HeaderNames.Expires, "0"); } - base.OnActionExecuted(filterContext); } @@ -244,78 +184,22 @@ namespace Saxonite.Net.Core.Web.Base.Controllers //private static String Base64IV_RequestPassword_Generated_Online { get; } = SacsEncryptionHelper.InitSymmetricEncryptionIV(Base64Key_RequestPassword_Online); protected static String Base64IV_RequestPassword_Online { get; } = "1VAWYTW6d72n/6HDNhbqow=="; - //------------------------------------------------------------------- - public static String EncryptParameters(NameValueCollection parameters, String base64IV, String base64Key) - { - String fn = nameof(ControllerBase) + ".EncryptParameters"; - try - { - // Copy the list of parameters and sort the keys. - NameValueCollection parametersToEncrypt = new NameValueCollection(parameters); - 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. - // Start with a random length parameter name at start of list to obscure the encryption. - string queryString = SacsRandomHelper.getRandomLengthNumber(4) + "&"; - queryString += String.Join("&", paramKeys.Select(a => a + "=" + HttpUtility.UrlEncode(parametersToEncrypt[a]))); - - // Encrypt data. - String encrypted = SacsEncryptionHelper.Encrypt(queryString, base64IV, base64Key); - //if (SacsSystem.DebugMode) SaLogger.Debug(fn + "\n\tData: " + queryString + " \n\tEncrypted: " + encrypted); - return encrypted; - } - catch (Exception exc) { SaLogger.Warn(fn + " " + ExceptionUtil.GetErrorStateMessage(exc)); } - return ""; - } - public static NameValueCollection DecryptParameters(String encrypted, String base64IV, String base64Key) - { - String fn = nameof(ControllerBase) + ".DecryptParameters"; - try - { - String queryString = SacsEncryptionHelper.Decrypt(encrypted, base64IV, base64Key); - //if (SacsSystem.DebugMode) SaLogger.Debug(fn + " \n\tEncrypted: " + encrypted + "\n\tData: " + queryString); - return HttpUtility.ParseQueryString(queryString); - } - catch (Exception exc) { SaLogger.Warn(fn + " " + ExceptionUtil.GetErrorStateMessage(exc)); } - return new NameValueCollection(); - } - - //------------------------------------------------------------------- - // Cookies. - //------------------------------------------------------------------- - /// Retrieve the value from the named request cookie. - public String GetRequestCookie(String key) - { - if (Request.Cookies.ContainsKey(key)) - return Request.Cookies[key]; - return null; - } - /// Set the value against the named response cookie. - 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. //------------------------------------------------------------------- + private bool UserActiveRecorded { get; set; } = false; public bool ValidUserSession { get { if (SessionData == null || !SessionData.ValidUserSession) return false; SaxSessionDataConsole sessionDataConsole = SessionDataConsole; - if (sessionDataConsole != null) + if (sessionDataConsole != null && !UserActiveRecorded) + { + // Record the user as active once per request. sessionDataConsole.RecordUserActive(SessionUserAccountId, CreatedAt); + UserActiveRecorded = true; + } return true; } } @@ -403,240 +287,6 @@ namespace Saxonite.Net.Core.Web.Base.Controllers } 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); - } - - //------------------------------------------------------------------- - // Response data. - // Null values are ignored by JSON serialisation. - // - See Saxonite.Net.Core.Web.Base.App.SaxStartup.ConfigureServices() - // - Ref. config.services.AddMvc(...).AddJsonOptions(options => { options.JsonSerializerOptions.IgnoreNullValues = true; }); - //------------------------------------------------------------------- - // Base response data - for simple responses. - public class AjaxResponse_Base - { - // Response status. - public bool? Success { get; set; } = null; - public bool? Error { get; set; } = null; - public bool? SessionExpired { get; protected set; } = null; - public bool? GoHome { get; protected set; } = null; - 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 - { - //--------------------------------------------------------------- - // Construction. - protected AjaxResponse_Data(SaxModel_Base model) - { - } - - //--------------------------------------------------------------- - /// Item data - Common properties for data items. - public class ItemData_Base - { - // Common item properties. - public String Id { get; set; } - public String Notes { get; set; } - public String StatusCode { get; set; } - public String StatusDesc { get; set; } - // Flags for common status values. - // Return true or null so that property is not serialised when false. - public bool? Awaiting { get { return StatusCode.EqualsCI(SacsSystem.GetStatusCode(SaStatus.Awaiting)) ? true : (bool?)null; } } - public bool? Active { get { return StatusCode.EqualsCI(SacsSystem.GetStatusCode(SaStatus.Active)) ? true : (bool?)null; } } - public bool? Inactive { get { return StatusCode.EqualsCI(SacsSystem.GetStatusCode(SaStatus.Inactive)) ? true : (bool?)null; } } - public bool? Deleted { get { return StatusCode.EqualsCI(SacsSystem.GetStatusCode(SaStatus.Deleted)) ? true : (bool?)null; } } - /// Created on date - YYYYMMDD - public String CreatedOn { get; set; } - /// Created on date - formatted per locale - public String CreatedOnFmt { get; set; } - /// Updated on date - YYYYMMDD - public String UpdatedOn { get; set; } - /// Updated on date - formatted per locale - public String UpdatedOnFmt { get; set; } - } - /// Contact Method properties. - public class ContactMethodData - { - public String Method { get; set; } - public String MethodCaption { get; set; } - public String Value { get; set; } - } - /// Address properties. - public class AddressData : ItemData_Base - { - public String Line1 { get; set; } - public String Line2 { get; set; } - public String Line3 { get; set; } - public String Town { get; set; } - public String RegionIdProvince { get; set; } - public String RegionNameProvince { get; set; } - public String RegionIdCountry { get; set; } - public String RegionNameCountry { get; set; } - public String PostalCode { get; set; } - } - /// - /// Item data with label and value. - /// - public class ItemLabelValue - { - public String Label { get; set; } - public String Value { get; set; } - } - /// - /// Item data with just Id and name. - /// - Id is String to allow for obfuscated values. - /// - public class ItemIdName - { - public String Id { get; set; } - public String Name { get; set; } - } - /// Item code and desc values. - public class ItemCodeDesc - { - public String Code { get; set; } - public String Desc { get; set; } - } - - //--------------------------------------------------------------- - // 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 controlValues = new Dictionary(); - public List ControlValues { get { return controlValues.Count > 0 ? controlValues.Values.ToList() : null; } } - 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)); - } - } - /// Assign the error to the control by name and return this response data. - public AjaxResponse_Data SetControlError(String controlName, String error) - { - return SetControlError(controlName, error, this); - } - /// - /// Assign the error to the control by name and return the specified value. - /// - Allows alternative return values to be specified (eg. 'false') to avoid separate return statement in calling routine. - /// - public T SetControlError(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 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.Paging.PageNo, model.Paging.PageLength, model.Paging.ItemCount, model.Paging.ItemTotal) - { } - 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); - } - - //--------------------------------------------------------------- - } - //------------------------------------------------------------------- } //======================================================================= diff --git a/SaxoniteCoreWeb/Base/Middleware/SaxAuthenticationHandler.cs b/SaxoniteCoreWeb/Base/Middleware/SaxAuthenticationHandler.cs index bbfe397..5c18399 100644 --- a/SaxoniteCoreWeb/Base/Middleware/SaxAuthenticationHandler.cs +++ b/SaxoniteCoreWeb/Base/Middleware/SaxAuthenticationHandler.cs @@ -37,7 +37,7 @@ namespace Saxonite.Net.Core.Web.Base.Middleware protected override async Task HandleAuthenticateAsync() { // NoOp task to avoid compiler warning. - await Task.Yield(); + await Task.CompletedTask; Context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; return AuthenticateResult.NoResult(); } diff --git a/SaxoniteCoreWeb/Base/Middleware/SaxModelBinder.cs b/SaxoniteCoreWeb/Base/Middleware/SaxModelBinder.cs index a328d75..84abc99 100644 --- a/SaxoniteCoreWeb/Base/Middleware/SaxModelBinder.cs +++ b/SaxoniteCoreWeb/Base/Middleware/SaxModelBinder.cs @@ -55,7 +55,6 @@ namespace Saxonite.Net.Core.Web.Base.Middleware /// 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. @@ -91,8 +90,8 @@ namespace Saxonite.Net.Core.Web.Base.Middleware 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() + "'" : "")); + if (result.Model != null && !result.Model.ToString().Equals(result.Model.GetType().FullName) && SaxSystem.DebugMode) + SaLogger.Debug(SysUtil.Fn + " Form property " + modelName + ": '" + result.Model.ToString() + "'." + (originalValue != null && !originalValue.Equals(result.Model) ? " Original value: '" + originalValue.ToString() + "'" : "")); base.SetProperty(bindingContext, modelName, propertyMetadata, result); } diff --git a/SaxoniteCoreWeb/Base/Middleware/ValidateAntiForgeryTokenMiddleware.cs b/SaxoniteCoreWeb/Base/Middleware/ValidateAntiForgeryTokenMiddleware.cs index 95c334d..927063c 100644 --- a/SaxoniteCoreWeb/Base/Middleware/ValidateAntiForgeryTokenMiddleware.cs +++ b/SaxoniteCoreWeb/Base/Middleware/ValidateAntiForgeryTokenMiddleware.cs @@ -62,7 +62,7 @@ namespace Saxonite.Net.Core.Web.Base.Middleware catch (Exception exc) { SaLogger.Warn(fn + " " + ExceptionUtil.GetLogMessage(exc)); - throw exc; + throw; } } } diff --git a/SaxoniteCoreWeb/Base/Models/SaxModel_Base.cs b/SaxoniteCoreWeb/Base/Models/SaxModel_Base.cs index 77f0893..69f5635 100644 --- a/SaxoniteCoreWeb/Base/Models/SaxModel_Base.cs +++ b/SaxoniteCoreWeb/Base/Models/SaxModel_Base.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Html; using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; using Microsoft.AspNetCore.Mvc.Rendering; +using Sacs.Web.Components; using Sarand.Net.Standard.Base.App; using Sarand.Net.Standard.Base.Bus; using Sarand.Net.Standard.Base.Core; @@ -22,14 +23,11 @@ using static Saxonite.Net.Standard.Base.App.Sax; namespace Saxonite.Net.Core.Web.Base.Models { //======================================================================= - public class SaxModel_Base + public class SaxModel_Base : SaModel { //------------------------------------------------------------------- // Data members. private SaxInternals internals; - private readonly List infoMessages = new List(); - private readonly List warningMessages = new List(); - private readonly List errorMessages = new List(); //------------------------------------------------------------------- // Protected construction. @@ -45,7 +43,6 @@ namespace Saxonite.Net.Core.Web.Base.Models protected SaxSystem_Web System { get { return App.System_Web; } } public String SessionCookieName { get { return App.SessionCookieName; } } public SaxLocaliser Localiser { get; } - public bool DebugMode { get { return SaxSystem.DebugMode; } } // Session properties. public SaxSessionData SessionData { get; set; } public SaxSessionContext SessionContext { get { return SessionData?.SessionContext ?? App.SessionContext; } } @@ -56,25 +53,7 @@ namespace Saxonite.Net.Core.Web.Base.Models //------------------------------------------------------------------- // 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.AppConfig.Instance) ? App.AppConfig.Instance + " v" + Sax.Version.GetFullVersion() : "v" + Sax.Version.GetAppVersion(); } } - public bool IncludeGoogleAnalytics { get; set; } = false; - // Views can be registered to avoid multiple inclusions in a page. - private HashSet registeredViews = new HashSet(StringComparer.OrdinalIgnoreCase); - /// - /// Views can be registered to avoid multiple inclusions in a page. - /// Only generate view content if this method returns true. - /// - public bool RegisterView(String viewName) - { -#if DEBUG - if (registeredViews.Contains(viewName)) - SaLogger.Warn(this.GetType().Name + ".RegisterView View already registered: " + viewName); -#endif - return registeredViews.Add(viewName); - } //------------------------------------------------------------------- // Business components. @@ -156,33 +135,6 @@ namespace Saxonite.Net.Core.Web.Base.Models return new SelectList(items, "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 HashSet AdditionalFiles_CSS { get; set; } = new HashSet(StringComparer.OrdinalIgnoreCase); - public HashSet AdditionalFiles_JS { get; set; } = new HashSet(StringComparer.OrdinalIgnoreCase); - - //------------------------------------------------------------------- - // 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(); - //------------------------------------------------------------------- // Link URLs. public class LinksData @@ -204,42 +156,6 @@ namespace Saxonite.Net.Core.Web.Base.Models } } - //------------------------------------------------------------------- - // Paging properties. - public 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 String PageNoValue { get { return paging.PageNo.ToString(); } set { paging.PageNo = (uint)StringUtil.ToInt(value); } } - public ushort PageLength { get { return paging.PageLength; } set { paging.PageLength = value; } } - public String PageLengthValue { get { return paging.PageLength.ToString(); } set { paging.PageLength = (ushort)StringUtil.ToShort(value); } } - public uint PageCount { get { return (PageLength > 0 ? (uint)Math.Ceiling((decimal)ItemTotal / paging.PageLength) : 0); } } - public ushort ItemCount { get; set; } - private ulong itemTotal = 0; - public ulong ItemTotal - { - get { return itemTotal; } - set - { - itemTotal = value; - // Check whether the total number of items is less than the current page and reduce page no if needed. - if (IsPaged && (PageNo - 1) * PageLength > itemTotal) - PageNo = (uint)((itemTotal - 1) / PageLength + 1); - } - } - } - public DataPaging Paging { get; } = new DataPaging(); - public void PopulatePagingTo(DbQuery.Paging resultsPaging) - { - Paging.PopulatePagingTo(resultsPaging); - } - //------------------------------------------------------------------- /// /// Form control value max length. @@ -487,35 +403,6 @@ namespace Saxonite.Net.Core.Web.Base.Models } } - //------------------------------------------------------------------- - // Messages. - //------------------------------------------------------------------- - public bool HasMessages() - { - return infoMessages.Count > 0 || warningMessages.Count > 0 || errorMessages.Count > 0; - } - public List InfoMessages { get { return new List(infoMessages); } } - public List WarningMessages { get { return new List(warningMessages); } } - public List ErrorMessages { get { return new List(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); - } - //------------------------------------------------------------------- } //======================================================================= diff --git a/SaxoniteCoreWeb/Base/Procs/Data/UploadFileData.cs b/SaxoniteCoreWeb/Base/Procs/Data/UploadFileData.cs new file mode 100644 index 0000000..6cca7a3 --- /dev/null +++ b/SaxoniteCoreWeb/Base/Procs/Data/UploadFileData.cs @@ -0,0 +1,62 @@ +using Microsoft.AspNetCore.Http; +using Sarand.Net.Standard.Base.Core; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Saxonite.Net.Core.Web.Base.Procs.Data +{ + //======================================================================= + /// + /// Object to persist upload file after request has terminated, and the Form File has been disposed. + /// - Too allow async processes to access the file after request completed. + /// + public class UploadFileData + { + //------------------------------------------------------------------- + public UploadFileData(IFormFile dataFile) + { + ContentDisposition = dataFile.ContentDisposition; + ContentType = dataFile.ContentType; + FileName = dataFile.FileName; + Length = dataFile.Length; + Name = dataFile.Name; + foreach (var header in dataFile.Headers) + Headers.Add(header.Key, header.Value.ToString()); + // Load file data. + using (MemoryStream stream = new MemoryStream()) + { + dataFile.CopyTo(stream); + FileData = stream.ToArray(); + } + } + + //------------------------------------------------------------------- + // Properties. + + /// Gets the raw Content-Disposition header of the uploaded file. + public String ContentDisposition { get; } + + /// Gets the raw Content-Type header of the uploaded file. + public String ContentType { get; } + + /// Gets the file name from the Content-Disposition header. + public String FileName { get; } + + /// Gets the header dictionary of the uploaded file. + public Dictionary Headers { get; } = new Dictionary(); + + /// Gets the file length in bytes. + public long Length { get; } + + /// Gets the form field name from the Content-Disposition header. + public String Name { get; } + + /// File data. + public byte[] FileData { get; } + + //------------------------------------------------------------------- + } + //======================================================================= +} diff --git a/SaxoniteCoreWeb/SaxoniteCoreWeb.csproj b/SaxoniteCoreWeb/SaxoniteCoreWeb.csproj index 8f6a14e..5254883 100644 --- a/SaxoniteCoreWeb/SaxoniteCoreWeb.csproj +++ b/SaxoniteCoreWeb/SaxoniteCoreWeb.csproj @@ -1,7 +1,7 @@ - netcoreapp3.1 + net6.0 Saxonite.Net.Core.Web diff --git a/SaxoniteCoreWebAdmin/Admin/App/Program.cs b/SaxoniteCoreWebAdmin/Admin/App/Program.cs index 28e178a..7323304 100644 --- a/SaxoniteCoreWebAdmin/Admin/App/Program.cs +++ b/SaxoniteCoreWebAdmin/Admin/App/Program.cs @@ -15,7 +15,16 @@ namespace Saxonite.Net.Core.Web.Admin.App //------------------------------------------------------------------- public static void Main(string[] args) { - CreateHostBuilder(args).Build().Run(); + try + { + CreateHostBuilder(args).Build().Run(); + } + finally + { + // Cleanup - globals, async processes etc. + if (SaxApplication_Admin.AppInstance != null) + SaxApplication_Admin.AppInstance.Dispose(); + } } //------------------------------------------------------------------- diff --git a/SaxoniteCoreWebAdmin/Admin/App/SaxApplication_Admin.cs b/SaxoniteCoreWebAdmin/Admin/App/SaxApplication_Admin.cs index 0c6eb41..6b3f16b 100644 --- a/SaxoniteCoreWebAdmin/Admin/App/SaxApplication_Admin.cs +++ b/SaxoniteCoreWebAdmin/Admin/App/SaxApplication_Admin.cs @@ -49,30 +49,45 @@ namespace Saxonite.Net.Core.Web.Admin.App try { AppInstance = this; - SaxGlobals.App = this; - SaxGlobals.System = (System_Web = new SaxSystem_Web()); Configuration = configuration; - ConsoleHelper_Web = new SaxConsoleHelper_WebAdmin(this); + ConsoleHelper_Admin = new SaxConsoleHelper_Admin(this); } catch (Exception exc) { SaLogger.Error(GetType().Name + ".CTR " + ExceptionUtil.GetErrorStateMessage(exc)); - throw exc; + throw; } } + protected override void Dispose(bool disposing) + { + if (disposing) + { + // Clear globals. + AppInstance = null; + // Console helper. + if (ConsoleHelper_Admin != null) + ConsoleHelper_Admin.Dispose(); + ConsoleHelper_Admin = null; + } + base.Dispose(disposing); + } //------------------------------------------------------------------- // Properties. public static SaxApplication_Admin AppInstance { get; private set; } - public List Messages { get; } = new List(); + public override SaxConsoleHelper ConsoleHelper { get { return ConsoleHelper_Admin; } } + public SaxConsoleHelper_Admin ConsoleHelper_Admin { get; private set; } + // Database. public override ISacsDatabase GetDatabase(SaxSessionContext sessionContext, Sax.Database databaseType) { return sessionContexts.GetValue(sessionContext.Index, null)?.Databases.GetValue(databaseType, null); } - public override String SessionCookieName { get { return Sax.Config.Admin.Session.Cookie.Name; } } // Configuration. public override SaxAppConfig AppConfig { get { return appConfigs.Values.ToList().getConfigForApplication(); } } public override SaxEmailConfig EmailConfig { get { return emailConfigs.Values.ToList().getConfigForSession(AppConfig); } } + public override String SessionCookieName { get { return Sax.Config.Admin.Session.Cookie.Name; } } + // Startup messages - for debugging. See Admin.Views._Layout. + public List Messages { get; } = new List(); //------------------------------------------------------------------- // Initialisation. diff --git a/SaxoniteCoreWebAdmin/Admin/App/SaxStartup_Admin.cs b/SaxoniteCoreWebAdmin/Admin/App/SaxStartup_Admin.cs index ff08772..16ff632 100644 --- a/SaxoniteCoreWebAdmin/Admin/App/SaxStartup_Admin.cs +++ b/SaxoniteCoreWebAdmin/Admin/App/SaxStartup_Admin.cs @@ -4,6 +4,7 @@ using Microsoft.AspNetCore.HttpsPolicy; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Saxonite.Net.Core.Web.Admin.Consoles; using Saxonite.Net.Core.Web.Base.App; using Saxonite.Net.Standard.Base.App; using System; diff --git a/SaxoniteCoreWebAdmin/Admin/Consoles/SaxConsoleHelper_WebAdmin.cs b/SaxoniteCoreWebAdmin/Admin/Consoles/SaxConsoleHelper_Admin.cs similarity index 63% rename from SaxoniteCoreWebAdmin/Admin/Consoles/SaxConsoleHelper_WebAdmin.cs rename to SaxoniteCoreWebAdmin/Admin/Consoles/SaxConsoleHelper_Admin.cs index faef30a..0402260 100644 --- a/SaxoniteCoreWebAdmin/Admin/Consoles/SaxConsoleHelper_WebAdmin.cs +++ b/SaxoniteCoreWebAdmin/Admin/Consoles/SaxConsoleHelper_Admin.cs @@ -10,7 +10,7 @@ using System.Threading.Tasks; namespace Saxonite.Net.Core.Web.Admin.Consoles { //======================================================================= - public class SaxConsoleHelper_WebAdmin : SaxConsoleHelper_Web + public class SaxConsoleHelper_Admin : SaxConsoleHelper_Web { //------------------------------------------------------------------- // Data members: @@ -18,10 +18,23 @@ namespace Saxonite.Net.Core.Web.Admin.Consoles //------------------------------------------------------------------- // Construction. - public SaxConsoleHelper_WebAdmin(SaxApplication_Admin app) + public SaxConsoleHelper_Admin(SaxApplication_Admin app) { this.app = app; } + protected override void Dispose(bool disposing) + { + if (disposing) + { + // Clear process consoles. + foreach (SaxProcessConsole_Admin processConsole in processConsoles.Values) + { + processConsole.Dispose(); + } + processConsoles.Clear(); + } + base.Dispose(disposing); + } //------------------------------------------------------------------- // Data cache console. @@ -40,6 +53,20 @@ namespace Saxonite.Net.Core.Web.Admin.Consoles return null; } + //------------------------------------------------------------------- + // Process console. + private Dictionary processConsoles { get; } = new Dictionary(); + public SaxProcessConsole GetProcessConsole(SaxSessionContext sessionContext) + { + if (sessionContext == null) throw new NotSupportedException(GetType().Name + ".GetProcessConsole Session Context must be specified"); + SaxProcessConsole_Admin processConsole = processConsoles.GetValueOrDefault(sessionContext.Index, null); + if (processConsole == null) + { + processConsole = processConsoles[sessionContext.Index] = new SaxProcessConsole_Admin(sessionContext); + } + return processConsole; + } + //------------------------------------------------------------------- // Activity console. public override SaxActivityConsole CreateActivityConsole(SaxSessionContext sessionContext) diff --git a/SaxoniteCoreWebAdmin/Admin/Consoles/SaxProcessConsole_Admin.cs b/SaxoniteCoreWebAdmin/Admin/Consoles/SaxProcessConsole_Admin.cs new file mode 100644 index 0000000..204d147 --- /dev/null +++ b/SaxoniteCoreWebAdmin/Admin/Consoles/SaxProcessConsole_Admin.cs @@ -0,0 +1,120 @@ +using MimeKit; +using Sarand.Net.Standard.Base.Core; +using Sarand.Net.Standard.Base.Procs; +using Saxonite.Net.Core.Web.Admin.Procs; +using Saxonite.Net.Standard.Base.App; +using Saxonite.Net.Standard.Base.Consoles; +using Saxonite.Net.Standard.Base.Procs; +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace Saxonite.Net.Core.Web.Admin.Consoles +{ + //======================================================================= + public class SaxProcessConsole_Admin : SaxProcessConsole + { + //------------------------------------------------------------------- + // Definitions. + public enum ProcessType + { + OrganisationUpload + } + + //------------------------------------------------------------------- + // Process cache - to sustain processes outside of request/response timescales. + internal class ProcessData + { + public ProcessData(SaxProcess_Admin process) + { + this.Process = process; + + } + public SaxProcess_Admin Process { get; } + } + // Cache. + private Dictionary processes { get; } = new Dictionary(); + + //------------------------------------------------------------------- + // Data members. + private SaxInternals internals = null; + + //------------------------------------------------------------------- + // Construction. + public SaxProcessConsole_Admin(SaxSessionContext sessionContext) : base(sessionContext) + { + } + protected async override void Dispose(bool disposing) + { + if (disposing) + { + // Clear processes. + foreach (ProcessData processData in processes.Values) + { + await processData.Process.StopProcessAsync(); + processData.Process.Dispose(); + } + } + base.Dispose(disposing); + } + + //------------------------------------------------------------------- + // Processes. + //------------------------------------------------------------------- + + public SaxProcess_Admin GetProcess(ProcessType processType) + { + return processes.GetValueOrDefault(processType, null)?.Process; + } + + public bool IsProcessing(ProcessType processType) + { + SaxProcess_Admin process = GetProcess(processType); + if (process != null) + if (process.Processing) return true; + return false; + } + + /// + /// Remove the process from the cache. + /// + /// + /// + /// + public bool ClearProcess(ProcessType processType) + { + SaxProcess_Admin process = GetProcess(processType); + if (process != null) + { + if (process.Processing) + throw new SaProcess_Base.InProgressException(); + process.Dispose(); + } + return processes.Remove(processType); + } + + public void SetProcess(ProcessType processType, SaxProcess_Admin process) + { + ClearProcess(processType); + processes.Add(processType, new ProcessData(process)); + } + + //------------------------------------------------------------------- + // Specific processes. + //------------------------------------------------------------------- + + //------------------------------------------------------------------- + // Organisation Upload. + public SaxOrganisationUploader GetSaxOrganisationUploader() + { + return GetProcess(ProcessType.OrganisationUpload) as SaxOrganisationUploader; + } + public void SetProcess(SaxOrganisationUploader process) + { + SetProcess(ProcessType.OrganisationUpload, process); + } + + //------------------------------------------------------------------- + } + //======================================================================= +} diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/HomeController-Ajax-ApplicationStats.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/HomeController-Ajax-ApplicationStats.cs index a63e505..79908af 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/HomeController-Ajax-ApplicationStats.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/HomeController-Ajax-ApplicationStats.cs @@ -26,6 +26,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers { public String Caption { get; set; } public AjaxResponse_Data.ItemLabelValue Awaiting { get; set; } + public String UsersLink { get; set; } public String AwaitingLink { get; set; } public List OnlineStats { get; set; } public String OnlineLink { get; set; } @@ -35,6 +36,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers { public String Caption { get; set; } public AjaxResponse_Data.ItemLabelValue Awaiting { get; set; } + public String OrganisationsLink { get; set; } public String AwaitingLink { get; set; } } public OrganisationStatsData Organisation { get; } = new OrganisationStatsData(); @@ -98,10 +100,12 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers { case Sax.DataItemType.UserAccount: responseData.Stats.User.Awaiting = new AjaxResponse_Data.ItemLabelValue() { Label = Localiser["label.awaiting.activation"], Value = itemCount.ToString() }; + responseData.Stats.User.UsersLink = Url.Action("", "User").TrimStart('/'); responseData.Stats.User.AwaitingLink = Url.Action("", "User", new RouteValueDictionary() { { "sta", "Await" } }).TrimStart('/'); break; case Sax.DataItemType.Organisation: responseData.Stats.Organisation.Awaiting = new AjaxResponse_Data.ItemLabelValue() { Label = Localiser["label.awaiting.activation"], Value = itemCount.ToString() }; + responseData.Stats.Organisation.OrganisationsLink = Url.Action("", "Organisation").TrimStart('/'); responseData.Stats.Organisation.AwaitingLink = Url.Action("", "Organisation", new RouteValueDictionary() { { "sta", "Await" } }).TrimStart('/'); break; } diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/HomeController.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/HomeController.cs index 3812dbd..d2d7e25 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/HomeController.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/HomeController.cs @@ -36,30 +36,8 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers { String fn = GetType().Name + ".Index"; - // Check for 'Keep Logged In' cookie. - // NB Only allowed if there isn't a current user session. - if (!ValidUserSession && AppConfig.Security.SignIn.KeepLoggedIn.Allowed) - { - String encryptedParams = GetRequestCookie(KeepLoggedIn.CookieName); - KeepLoggedInParams keepLoggedInParams = ExtractKeepLoggedInParams(encryptedParams); - String logSuffix = keepLoggedInParams.GetValidParams().Concat("", " ", "."); - if (SysId.IsValid(keepLoggedInParams.UserAccountId)) - { - SaxUserAccount userAccount = AccountMgr.GetUserAccountById(keepLoggedInParams.UserAccountId); - if (userAccount != null) - { - if (userAccount.IsStatusActive) - { - String errorMessage = ""; - if (SignUserIn(userAccount, AccountMgr.GetRolesForUser(userAccount.Id), true, ref errorMessage)) - SaLogger.Info(fn + " Keep Logged In cookie found." + logSuffix); - else SaLogger.Warn(fn + " Keep Logged In cookie found but failed to sign user in: " + errorMessage + "." + logSuffix); - } - else SaLogger.Warn(fn + " Keep Logged In cookie found but UserAccount is not active." + logSuffix); - } - else SaLogger.Warn(fn + " Keep Logged In cookie found but UserAccount not found." + logSuffix); - } - } + if (!ValidUserSession) + AutoLoginUser(); // Redirect to sign-in if not signed-in. if (!ValidUserSession) diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Ajax-Upload.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Ajax-Upload.cs new file mode 100644 index 0000000..1c7ce5c --- /dev/null +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Ajax-Upload.cs @@ -0,0 +1,145 @@ +using Microsoft.AspNetCore.Mvc; +using Sarand.Net.Standard.Base.App; +using Sarand.Net.Standard.Base.Bus; +using Sarand.Net.Standard.Base.Core; +using Sarand.Net.Standard.Base.Email; +using Sarand.Net.Standard.Base.Utils; +using Saxonite.Net.Core.Web.Admin.Consoles; +using Saxonite.Net.Core.Web.Admin.Models; +using Saxonite.Net.Core.Web.Admin.Procs; +using Saxonite.Net.Standard.Base.App; +using Saxonite.Net.Standard.Base.Bus; +using Saxonite.Net.Standard.Base.Helpers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Saxonite.Net.Core.Web.Admin.Controllers +{ + //======================================================================= + // Controller: Organisation - Ajax - Upload + //======================================================================= + partial class OrganisationController + { + //------------------------------------------------------------------- + // Definitions. + + //------------------------------------------------------------------- + // Organisation Uploader process info. + //------------------------------------------------------------------- + private class AjaxResponse_OrganisationUploadInfo : AjaxResponse_Data + { + // Construction. + public AjaxResponse_OrganisationUploadInfo(SaxModel_Admin model) : base(model) { } + } + //------------------------------------------------------------------- + [HttpPost] + [Route("Organisation/Ajax/UploadInfo")] + public IActionResult Ajax_OrganisationUploadInfo(SaxModel_Admin model) + { + String fn = GetType().Name + ".Ajax_OrganisationUploadInfo"; + String messageTitle = Localiser["caption.upload.organisations"]; + AjaxResponse_OrganisationUploadInfo responseData = new AjaxResponse_OrganisationUploadInfo(model); + + // Validate session. + if (!ValidUserSession) + return GetAjaxResult_InvalidSession(responseData); + + // Record disposable items as they are created so all can be disposed when needed. + using (DisposableCollection disposableItems = new DisposableCollection()) + { + + // Process console. + SaxProcessConsole_Admin processConsole = GetProcessConsole_Admin(SessionContext); + if (processConsole == null) + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"])); + + try + { + + // Check whether upload is in progress. + if (processConsole.IsProcessing(SaxProcessConsole_Admin.ProcessType.OrganisationUpload)) + return Json(responseData.SetMessage(messageTitle, Localiser["msg.process.in.progress"])); + + } + catch (Exception exc) + { + SaLogger.Warn(fn + " " + exc.GetType().Name + " initialising organisation uploader."); + return Json(responseData.SetMessage(messageTitle, exc.GetType().Name + " initialising organisation uploader.")); + } + } + + // Complete response. + responseData.Success = true; + + return Json(responseData); + } + + //------------------------------------------------------------------- + // Upload Organisations. + //------------------------------------------------------------------- + private class AjaxResponse_OrganisationUpload : AjaxResponse_Data + { + // Construction. + public AjaxResponse_OrganisationUpload(SaxModel_Admin model) : base(model) { } + } + //------------------------------------------------------------------- + [HttpPost] + [Route("Organisation/Ajax/Upload")] + public IActionResult Ajax_UploadOrganisations(Model_OrganisationUpload model) + { + String fn = GetType().Name + ".Ajax_UploadOrganisations"; + String messageTitle = Localiser["caption.upload.organisations"]; + AjaxResponse_OrganisationUpload responseData = new AjaxResponse_OrganisationUpload(model); + + // Validate session. + if (!ValidUserSession) + return GetAjaxResult_InvalidSession(responseData); + + // Record disposable items as they are created so all can be disposed when needed. + using (DisposableCollection disposableItems = new DisposableCollection()) + { + + // Validate supplied data properties. + if (!model.ValidateProperties(responseData)) + return Json(responseData); + + // Process console. + SaxProcessConsole_Admin processConsole = GetProcessConsole_Admin(SessionContext); + if (processConsole == null) + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"])); + + try + { + + // Check whether upload is in progress. + if (processConsole.IsProcessing(SaxProcessConsole_Admin.ProcessType.OrganisationUpload)) + return Json(responseData.SetMessage(messageTitle, Localiser["msg.process.in.progress"])); + + // Create the upload process. + SaxOrganisationUploader.Config processConfig = new SaxOrganisationUploader.Config(SessionContext, model.OrganisationDataFile[0]); + processConfig.SheetNameOrNumber = model.SheetNameOrNumber; + SaxOrganisationUploader process = new SaxOrganisationUploader(processConfig); + + // Store the process and run. + processConsole.SetProcess(process); + process.RunAsync(); + + } + catch (Exception exc) + { + SaLogger.Warn(fn + " " + exc.GetType().Name + " initialising organisation uploader."); + return Json(responseData.SetMessage(messageTitle, exc.GetType().Name + " initialising organisation uploader.")); + } + } + + // Complete response. + responseData.Success = true; + + return Json(responseData); + } + + //------------------------------------------------------------------- + } +} diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Ajax.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Ajax.cs index 4e50231..9bd94af 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Ajax.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Ajax.cs @@ -5,8 +5,10 @@ using Sarand.Net.Standard.Base.Core; using Sarand.Net.Standard.Base.Dbas; using Sarand.Net.Standard.Base.Utils; using Saxonite.Net.Core.Web.Admin.Models; +using Saxonite.Net.Core.Web.Admin.Procs; using Saxonite.Net.Standard.Base.App; using Saxonite.Net.Standard.Base.Bus; +using Saxonite.Net.Standard.Base.Mgr; using System; using System.Collections.Generic; using System.Collections.Specialized; @@ -126,6 +128,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers public List ContactMethods { get; } = new List(); public List Statuses { get; } = new List(); public String DefaultStatusCode { get; set; } + public List UploadContentTypes { get; set; } } //------------------------------------------------------------------- [HttpPost] @@ -154,6 +157,9 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers responseData.Statuses.Add(SacsSystem.GetStatusCode(status), Localiser.GetStatusCaption(status)); responseData.DefaultStatusCode = SacsSystem.GetStatusCode(SaxOrganisation.DefaultStatus); + // Reference data - Image content types. + responseData.UploadContentTypes = SaxOrganisationUploader.ValidContentTypes; + // Store filter values. SysId filterSportId = model.Filter.OrganisationSportId.ToSysId(); SessionData_Admin.Filter.SportId = SysId.IsValid(filterSportId) ? filterSportId : null; @@ -161,14 +167,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers SessionData_Admin.Filter.RegionId = SysId.IsValid(filterRegionId) ? filterRegionId : null; // Item filter. - SaxOrganisation.Params prmOrganisation = new SaxOrganisation.Params(); - prmOrganisation.OrganisationFilter = model.Filter.Organisation; - prmOrganisation.OrganisationType = model.FilterOrganisationType; - prmOrganisation.OrganisationCategory = model.FilterOrganisationCategory; - prmOrganisation.SportIdsIncluded.AddId(filterSportId); - prmOrganisation.RegionIdsIncluded.AddId(filterRegionId); - prmOrganisation.StatusCat = model.Filter.OrganisationStatusCategoryCode != null ? model.FilterOrganisationStatusCategory : SaxOrganisation.StatusCat.NotDeleted; - prmOrganisation.SortColumns.Add(AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.Name)); + SaxOrganisation.Params prmOrganisation = GetOrganisationListFilter(model, AccountMgr); model.Paging.ItemTotal = (ulong)AccountMgr.GetOrganisationCount(prmOrganisation); model.PopulatePagingTo(prmOrganisation.ResultsPaging); @@ -237,6 +236,27 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers return Json(responseData); } + //------------------------------------------------------------------- + /// + /// Construct Organisation filter params. + /// - Shared with Organisation Exporter process. + /// + /// + /// + public static SaxOrganisation.Params GetOrganisationListFilter(Model_Organisation model, SaxAccountMgr accountMgr) + { + // Item filter. + SaxOrganisation.Params prmOrganisation = new SaxOrganisation.Params(); + prmOrganisation.OrganisationFilter = model.Filter.Organisation; + prmOrganisation.OrganisationType = model.FilterOrganisationType; + prmOrganisation.OrganisationCategory = model.FilterOrganisationCategory; + prmOrganisation.SportIdsIncluded.AddId(model.Filter.OrganisationSportId.ToSysId()); + prmOrganisation.RegionIdsIncluded.AddId(model.Filter.OrganisationRegionId.ToSysId()); + prmOrganisation.StatusCat = model.Filter.OrganisationStatusCategoryCode != null ? model.FilterOrganisationStatusCategory : SaxOrganisation.StatusCat.NotDeleted; + prmOrganisation.SortColumns.Add(accountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.Name)); + return prmOrganisation; + } + //------------------------------------------------------------------- // Get Organisation ajax request. //------------------------------------------------------------------- diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Download.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Download.cs new file mode 100644 index 0000000..143b3eb --- /dev/null +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Download.cs @@ -0,0 +1,79 @@ +using Microsoft.AspNetCore.Mvc; +using Sarand.Net.Standard.Base.App; +using Sarand.Net.Standard.Base.Bus; +using Sarand.Net.Standard.Base.Core; +using Sarand.Net.Standard.Base.Email; +using Sarand.Net.Standard.Base.Helpers; +using Sarand.Net.Standard.Base.Utils; +using Sarand.Net.Standard.Proc.Excel; +using Saxonite.Net.Core.Web.Admin.Consoles; +using Saxonite.Net.Core.Web.Admin.Models; +using Saxonite.Net.Core.Web.Admin.Procs; +using Saxonite.Net.Core.Web.Base.Controllers; +using Saxonite.Net.Standard.Base.App; +using Saxonite.Net.Standard.Base.Bus; +using Saxonite.Net.Standard.Base.Helpers; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +namespace Saxonite.Net.Core.Web.Admin.Controllers +{ + //======================================================================= + // Controller: Organisation - Download + //======================================================================= + partial class OrganisationController + { + //------------------------------------------------------------------- + // Definitions. + + //------------------------------------------------------------------- + // Download Organisations. + //------------------------------------------------------------------- + [HttpPost] + [Route("Organisation/Download")] + public IActionResult Ajax_DownloadOrganisations(Model_OrganisationDownload model) + { + String fn = GetType().Name + ".Ajax_DownloadOrganisations"; + + // Validate session. + if (!ValidUserSession) + return Unauthorized(); + + // Record disposable items as they are created so all can be disposed when needed. + using (DisposableCollection disposableItems = new DisposableCollection()) + { + + // Validate supplied data properties. + if (!model.ValidateProperties()) + return BadRequest(); + + // Export data. + SaxOrganisationExporter.Config exporterConfig = new SaxOrganisationExporter.Config(SessionContext, GetOrganisationListFilter(model, AccountMgr)); + exporterConfig.Format = SaxOrganisationExporter.ExportFormat.Excel_Open; + exporterConfig.IncludeId = StringUtil.ToBool(model.IncludeId); + SaxOrganisationExporter exporter = new SaxOrganisationExporter(exporterConfig); + if (exporter.RunProcess()) + { + // Organisations exported successfully. + byte[] data = exporter.GetDocumentData(); + using (MemoryStream content = new MemoryStream(data)) + { + String contentType = exporter.GetContentType(); + var fileName = "Sportierre-Organisations-" + DateTime.Now.ToString("yyyyMMdd") + ".xlsx"; + return File(content.ToArray(), contentType, fileName); + } + } + else + { + // Problems occurred. + } + } + return NoContent(); + } + + //------------------------------------------------------------------- + } +} diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController.cs index e281398..aa41643 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController.cs @@ -29,6 +29,8 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers public IActionResult Index(String sta) { // Validate session. + if (!ValidUserSession) + AutoLoginUser(); if (!ValidUserSession) return RedirectToAction("", ""); @@ -64,6 +66,8 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers public IActionResult Members(Model_OrganisationMembers model) { // Validate session. + if (!ValidUserSession) + AutoLoginUser(); if (!ValidUserSession) return RedirectToAction("", ""); @@ -90,6 +94,8 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers public IActionResult Associations(Model_OrganisationAssociations model) { // Validate session. + if (!ValidUserSession) + AutoLoginUser(); if (!ValidUserSession) return RedirectToAction("", ""); diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/RegionController.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/RegionController.cs index 01855e4..aa9d86c 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/RegionController.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/RegionController.cs @@ -26,6 +26,8 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers public IActionResult Index() { // Validate session. + if (!ValidUserSession) + AutoLoginUser(); if (!ValidUserSession) return RedirectToAction("", ""); diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/SaxController_Admin.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/SaxController_Admin.cs index 83482b3..58a9620 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/SaxController_Admin.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/SaxController_Admin.cs @@ -6,6 +6,7 @@ using Sarand.Net.Standard.Base.Core; using Sarand.Net.Standard.Base.Helpers; using Sarand.Net.Standard.Base.Utils; using Saxonite.Net.Core.Web.Admin.App; +using Saxonite.Net.Core.Web.Admin.Consoles; using Saxonite.Net.Core.Web.Base.App; using Saxonite.Net.Core.Web.Base.Controllers; using Saxonite.Net.Standard.Base.App; @@ -41,6 +42,15 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers return JsonConvert.DeserializeObject(sessionDataJson, SaxSessionDataJsonSerializerSettings); } + //------------------------------------------------------------------- + // Globals. + //------------------------------------------------------------------- + protected SaxConsoleHelper_Admin ConsoleHelper_Admin { get { return ConsoleHelper as SaxConsoleHelper_Admin; } } + protected SaxProcessConsole_Admin GetProcessConsole_Admin(SaxSessionContext sessionContext) + { + return ConsoleHelper_Admin?.GetProcessConsole(sessionContext) as SaxProcessConsole_Admin; + } + //------------------------------------------------------------------- // User Session. //------------------------------------------------------------------- @@ -50,54 +60,60 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers /// - Open user session - store details against Sesion Data. /// - Set 'Keep logged in' cookie if required. /// - protected bool SignUserIn(SaxUserAccount userAccount, List userRoles, bool keepLoggedIn, ref String errorMessage) + protected bool SignUserIn(SaxUserAccount userAccount, bool keepLoggedIn, ref String errorMessage) { String fn = GetType().Name + ".SignUserIn"; - // Validate User Account, just in case - should have been done before arriving here! - if (userAccount == null) + // Record disposable items as they are created so all can be disposed when needed. + using (DisposableCollection disposableItems = new DisposableCollection()) { - SaLogger.Error(fn + " Unable to sign account in - null user account."); - errorMessage = Localiser["security.msg.signin.invalid"]; - return false; + List userRoles = AccountMgr.GetRolesForUser(userAccount.Id); + disposableItems.AddRange(userRoles); + + // Validate User Account, just in case - should have been done before arriving here! + if (userAccount == null) + { + SaLogger.Error(fn + " Unable to sign account in - null user account."); + errorMessage = Localiser["security.msg.signin.invalid"]; + return false; + } + if (!userAccount.IsStatusActive) + { + SaLogger.Error(fn + " Unable to sign account in - user account is not active. Email: " + userAccount.EmailAddress); + errorMessage = Localiser["security.msg.signin.invalid"]; + return false; + } + + // 'Keep logged in' can be switched off by the application configuration. + keepLoggedIn = App.AppConfig.Security.SignIn.KeepLoggedIn.Allowed && keepLoggedIn; + + // Log user in - Admin details stored against System Admin role. + SaxUserRole userRole = userRoles.Find(item => item.UserRole == Sax.UserRole.SystemAdmin); + if (userRole == null) + { + SaLogger.Warn(fn + " System Admin role not found for " + userAccount.EmailAddress); + errorMessage = Localiser["security.msg.signin.invalid"]; + return false; + } + userRole.KeepLoggedIn = keepLoggedIn; + userRole.LastLoginAt = DateTime.Now; + userRole.InvalidLoginCount = 0; + userRole.LastInvalidLoginAt = DateTime.MinValue; + if (!AccountMgr.UpdateUserRole(userRole, DbUpdateInfo)) + { + SaLogger.Warn(fn + " Failed to update Admin user role for " + userAccount.EmailAddress + ": " + AccountMgr.ErrorStateMessage); + errorMessage = Localiser["security.msg.signin.invalid"]; + return false; + } + SaLogger.Info(fn + " User session opened for " + userAccount.EmailAddress, true); + + // Create session. + OpenUserSession(SessionContext, userAccount, userRoles); + + // Send the 'Keep Logged In' cookie if requested. + if (userRole.KeepLoggedIn) + SetKeepLoggedInCookie(); } - if (!userAccount.IsStatusActive) - { - SaLogger.Error(fn + " Unable to sign account in - user account is not active. Email: " + userAccount.EmailAddress); - errorMessage = Localiser["security.msg.signin.invalid"]; - return false; - } - - // 'Keep logged in' can be switched off by the application configuration. - keepLoggedIn = App.AppConfig.Security.SignIn.KeepLoggedIn.Allowed && keepLoggedIn; - - // Log user in - Admin details stored against System Admin role. - SaxUserRole userRole = userRoles.Find(item => item.UserRole == Sax.UserRole.SystemAdmin); - if (userRole == null) - { - SaLogger.Warn(fn + " System Admin role not found for " + userAccount.EmailAddress); - errorMessage = Localiser["security.msg.signin.invalid"]; - return false; - } - userRole.KeepLoggedIn = keepLoggedIn; - userRole.LastLoginAt = DateTime.Now; - userRole.InvalidLoginCount = 0; - userRole.LastInvalidLoginAt = DateTime.MinValue; - if (!AccountMgr.UpdateUserRole(userRole, DbUpdateInfo)) - { - SaLogger.Warn(fn + " Failed to update Admin user role for " + userAccount.EmailAddress + ": " + AccountMgr.ErrorStateMessage); - errorMessage = Localiser["security.msg.signin.invalid"]; - return false; - } - SaLogger.Info(fn + " User session opened for " + userAccount.EmailAddress, true); - - // Create session. - OpenUserSession(SessionContext, userAccount, userRoles); - - // Send the 'Keep Logged In' cookie if requested. - if (userRole.KeepLoggedIn) - SetKeepLoggedInCookie(); - return true; } @@ -121,22 +137,50 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers } //------------------------------------------------------------------- - // IMage utilities. + // Keep Logged In. //------------------------------------------------------------------- /// - /// Construct URL to display Organisation logo. - /// - Option to allow for 'Upload' image if active. + /// Check for 'Keep Logged In' cookie and auto-login user if details are valid. /// - protected String GetOrganisationLogoLink(SysId organisationId, bool uploadImage) + /// + protected bool AutoLoginUser() { - String imageLink = "Image/Organisation/Logo" + (uploadImage ? "/Upload" : "") + "?"; - NameValueCollection parameters = new NameValueCollection(); - parameters.Add(ImageController.ParamName_OrganisationId, SysId.ToString(organisationId)); - return imageLink + ImageController.ParamName_OrganisationLogoEncrypted + "=" + EncryptSessionParams(parameters); + String fn = "AutoLoginUser"; + + // NB Only allowed if there isn't a current user session. + if (ValidUserSession) return true; + if (!AppConfig.Security.SignIn.KeepLoggedIn.Allowed) return false; + + // Extract cookie details. + String encryptedParams = GetRequestCookie(KeepLoggedIn.CookieName); + KeepLoggedInParams keepLoggedInParams = ExtractKeepLoggedInParams(encryptedParams); + String logSuffix = keepLoggedInParams.GetValidParams().Concat("", " ", "."); + if (SysId.IsValid(keepLoggedInParams.UserAccountId)) + { + using (SaxUserAccount userAccount = AccountMgr.GetUserAccountById(keepLoggedInParams.UserAccountId)) + { + if (userAccount != null) + { + if (userAccount.IsStatusActive) + { + String errorMessage = ""; + if (SignUserIn(userAccount, true, ref errorMessage)) + { + // Successful login. + SaLogger.Info(fn + " Keep Logged In cookie found." + logSuffix); + return true; + } + else SaLogger.Warn(fn + " Keep Logged In cookie found but failed to sign user in: " + errorMessage + "." + logSuffix); + } + else SaLogger.Warn(fn + " Keep Logged In cookie found but UserAccount is not active." + logSuffix); + } + else SaLogger.Warn(fn + " Keep Logged In cookie found but UserAccount not found." + logSuffix); + } + } + ClearKeepLoggedInCookie(); + return false; } - //------------------------------------------------------------------- - // Keep Logged In. //------------------------------------------------------------------- /// Keep-Logged-In cookie. protected void SetKeepLoggedInCookie() @@ -164,8 +208,8 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers // - IV Must be regenerated if key is changed: uncomment 'Generated' variable and copy resulting value to IV variable. public static class KeepLoggedIn { - public static String CookieName = "SXKLI"; - public static String Base64Key = SacsEncryptionHelper.EncodePlainKey("<=Saxonite^{kEEp+1Ogg3d-IN}~32*>", SacsEncryptionHelper.KeyLength); + public static String CookieName = "SXKLI"; + public static String Base64Key = SacsEncryptionHelper.EncodePlainKey("<=Saxonite^{kEEp+1Ogg3d-IN}~32*>", SacsEncryptionHelper.KeyLength); public static String Base64IV = "YOX10FIPp1zM1pg5U6fW7A=="; private static String Base64IV_Generated = SacsEncryptionHelper.InitSymmetricEncryptionIV(KeepLoggedIn.Base64Key); } @@ -205,12 +249,27 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers return keepLoggedInParams; } + //------------------------------------------------------------------- + // Image utilities. + //------------------------------------------------------------------- + /// + /// Construct URL to display Organisation logo. + /// - Option to allow for 'Upload' image if active. + /// + protected String GetOrganisationLogoLink(SysId organisationId, bool uploadImage) + { + String imageLink = "Image/Organisation/Logo" + (uploadImage ? "/Upload" : "") + "?"; + NameValueCollection parameters = new NameValueCollection(); + parameters.Add(ImageController.ParamName_OrganisationId, SysId.ToString(organisationId)); + return imageLink + ImageController.ParamName_OrganisationLogoEncrypted + "=" + EncryptSessionParams(parameters); + } + //------------------------------------------------------------------- // Parameter encryption, eg. for images. //------------------------------------------------------------------- // Encryption key is the same for all users each time the application is started, // but a different IV value is generated for each session so that the links/parameters cannot be shared. - public static String Base64Key_AdminApp { get; } = SacsEncryptionHelper.EncodePlainKey("Saxonite&Admin#Encryption-"+DateTime.Today.Format("ddMMyy"), SacsEncryptionHelper.KeyLength); + public static String Base64Key_AdminApp { get; } = SacsEncryptionHelper.EncodePlainKey("Saxonite&Admin#Encryption-" + DateTime.Today.Format("ddMMyy"), SacsEncryptionHelper.KeyLength); protected String EncryptSessionParams(NameValueCollection parameters) { return HttpUtility.UrlEncode(EncryptParameters(parameters, SessionData_Admin.Base64IV_AdminApp, Base64Key_AdminApp)); diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/SecurityController-Ajax.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/SecurityController-Ajax.cs index f289fae..e671fed 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/SecurityController-Ajax.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/SecurityController-Ajax.cs @@ -56,7 +56,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers return Json(responseData); String errorMessage = ""; - if (!SignUserIn(userAccount, AccountMgr.GetRolesForUser(userAccount.Id), model.KeepMeSignedIn, ref errorMessage)) + if (!SignUserIn(userAccount, model.KeepMeSignedIn, ref errorMessage)) { return Json(responseData.SetControlError(nameof(model.EmailAddress), errorMessage)); } diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/SportController.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/SportController.cs index 152dbc5..38f8cf1 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/SportController.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/SportController.cs @@ -26,6 +26,8 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers public IActionResult Index() { // Validate session. + if (!ValidUserSession) + AutoLoginUser(); if (!ValidUserSession) return RedirectToAction("", ""); diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/SystemController-Ajax-Database.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/SystemController-Ajax-Database.cs index cf0a5c9..dbd1077 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/SystemController-Ajax-Database.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/SystemController-Ajax-Database.cs @@ -23,7 +23,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers //======================================================================= // System - Ajax - Database controller //======================================================================= - partial class SystemController : SaDbProcess_CompareDatabaseSchemas.IProcessNotifier + partial class SystemController { //------------------------------------------------------------------- // Database data. @@ -169,14 +169,14 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers SaDbProcess_ConvertDbSchema.Config cfgSchemaConvert = new SaDbProcess_ConvertDbSchema.Config(); SaDbProcess_ConvertDbSchema procSchemaConvert = new SaDbProcess_ConvertDbSchema(cfgSchemaConvert); List tables = procSchemaConvert.ConvertXmlToTables(xmlSchema); - if (procSchemaConvert.HasProcessError()) - return Json(responseData.SetMessage(messageTitle, "Failed to convert XML schema: " + procSchemaConvert.GetProcessError())); + if (procSchemaConvert.HasErrorMessage()) + return Json(responseData.SetMessage(messageTitle, "Failed to convert XML schema: " + procSchemaConvert.GetErrorMessage())); SaDbProcess_CompareDatabaseSchemas.Config cfgSchemaCompare = new SaDbProcess_CompareDatabaseSchemas.Config(tables, db); cfgSchemaCompare.HideIdenticalObjects = true; cfgSchemaCompare.IgnorePosition = true; SaDbProcess_CompareDatabaseSchemas procSchemaCompare = new SaDbProcess_CompareDatabaseSchemas(cfgSchemaCompare); - procSchemaCompare.ProcessNotifier = this; + procSchemaCompare.OnSchemaComparisonItemAdded += OnSchemaComparisonItemAdded; procSchemaCompare.RunProcess(); List schemaComparisonItems = procSchemaCompare.GetSchemaComparisonItems(); @@ -200,17 +200,10 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers return Json(responseData); } - public void OnStartProcess() - { - } - public void SetProgressInfo(List progressInfo) - { - } - public void OnEndProcess() - { - } - public void OnSchemaComparisonItemAdded(SaDbProcess_CompareDatabaseSchemas proc) + // Processing events. + public void OnSchemaComparisonItemAdded(object sender, SaDbProcess_CompareDatabaseSchemas.SchemaComparisonItemAddedEventArgs e) { + SaDbProcess_CompareDatabaseSchemas proc = sender as SaDbProcess_CompareDatabaseSchemas; } //------------------------------------------------------------------- diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/SystemController.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/SystemController.cs index 2a0043f..83d298a 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/SystemController.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/SystemController.cs @@ -45,6 +45,8 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers public IActionResult Database(Model_System model) { // Validate session. + if (!ValidUserSession) + AutoLoginUser(); if (!ValidUserSession) return RedirectToAction("", ""); @@ -60,6 +62,8 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers public IActionResult Logs(Model_System model) { // Validate session. + if (!ValidUserSession) + AutoLoginUser(); if (!ValidUserSession) return RedirectToAction("", ""); diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/UserController.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/UserController.cs index bdfd8f6..4d72925 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/UserController.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/UserController.cs @@ -28,6 +28,8 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers public IActionResult Index(String sta) { // Validate session. + if (!ValidUserSession) + AutoLoginUser(); if (!ValidUserSession) return RedirectToAction("", ""); diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/UsersOnlineController.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/UsersOnlineController.cs index 70d6657..2376ed5 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/UsersOnlineController.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/UsersOnlineController.cs @@ -28,6 +28,8 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers public IActionResult Index(String sta) { // Validate session. + if (!ValidUserSession) + AutoLoginUser(); if (!ValidUserSession) return RedirectToAction("", ""); diff --git a/SaxoniteCoreWebAdmin/Admin/Models/ModelData_Address.cs b/SaxoniteCoreWebAdmin/Admin/Models/ModelData_Address.cs index 51d94c7..25a7323 100644 --- a/SaxoniteCoreWebAdmin/Admin/Models/ModelData_Address.cs +++ b/SaxoniteCoreWebAdmin/Admin/Models/ModelData_Address.cs @@ -1,4 +1,5 @@ -using Sarand.Net.Standard.Base.App; +using Sacs.Web.Components; +using Sarand.Net.Standard.Base.App; using Sarand.Net.Standard.Base.Bus; using Sarand.Net.Standard.Base.Core; using Sarand.Net.Standard.Base.Helpers; @@ -63,7 +64,7 @@ namespace Saxonite.Net.Core.Web.Admin.Models // Validation. //------------------------------------------------------------------- // Address. - public bool ValidateAddressData(SaxModel_Admin model, SaxController_Base.AjaxResponse_Data responseData) + public bool ValidateAddressData(SaxModel_Admin model, SaController.AjaxResponse_Data responseData) { // Are any properties populated. bool populated = IsPopulated(); diff --git a/SaxoniteCoreWebAdmin/Admin/Models/Model_OrganisationDownload.cs b/SaxoniteCoreWebAdmin/Admin/Models/Model_OrganisationDownload.cs new file mode 100644 index 0000000..81799dc --- /dev/null +++ b/SaxoniteCoreWebAdmin/Admin/Models/Model_OrganisationDownload.cs @@ -0,0 +1,50 @@ +using Microsoft.AspNetCore.Http; +using Sarand.Net.Standard.Base.App; +using Sarand.Net.Standard.Base.Bus; +using Sarand.Net.Standard.Base.Core; +using Sarand.Net.Standard.Base.Utils; +using Sarand.Net.Standard.Proc.Excel; +using Saxonite.Base.App; +using Saxonite.Net.Core.Web.Base.App; +using Saxonite.Net.Core.Web.Base.Controllers; +using Saxonite.Net.Core.Web.Base.Models; +using Saxonite.Net.Standard.Base.App; +using Saxonite.Net.Standard.Base.Bus; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; + +namespace Saxonite.Net.Core.Web.Admin.Models +{ + //======================================================================= + /// + /// Model for Organisation Download. + /// + public class Model_OrganisationDownload : Model_Organisation + { + //------------------------------------------------------------------- + // Construction. + public Model_OrganisationDownload(SaxLocaliser localiser) : + base(localiser) + { + } + + //------------------------------------------------------------------- + // Properties. + public String IncludeId { get; set; } + + //------------------------------------------------------------------- + // Validation. + //------------------------------------------------------------------- + public bool ValidateProperties() + { + return true; + } + + //------------------------------------------------------------------- + } + //======================================================================= +} diff --git a/SaxoniteCoreWebAdmin/Admin/Models/Model_OrganisationUpload.cs b/SaxoniteCoreWebAdmin/Admin/Models/Model_OrganisationUpload.cs new file mode 100644 index 0000000..a03fadb --- /dev/null +++ b/SaxoniteCoreWebAdmin/Admin/Models/Model_OrganisationUpload.cs @@ -0,0 +1,72 @@ +using Microsoft.AspNetCore.Http; +using Sarand.Net.Standard.Base.App; +using Sarand.Net.Standard.Base.Bus; +using Sarand.Net.Standard.Base.Core; +using Sarand.Net.Standard.Base.Utils; +using Sarand.Net.Standard.Proc.Excel; +using Saxonite.Base.App; +using Saxonite.Net.Core.Web.Base.App; +using Saxonite.Net.Core.Web.Base.Controllers; +using Saxonite.Net.Core.Web.Base.Models; +using Saxonite.Net.Standard.Base.App; +using Saxonite.Net.Standard.Base.Bus; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; + +namespace Saxonite.Net.Core.Web.Admin.Models +{ + //======================================================================= + /// + /// Model for Organisation Upload. + /// + public class Model_OrganisationUpload : SaxModel_Admin + { + //------------------------------------------------------------------- + // Construction. + public Model_OrganisationUpload(SaxLocaliser localiser) : + base(localiser) + { + } + + //------------------------------------------------------------------- + // Organisation data files - only one allowed per request. + public List OrganisationDataFile { get; set; } + // Data file properties. + public String SheetNameOrNumber { get; set; } + + //------------------------------------------------------------------- + // Validation. + //------------------------------------------------------------------- + public bool ValidateProperties(SaxController_Base.AjaxResponse_Data responseData) + { + + // Data file. + { + String controlName = nameof(OrganisationDataFile); + // A single data file must be supplied. + if (OrganisationDataFile == null || !OrganisationDataFile.Any()) + responseData.SetControlError(controlName, Localiser["msg.error.data.file.missing"]); + else if (OrganisationDataFile != null && OrganisationDataFile.Count() > 1) + responseData.SetControlError(controlName, Localiser["msg.error.data.file.multiple"]); + } + + // Sheet name or number. + { + String controlName = nameof(SheetNameOrNumber); + if (StringUtil.IsBlank(SheetNameOrNumber)) + responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]); + else if (!ExcelHelper.IsValidSheetName(SheetNameOrNumber)) + responseData.SetControlError(controlName, Localiser["msg.error.sheet.name.invalid"]); + } + + return !responseData.HasControlErrors(); + } + + //------------------------------------------------------------------- + } + //======================================================================= +} diff --git a/SaxoniteCoreWebAdmin/Admin/Procs/SaxOrganisationExporter.cs b/SaxoniteCoreWebAdmin/Admin/Procs/SaxOrganisationExporter.cs new file mode 100644 index 0000000..8bec81f --- /dev/null +++ b/SaxoniteCoreWebAdmin/Admin/Procs/SaxOrganisationExporter.cs @@ -0,0 +1,198 @@ +using Microsoft.AspNetCore.Http; +using Saxonite.Net.Core.Web.Base.Procs.Data; +using System.Collections.Generic; +using System; +using Saxonite.Net.Core.Web.Admin.Models; +using System.Collections; +using Sarand.Net.Standard.Base.Helpers; +using Sarand.Net.Standard.Proc.Excel; +using Sarand.Net.Standard.Base.App; +using System.IO; +using DocumentFormat.OpenXml.EMMA; +using Saxonite.Net.Standard.Base.Bus; +using Saxonite.Net.Core.Web.Admin.Controllers; +using Saxonite.Net.Standard.Base.App; +using Sarand.Net.Standard.Base.Core; +using Sarand.Net.Standard.Base.Bus; +using Sarand.Net.Standard.Base.Dbas; + +namespace Saxonite.Net.Core.Web.Admin.Procs +{ + //======================================================================= + public class SaxOrganisationExporter : SaxProcess_Admin + { + //------------------------------------------------------------------- + // Definitions. + + public enum ExportFormat + { + Unknown, + Excel_Open, /* xlxs */ + } + + // Process configuration. + public new class Config : SaxProcess_Admin.Config + { + public Config(SaxSessionContext sessionContext, SaxOrganisation.Params prmOrganisation) : base(sessionContext) + { + PrmOrganisation = prmOrganisation; + } + // Properties. + public SaxOrganisation.Params PrmOrganisation { get; } + public ExportFormat Format { get; set; } + public bool IncludeId { get; set; } + public ushort MaxRows { get; set; } = 2000; + } + + // Processing control data. + private class ProcessControlData + { + public Config Config { get; set; } + public SaMsSpreadsheet Spreadsheet { get; set; } + } + + //------------------------------------------------------------------- + // Data members. + private ProcessControlData control { get; } = new ProcessControlData(); + + //------------------------------------------------------------------- + // Construction. + public SaxOrganisationExporter(Config config) : base(config) + { + control.Config = config; + } + protected override void Dispose(bool disposing) + { + if (disposing) + { + // Spreadsheet. + DisposeSpreadsheet(); + } + base.Dispose(disposing); + } + private void DisposeSpreadsheet() + { + if (control.Spreadsheet != null) + control.Spreadsheet.Dispose(); + control.Spreadsheet = null; + } + + //------------------------------------------------------------------- + // Properties. + public override string ProcessDescription => "Organisation Export"; + public byte[] GetDocumentData() + { + switch (control.Config.Format) + { + case ExportFormat.Excel_Open: + return control.Spreadsheet.GetDocumentData(); + } + return null; + } + public String GetContentType() + { + switch (control.Config.Format) + { + case ExportFormat.Excel_Open: return SacsContentTypeHelper.ContentType_Excel_Open; + } + return ""; + } + + //------------------------------------------------------------------- + // Run the process. + protected override bool Run() + { + String fn = GetType().Name + ".Run"; + + // Verify configuration. + if (control.Config == null) + { + AddErrorMessage("Configuration must be supplied."); + SaLogger.Warn(fn + " " + GetErrorMessage()); + return OK; + } + if (control.Config.PrmOrganisation == null) + { + AddErrorMessage("Organisation params must be supplied."); + SaLogger.Warn(fn + " " + GetErrorMessage()); + return OK; + } + else if (control.Config.Format != ExportFormat.Excel_Open) + { + AddErrorMessage("Unsupported export format: " + control.Config.Format.ToString() + "."); + SaLogger.Warn(fn + " " + GetErrorMessage()); + } + AddInfoMessage("Export format: " + control.Config.Format.ToString()); + + if (OK) ExportOrganisations(); + + return OK; + } + + //------------------------------------------------------------------- + private bool ExportOrganisations() + { + if (!OK) return false; + + DisposeSpreadsheet(); + control.Spreadsheet = new SaMsSpreadsheet_POI(); + control.Spreadsheet.CreateWorkbook(); + control.Spreadsheet.WorksheetName = "Organisations"; + control.Spreadsheet.ApplyDefaultStyle(GetExceldefaultWorkbookStyle()); + + // Columns. + ColumnDfn colId = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.Id); + colId.Alignment = ColumnAlignment.Center; + + // Columns to output. + List columnsExport = new List(); + if (control.Config.IncludeId) + columnsExport.Add(colId); + columnsExport.Add(AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.Name)); + columnsExport.Add(AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.OrganisationCategory_Desc)); + columnsExport.Add(AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.OrganisationType_Desc)); + columnsExport.Add(AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.Sport_Name)); + columnsExport.Add(AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.Region_Name)); + columnsExport.Add(AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.User_Count)); + columnsExport.Add(AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.Member_Count)); + + // Initialise spreadsheet column headings and formats etc + SaMsExcelColumnHeading_Config configHeading = GetExcelColumnHeadingConfig(); + control.Spreadsheet.SetColumnHeadings(columnsExport, configHeading); + + // Columns to query - ensure Id is in the list. + List columnsQuery = new List(columnsExport); + if (!columnsQuery.Exists(item => item.Id == (int)SaxOrganisation.Column.Id)) + columnsQuery.Add(colId); + + // Organisation params. + control.Config.PrmOrganisation.ResultsPaging.PageNo = 1; + control.Config.PrmOrganisation.ResultsPaging.PageLength = (ushort)Math.Min(ushort.MaxValue, Math.Max(ushort.MinValue, control.Config.MaxRows)); + + // Query Organisations. + uint dataRowPos = configHeading.FirstDataRowPos - 1; + using (DbResultSet resultSet = AccountMgr.ExecuteOrganisationQuery(columnsQuery, control.Config.PrmOrganisation)) + { + while (resultSet != null && resultSet.HasRows && resultSet.NextRecord()) + { + dataRowPos++; + NamedValue.Map values = resultSet.GetRecordValues(); + SysId organisationId = new SysId(values.GetLong(colId.DataName)); + int colIdx = -1; + foreach (ColumnDfn col in columnsExport) + { + colIdx++; + uint dataColPos = (uint)(configHeading.FirstHeadingColPos + colIdx); + DataValue value = AccountMgr.GetOrganisationDataValue(col, values); + if (col.ShowZeroValues || !col.IsNumeric || value.ToNumeric() != 0.0) + control.Spreadsheet.SetCellValue(dataColPos, dataRowPos, value); + } + } + } + return OK; + } + + //------------------------------------------------------------------- + } + //======================================================================= +} diff --git a/SaxoniteCoreWebAdmin/Admin/Procs/SaxOrganisationUploader.cs b/SaxoniteCoreWebAdmin/Admin/Procs/SaxOrganisationUploader.cs new file mode 100644 index 0000000..7ad103e --- /dev/null +++ b/SaxoniteCoreWebAdmin/Admin/Procs/SaxOrganisationUploader.cs @@ -0,0 +1,259 @@ +using Microsoft.AspNetCore.Http; +using Sarand.Net.Standard.Base.App; +using Sarand.Net.Standard.Base.Helpers; +using Sarand.Net.Standard.Base.Utils; +using Sarand.Net.Standard.Proc.Excel; +using Sarand.Net.Standard.Proc.Procs; +using Saxonite.Net.Core.Web.Base.Procs.Data; +using Saxonite.Net.Standard.Base.App; +using Saxonite.Net.Standard.Base.Procs; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; + +namespace Saxonite.Net.Core.Web.Admin.Procs +{ + //======================================================================= + public class SaxOrganisationUploader : SaxProcess_Admin + { + //------------------------------------------------------------------- + // Definitions. + + // Process configuration. + public new class Config : SaxProcess_Admin.Config + { + public Config(SaxSessionContext sessionContext, IFormFile dataFile) : base(sessionContext) + { + this.DataFile = new UploadFileData(dataFile); + } + // Properties. + public UploadFileData DataFile { get; } + public String SheetNameOrNumber { get; set; } + public uint HeaderRowPos { get; set; } + public bool ValidateOnly { get; set; } + public bool AllowCreateOrganisation { get; set; } + public bool RequireOrganisationIdForUpdate { get; set; } + } + + // Supported content types. + private static HashSet validContentTypes { get; } = new HashSet(StringComparer.OrdinalIgnoreCase) { + SacsContentTypeHelper.ContentType_Excel_Legacy, /* xls */ + SacsContentTypeHelper.ContentType_Excel_Open, /* xlsx */ + SacsContentTypeHelper.ContentType_Excel_MacroEnabled, /* xlsm */ + SacsContentTypeHelper.ContentType_Excel_BinaryMacroEnabled /* xlsb */ + }; + /// Allowed content types. + public static List ValidContentTypes { get { return new List(validContentTypes); } } + + //------------------------------------------------------------------- + // Organisation data profile. + private enum OrganisationProperty + { + // Property columns. + OrganisationId, OrganisationName, Sport, Region, + // Address. + AddressLine1, AddressLine2, AddressTown, AddressPostcode, + // Contact personal. + ContactName, ContactRole, ContactEmail, + // Contact. + ContactPhone, ContactWeb, ContactMap, ContactWiki, ContactFacebook, ContactInstagram, ContactTwitter, + } + private class OrganisationPropertyData + { + public OrganisationPropertyData(OrganisationProperty property, String columnHeaders) + { + this.Property = property; + foreach (String value in Regex.Split(columnHeaders, "[\\s,]+", RegexOptions.None)) + if (!StringUtil.IsBlank(value)) this.ColumnHeaders.Add(value); + } + public OrganisationProperty Property { get; } + public HashSet ColumnHeaders { get; } = new HashSet(StringComparer.OrdinalIgnoreCase); + public uint ColumnPos { get; set; } + public bool IsValid { get { return ColumnPos > 0; } } + } + private class OrganisationDataProfile + { + public OrganisationDataProfile() + { + // Property columns. + AddProperty(OrganisationProperty.OrganisationId, "Id, Organisation Id"); + AddProperty(OrganisationProperty.OrganisationName, "Name, Organisation Name"); + AddProperty(OrganisationProperty.Sport, "Sport"); + AddProperty(OrganisationProperty.Region, "Region"); + // Address. + AddProperty(OrganisationProperty.AddressLine1, "Address Line 1, Line 1, Street"); + AddProperty(OrganisationProperty.AddressLine2, "Address Line 2, Line 2"); + AddProperty(OrganisationProperty.AddressTown, "Town,City,Address Town,Address City"); + AddProperty(OrganisationProperty.AddressPostcode, "Postcode"); + // Contact personal. + AddProperty(OrganisationProperty.ContactName, "Contact Name"); + AddProperty(OrganisationProperty.ContactRole, "Contact Role"); + AddProperty(OrganisationProperty.ContactEmail, "Contatc Email"); + // Contact. + AddProperty(OrganisationProperty.ContactPhone, "Phone"); + AddProperty(OrganisationProperty.ContactWeb, "Web, Web Link, Website"); + AddProperty(OrganisationProperty.ContactMap, "Web Map, Map Link"); + AddProperty(OrganisationProperty.ContactWiki, "Wiki, Wikipedia, Wiki Link"); + AddProperty(OrganisationProperty.ContactFacebook, "Facebook"); + AddProperty(OrganisationProperty.ContactInstagram, "Instagram"); + AddProperty(OrganisationProperty.ContactTwitter, "X, Twitter"); + } + public Dictionary Properties { get; } = new Dictionary(); + private void AddProperty(OrganisationProperty property, String columnHeaders) + { + Properties.Add(property, new OrganisationPropertyData(property, columnHeaders)); + } + public bool IsValidProperty(OrganisationProperty property) + { + OrganisationPropertyData propertyData = Properties[property]; + return propertyData != null && propertyData.IsValid; + } + } + + // Processing control data. + private class ProcessControlData + { + public Config config { get; set; } + public SaMsSpreadsheet spreadsheet { get; set; } + public OrganisationDataProfile dataProfile { get; } = new OrganisationDataProfile(); + } + + //------------------------------------------------------------------- + // Data members. + private ProcessControlData control { get; } = new ProcessControlData(); + + //------------------------------------------------------------------- + // Construction. + public SaxOrganisationUploader(Config config) : base(config) + { + control.config = config; + } + + //------------------------------------------------------------------- + // Properties. + + public override string ProcessDescription => "Organisation Upload"; + + //------------------------------------------------------------------- + // Run the process. + protected override bool Run() + { + String fn = GetType().Name + ".Run"; + + // Verify file. + if (control.config.DataFile == null) + { + AddErrorMessage("Data file must be supplied."); + SaLogger.Warn(fn + " " + GetErrorMessage()); + return OK; + } + else if (!validContentTypes.Contains(control.config.DataFile.ContentType)) + { + AddErrorMessage("Unsupported content type: " + control.config.DataFile.ContentType + "."); + SaLogger.Warn(fn + " " + GetErrorMessage()); + } + AddInfoMessage("Data file: " + control.config.DataFile.FileName); + AddInfoMessage("Sheet: " + control.config.SheetNameOrNumber); + + try + { + if (OK) + { + // Open spreadsheet. + using (SaMsSpreadsheet_POI spreadsheet = new SaMsSpreadsheet_POI()) + using (Stream stream = new MemoryStream()) + { + stream.Write(control.config.DataFile.FileData, 0, (int)control.config.DataFile.Length); + if (!spreadsheet.OpenWorkbook(stream)) + { + // Failed to open workbook. + String msg = "Failed to open workbook - " + spreadsheet.ErrorStateMessage; + AddErrorMessage(msg); + SaLogger.Warn(fn + " " + msg); + } + if (OK && !spreadsheet.OpenWorksheet(1)) + { + // Failed to open workbook. + String msg = "Failed to open worksheet - " + spreadsheet.ErrorStateMessage; + AddErrorMessage(msg); + SaLogger.Warn(fn + " " + msg); + } + if (OK) + { + control.spreadsheet = spreadsheet; + PopulateDataProfile(); + } + if (OK) + { + uint firstRowPos = spreadsheet.FirstRowPos; + for (uint rowPos = firstRowPos; rowPos <= firstRowPos; rowPos++) + { + for (uint colPos = 1; colPos <= 5; colPos++) + { + SaLogger.Info("Row: " + rowPos + ", colPos: " + colPos + ": '" + spreadsheet.GetCellText(colPos, rowPos) + "'"); + } + } + } + // Clean-up. + control.spreadsheet = null; + } + } + } + catch (Exception exc) + { + String msg = exc.GetType().Name + " uploading organisation data."; + AddErrorMessage(msg); + SaLogger.Warn(fn + " " + msg + " " + exc.Message + ExceptionUtil.GetStackEntries(exc, 6).Concat("", "\n\t")); + } + + return OK; + } + + //------------------------------------------------------------------- + private bool PopulateDataProfile() + { + String fn = GetType().Name + ".PopulateDataProfile"; + + // Identify header row. + uint headerRowPos = control.config.HeaderRowPos; + if (headerRowPos < 1) + headerRowPos = control.spreadsheet.FirstRowPos; + + // Column headings. + List columnHeadings = new List(); + for (uint colPos = 1; colPos <= 100; colPos++) + { + columnHeadings.Add(control.spreadsheet.GetCellText(colPos, headerRowPos)); + } + SaLogger.Info(fn + " Column headings: " + columnHeadings.Concat(", ")); + + // Populate header columns in profile. + foreach (OrganisationPropertyData propertyData in control.dataProfile.Properties.Values) + { + for (uint colPos = 1; colPos <= columnHeadings.Count; colPos++) + { + String columnHeading = columnHeadings[(int)(colPos - 1)]; + if (propertyData.ColumnHeaders.ContainsCI(columnHeading)) + { + if (control.dataProfile.Properties.Values.ToList().Exists(item => item.ColumnPos == colPos)) + AddWarningMessage("Multiple properties associated with column '" + columnHeading + "'."); + propertyData.ColumnPos = colPos; + break; + } + } + } + + if (!control.dataProfile.IsValidProperty(OrganisationProperty.OrganisationId) && !control.dataProfile.IsValidProperty(OrganisationProperty.OrganisationName)) + return AddErrorMessage("Organisation Id or Name column must be present"); + + return OK; + } + + //------------------------------------------------------------------- + } + //======================================================================= +} diff --git a/SaxoniteCoreWebAdmin/Admin/Procs/SaxProcess_Admin.cs b/SaxoniteCoreWebAdmin/Admin/Procs/SaxProcess_Admin.cs new file mode 100644 index 0000000..2f54a89 --- /dev/null +++ b/SaxoniteCoreWebAdmin/Admin/Procs/SaxProcess_Admin.cs @@ -0,0 +1,64 @@ +using Sarand.Net.Standard.Proc.Excel; +using Sarand.Net.Standard.Proc.Procs; +using Saxonite.Net.Standard.Base.App; +using Saxonite.Net.Standard.Base.Procs; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Saxonite.Net.Core.Web.Admin.Procs +{ + //======================================================================= + public abstract class SaxProcess_Admin : SaxProcess_Base + { + //------------------------------------------------------------------- + // Definitions. + public new class Config : SaxProcess_Base.Config + { + protected Config(SaxSessionContext sessionContext) : base(sessionContext) + { } + } + + //------------------------------------------------------------------- + // Data members. + private Config config; + + //------------------------------------------------------------------- + // Construction. + protected SaxProcess_Admin(Config config) : base(config) + { + this.config = config; + } + + //------------------------------------------------------------------- + // Excel Spreadsheet utilities. + //------------------------------------------------------------------- + protected SaMsExcelColumnHeading_Config GetExcelColumnHeadingConfig() + { + SaMsExcelColumnHeading_Config configHeading = new SaMsExcelColumnHeading_Config(); + configHeading.FontAttr.Name = "Arial"; + configHeading.FontAttr.HeightInPoints = 10; + configHeading.FontAttr.ColourIndex = SaXlColourIndex.DarkBlue; + configHeading.FontAttr.Bold = true; + configHeading.HeadingTextWrap = true; + configHeading.ForegroundColourIndex = SaXlColourIndex.LightYellow; + configHeading.BorderWeight = SaXlBorderWeight.Medium; + return configHeading; + } + + protected SaMsExcelStyleAttr GetExceldefaultWorkbookStyle() + { + SaMsExcelStyleAttr styleAttr = new SaMsExcelStyleAttr(); + styleAttr.FontAttr.Name = "Arial"; + styleAttr.FontAttr.HeightInPoints = 9; + styleAttr.FontAttr.ColourIndex = SaXlColourIndex.Black; + styleAttr.HorizontalAlignment = (short)XlHAlign.xlHAlignLeft; + styleAttr.VerticalAlignment = (short)XlVAlign.xlVAlignTop; + styleAttr.WrapText = true; + return styleAttr; + } + + //------------------------------------------------------------------- + } + //======================================================================= +} diff --git a/SaxoniteCoreWebAdmin/Localisation/SaxAdminProperties.resx b/SaxoniteCoreWebAdmin/Localisation/SaxAdminProperties.resx index eb9b315..59f0582 100644 --- a/SaxoniteCoreWebAdmin/Localisation/SaxAdminProperties.resx +++ b/SaxoniteCoreWebAdmin/Localisation/SaxAdminProperties.resx @@ -120,6 +120,9 @@ Contact Method + + Download Organisations + Image Upload @@ -156,6 +159,9 @@ Organisations + + Upload Organisations + User Report @@ -276,6 +282,9 @@ Create + + Data file + Database @@ -288,6 +297,9 @@ Delete + + Download + Edit @@ -321,6 +333,9 @@ Images + + Include Id + Index @@ -405,6 +420,9 @@ Online today + + Options + Organisation @@ -474,6 +492,9 @@ none selected + + Sheet name or number (from 1) + Speciality @@ -540,6 +561,15 @@ Drag image here + + Drop organisation data file here. + + + Data file must be supplied for upload. + + + Only a single data file can be supplied for upload. + Data is incomplete @@ -561,9 +591,18 @@ Item update failed + + Sheet not found. + + + Invalid sheet name supplied. + System error encountered. + + The process is already running. + Unfortunately, something has gone wrong.\n\nYou will be returned to the home page. diff --git a/SaxoniteCoreWebAdmin/Properties/launchSettings.json b/SaxoniteCoreWebAdmin/Properties/launchSettings.json index 1db5bc0..cf6f2f9 100644 --- a/SaxoniteCoreWebAdmin/Properties/launchSettings.json +++ b/SaxoniteCoreWebAdmin/Properties/launchSettings.json @@ -7,7 +7,8 @@ "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" - } + }, + "nativeDebugging": false }, "SaxoniteCoreWebAdmin": { "commandName": "Project", @@ -23,8 +24,8 @@ "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { - "applicationUrl": "http://localhost:50421", - "sslPort": 44351 + "applicationUrl": "http://localhost:43216", + "sslPort": 44365 } } } \ No newline at end of file diff --git a/SaxoniteCoreWebAdmin/SaxoniteCoreWebAdmin.csproj b/SaxoniteCoreWebAdmin/SaxoniteCoreWebAdmin.csproj index 951a063..51db26a 100644 --- a/SaxoniteCoreWebAdmin/SaxoniteCoreWebAdmin.csproj +++ b/SaxoniteCoreWebAdmin/SaxoniteCoreWebAdmin.csproj @@ -1,7 +1,7 @@ - netcoreapp3.1 + net6.0 false Saxonite.Net.Core.Web Saxonite.Net.Core.Web @@ -9,14 +9,16 @@ - + + + diff --git a/SaxoniteCoreWebAdmin/Views/Organisation/Index.cshtml b/SaxoniteCoreWebAdmin/Views/Organisation/Index.cshtml index c8d6b4a..d0f1ec0 100644 --- a/SaxoniteCoreWebAdmin/Views/Organisation/Index.cshtml +++ b/SaxoniteCoreWebAdmin/Views/Organisation/Index.cshtml @@ -183,6 +183,10 @@ @* Organisation Images dialog, form and script *@ + @* Organisation download dialog, form and script *@ + + @* Organisation upload dialog, form and script *@ + @* Select Item dialog and script *@ diff --git a/SaxoniteCoreWebAdmin/Views/Organisation/Panel-Organisation-Download.cshtml b/SaxoniteCoreWebAdmin/Views/Organisation/Panel-Organisation-Download.cshtml new file mode 100644 index 0000000..2474ecf --- /dev/null +++ b/SaxoniteCoreWebAdmin/Views/Organisation/Panel-Organisation-Download.cshtml @@ -0,0 +1,52 @@ +@model SaxModel_Admin +@inject SaxLocaliser Localiser +@{ + /* Organisations - Organisation Download panel */ + // Ensure view is only included once in page. + if (!Model.RegisterView("Views/Organisation/Panel-Organisation-Download")) return; + // Scripts. + Model.AdditionalFiles_JS.Add("js/sax-org-dnl.js"); +} + + + + + +
+ +
+ @using (@Html.BeginForm(FormMethod.Post, new { @id = "frmOrgDownload", @class = "sax-form" })) + { + @* Organisation filter. *@ + + + + + + + @* Sheet name or number *@ +
+
+
+
+ +
+
+
+
+  @Localiser["label.include.id"] +
+
+
+
+
+ + } +
+ +
+ diff --git a/SaxoniteCoreWebAdmin/Views/Organisation/Panel-Organisation-Images.cshtml b/SaxoniteCoreWebAdmin/Views/Organisation/Panel-Organisation-Images.cshtml index f3ba160..bb4895c 100644 --- a/SaxoniteCoreWebAdmin/Views/Organisation/Panel-Organisation-Images.cshtml +++ b/SaxoniteCoreWebAdmin/Views/Organisation/Panel-Organisation-Images.cshtml @@ -6,6 +6,9 @@ if (!Model.RegisterView("Views/Organisation/Panel-Organisation-Images")) return; // Scripts. Model.AdditionalFiles_JS.Add("js/sax-org-img.js"); + // Dynamic style values (to avoid build warning). + String maxImageWidth = Sax.Config.Organisation.Logo.DisplayWidthMax_Item + "px"; + String maxImageHeight = Sax.Config.Organisation.Logo.DisplayHeightMax_Item + "px"; } diff --git a/SaxoniteCoreWebAdmin/Views/Organisation/Panel-Organisation-Upload.cshtml b/SaxoniteCoreWebAdmin/Views/Organisation/Panel-Organisation-Upload.cshtml new file mode 100644 index 0000000..0f6acec --- /dev/null +++ b/SaxoniteCoreWebAdmin/Views/Organisation/Panel-Organisation-Upload.cshtml @@ -0,0 +1,85 @@ +@model SaxModel_Admin +@inject SaxLocaliser Localiser +@{ + /* Organisations - Organisation Upload panel */ + // Ensure view is only included once in page. + if (!Model.RegisterView("Views/Organisation/Panel-Organisation-Upload")) return; + // Scripts. + Model.AdditionalFiles_JS.Add("js/sax-org-upl.js"); +} + + + + + +
+ +
+ @using (@Html.BeginForm(FormMethod.Post, new { @id = "frmOrgUpload", @class = "sax-form", @enctype = "multipart/form-data", @onSubmit = "return false;" })) + { + +
+ +
+
@Localiser["msg.drop.organisation.data.file.here"]
+
+ + @* Organisation data file *@ +
+
+
+
+ +
+
+
+
+ +
+
+
+
+
+ + @* Sheet name or number *@ +
+
+
+
+ +
+
+
+
+ +
+
+
+
+
+ +
+ + } +
+ +
+ diff --git a/SaxoniteCoreWebAdmin/Views/_ViewImports.cshtml b/SaxoniteCoreWebAdmin/Views/_ViewImports.cshtml index 5b52a3f..5ea8d6f 100644 --- a/SaxoniteCoreWebAdmin/Views/_ViewImports.cshtml +++ b/SaxoniteCoreWebAdmin/Views/_ViewImports.cshtml @@ -1,4 +1,5 @@ -@using Sarand.Net.Standard.Base.Core +@using System.Web +@using Sarand.Net.Standard.Base.Core @using Sarand.Net.Standard.Base.Utils @using Saxonite.Base.App @using Saxonite.Net.Standard.Base.App diff --git a/SaxoniteCoreWebAdmin/wwwroot/css/sax-data.css b/SaxoniteCoreWebAdmin/wwwroot/css/sax-data.css index 5d548ac..25abdd5 100644 --- a/SaxoniteCoreWebAdmin/wwwroot/css/sax-data.css +++ b/SaxoniteCoreWebAdmin/wwwroot/css/sax-data.css @@ -116,6 +116,7 @@ .sax-header ul.nav.sax-burger ul.sax-dropdown-menu { padding: 4px 8px 4px 4px; + border-radius:2px 2px 8px 4px; box-shadow: 0 0 8px 2px #80808080; } @@ -187,6 +188,16 @@ background-image: url('../img/action/process24.png'); } +.sax-header ul.nav.sax-burger ul.sax-dropdown-menu li.sax-action.sax-dropdown-item.sax-upload +{ + background-image: url('../img/action/upload24.png'); +} + +.sax-header ul.nav.sax-burger ul.sax-dropdown-menu li.sax-action.sax-dropdown-item.sax-download +{ + background-image: url('../img/action/download24.png'); +} + .sax-header ul.nav.sax-burger ul.sax-dropdown-menu li.sax-action.sax-dropdown-item.sax-network { background-image: url('../img/action/network-24.png'); diff --git a/SaxoniteCoreWebAdmin/wwwroot/js/sax-home.js b/SaxoniteCoreWebAdmin/wwwroot/js/sax-home.js index 7f462c8..25b0824 100644 --- a/SaxoniteCoreWebAdmin/wwwroot/js/sax-home.js +++ b/SaxoniteCoreWebAdmin/wwwroot/js/sax-home.js @@ -33,17 +33,17 @@ if (!response.success) return; //return $msgs.append($('
' + sax.msg.noItemsAwaitAct + '
')).fadeIn(500); // Users. - let user = response.stats.user; - let $user = sax.getAppStatItemTemplate(); - $('.sax-header .sax-caption', $user).text(user.caption || 'Users'); - ah.populateAppStat($user, user.awaiting.label, user.awaiting.value, user.awaitingLink) - for (let item of user.onlineStats) - ah.populateAppStat($user, item.label, item.value, user.onlineLink) - $items.append($user); + let usr = response.stats.user; + let $usr = sax.getAppStatItemTemplate(); + $('.sax-header .sax-caption', $usr).text(usr.caption || 'Users').on('click', function () { win.location = usr.usersLink; }).addClass("sax-action"); + ah.populateAppStat($usr, usr.awaiting.label, usr.awaiting.value, usr.awaitingLink) + for (let item of usr.onlineStats) + ah.populateAppStat($usr, item.label, item.value, usr.onlineLink) + $items.append($usr); // Organisations. let org = response.stats.organisation; let $org = sax.getAppStatItemTemplate(); - $('.sax-header .sax-caption', $org).text(org.caption || 'Organisations'); + $('.sax-header .sax-caption', $org).text(org.caption || 'Organisations').on('click', function () { win.location = org.organisationsLink; }).addClass("sax-action"); ah.populateAppStat($org, org.awaiting.label, org.awaiting.value, org.awaitingLink) $items.append($org); $items.slideDown(750); diff --git a/SaxoniteCoreWebAdmin/wwwroot/js/sax-org-dnl.js b/SaxoniteCoreWebAdmin/wwwroot/js/sax-org-dnl.js new file mode 100644 index 0000000..18e3fec --- /dev/null +++ b/SaxoniteCoreWebAdmin/wwwroot/js/sax-org-dnl.js @@ -0,0 +1,58 @@ +/* + * Admin - Organisation Download scripts. + */ + +(function (win, doc, undef) +{ + "use strict"; + let od = sax.mdl.orgDownload = { data: {}, ajax: {} }; + let $pnl = od.$pnl = {}, $frm = od.$frm = {}, $ctrl = od.$ctrl = {}, $dlg = od.$dlg = {}; + od.classFileDrag = 'sax-file-drag'; + + //-------------------------------------------------------------- + sax.downloadOrganisationData = function () + { + //sax.debug('downloadOrganisationData'); + let frm = $frm.orgDnl; + if (!frm.length) return; + sax.clearControlErrors(frm); + sax.openDialog($dlg.orgDnl, 640, 480); + } + + od.downloadOrgData = function () + { + sax.debug("downloadOrgData"); + $('.FilterOrganisation', $frm.orgDnl).val($('.FilterOrganisation', $frm.orgFlt).val()); + $('.FilterOrganisationCategory', $frm.orgDnl).val($('.FilterOrganisationCategory', $frm.orgFlt).val()); + sax.debug($('.FilterOrganisationSportId', $frm.orgFlt)); + sax.debug($('.FilterOrganisationSportId', $frm.orgFlt).val()); + $('.FilterOrganisationSportId', $frm.orgDnl).val($('.FilterOrganisationSportId', $frm.orgFlt).val()); + $('.FilterOrganisationRegionId', $frm.orgDnl).val($('.FilterOrganisationRegionId', $frm.orgFlt).val()); + $('.FilterOrganisationStatusCategory', $frm.orgDnl).val($('.FilterOrganisationStatusCategory', $frm.orgFlt).val()); + sax.debug($frm.orgDnl); + if (!$frm.orgDnl.length) return; + $frm.orgDnl.prop('target', '_blank').prop('action', '/Organisation/Download').submit(); + } + + od.onDownloadOrgData = function (response) + { + sax.debug('onDownloadOrgData', response); + } + + //-------------------------------------------------------------- + $(function () + { + $frm.orgFlt = $('#frmOrganisationFilter'); + // Organisation Download dialog and form. + var opt = sax.dlgOpts(); + opt.buttons[sax.lbl.download] = function () { od.downloadOrgData(); }; + opt.buttons[sax.dlg.btn.cancel] = function () { $(this).dialog('close'); }; + $dlg.orgDnl= $('#dlgOrgDownload').dialog(opt); + $frm.orgDnl = $('#frmOrgDownload', $dlg.orgDnl); + $frm.orgDnl.on('input', '.sax-input, .sax-textarea', function () { sax.clearControlError(this); }).on('change', '.sax-select', function () { sax.clearControlError(this); }); + }); + + //-------------------------------------------------------------- +}(window, window.document)); + +/* The End */ \ No newline at end of file diff --git a/SaxoniteCoreWebAdmin/wwwroot/js/sax-org-upl.js b/SaxoniteCoreWebAdmin/wwwroot/js/sax-org-upl.js new file mode 100644 index 0000000..c41b9bf --- /dev/null +++ b/SaxoniteCoreWebAdmin/wwwroot/js/sax-org-upl.js @@ -0,0 +1,77 @@ +/* + * Admin - Organisation Upload scripts. + */ + +(function (win, doc, undef) +{ + "use strict"; + let ou = sax.mdl.orgUpload = { data: {}, ajax: {} }; + let $pnl = ou.$pnl = {}, $frm = ou.$frm = {}, $ctrl = ou.$ctrl = {}, $dlg = ou.$dlg = {}; + ou.classFileDrag = 'sax-file-drag'; + + //-------------------------------------------------------------- + sax.uploadOrganisationData = function () + { + //sax.debug('uploadOrganisationData'); + let frm = $frm.orgUpl; + if (!frm.length) return; + $ctrl.file.val(''); + sax.clearControlErrors(frm); + sax.openDialog($dlg.orgUpl, 640, 480); + } + + ou.onDropOrgDataFile = function (evt) + { + //sax.debug('onDropOrgDataFile'); + let files = evt.originalEvent.dataTransfer.files; + if (files.length < 1) return; + if (files.length > 1) return sax.doAlert(sax.dlg.title.uploadOrganisation, sax.msg.uploadImageSingle); + let file = files[0]; + if (file.size > sax.maxProfileImageSize) return sax.doAlert(sax.dlg.title.uploadOrganisation, sax.msg.uploadFileSizeMax.format(sax.formatFileSize(sax.maxProfileOrgData))); + if ((sax.data.orgUploadContentTypes || []).indexOf(file.type) < 0) return sax.doAlert(sax.dlg.title.imageUpload, sax.msg.uploadFileTypeInvalid.format(sax.getFileType(file))); + if ($ctrl.file.length) $ctrl.file[0].files = files; + sax.clearControlError($ctrl.file); + } + + //-------------------------------------------------------------- + ou.uploadOrgData = function () + { + ou.ajax.upload = ou.ajax.upload || { url: sax.url.base + 'Organisation/Ajax/Upload', callbackFn: ou.onUploadOrgData }; + ou.ajax.upload.formData = new FormData($frm.orgUpl[0]); + sax.doAjaxRequest(ou.ajax.upload, 50); + } + + ou.onUploadOrgData = function (response) + { + //sax.debug('onUploadOrgData', response); + sax.refreshControlValues(response.controlValues, $frm.org); + if (!response.success) return; + + $dlg.orgUpl.dialog('close'); + } + + //-------------------------------------------------------------- + $(function () + { + // Organisation Upload dialog and form. + var opt = sax.dlgOpts(); + opt.buttons[sax.lbl.upload] = function () { ou.uploadOrgData(); }; + opt.buttons[sax.dlg.btn.cancel] = function () { $(this).dialog('close'); }; + $dlg.orgUpl = $('#dlgOrgUpload').dialog(opt); + $frm.orgUpl = $('#frmOrgUpload', $dlg.orgUpl); + $ctrl.file = $('input.OrganisationDataFile[type="file"]', $frm.orgUpl); + $frm.orgUpl.on('input', '.sax-input, .sax-textarea', function () { sax.clearControlError(this); }).on('change', '.sax-select', function () { sax.clearControlError(this); }); + + // Organisation data file drop area. + $pnl.orgLogoImgDrop = $('.sax-panel-drop.OrgDataFileDrop', $dlg.orgUpl) + .on('dragenter', function (evt) { sax.haltEvent(evt); }) + .on('dragover', function (evt) { sax.haltEvent(evt); if (!$pnl.orgLogoImgDrop.hasClass(ou.classFileDrag)) $pnl.orgLogoImgDrop.addClass(ou.classFileDrag); }) + .on('dragleave', function (evt) { sax.haltEvent(evt); $pnl.orgLogoImgDrop.removeClass(ou.classFileDrag); }) + .on('drop', function (evt) { sax.haltEvent(evt); $pnl.orgLogoImgDrop.removeClass(ou.classFileDrag); ou.onDropOrgDataFile(evt); }); + + }); + + //-------------------------------------------------------------- +}(window, window.document)); + +/* The End */ \ No newline at end of file diff --git a/SaxoniteCoreWebAdmin/wwwroot/js/sax-org.js b/SaxoniteCoreWebAdmin/wwwroot/js/sax-org.js index bb69dcf..1220b54 100644 --- a/SaxoniteCoreWebAdmin/wwwroot/js/sax-org.js +++ b/SaxoniteCoreWebAdmin/wwwroot/js/sax-org.js @@ -32,11 +32,14 @@ op.data.contactMethods = response.contactMethods; op.data.defaultStatusCode = response.defaultStatusCode; sax.populateSelectCodeDesc('#cbOrganisationStatus', response.statuses); + sax.data.orgUploadContentTypes = response.uploadContentTypes; // Menu. var $bgr = $('.sax-burger', $pnl.filter).hide(); var $mnu = $('ul.sax-dropdown-menu', $bgr).empty(); - $mnu.append(op.getFilterMenuOption(sax.lbl.refresh, function () { op.refreshResults(); }, 'sax-refresh')); $mnu.append(op.getFilterMenuOption(sax.lbl.create, function () { op.createItem(); }, 'sax-add')); + $mnu.append(op.getFilterMenuOption(sax.lbl.download, function () { sax.downloadOrganisationData(); }, 'sax-download')); + $mnu.append(op.getFilterMenuOption(sax.lbl.upload, function () { sax.uploadOrganisationData(); }, 'sax-upload')); + $mnu.append(op.getFilterMenuOption(sax.lbl.refresh, function () { op.refreshResults(); }, 'sax-refresh')); $bgr.toggle(!!$('.sax-dropdown-item', $mnu).length); // Items. var items = response.organisations; diff --git a/SaxoniteCoreWebOnline/Online/App/Program.cs b/SaxoniteCoreWebOnline/Online/App/Program.cs index 39945ae..ec5defd 100644 --- a/SaxoniteCoreWebOnline/Online/App/Program.cs +++ b/SaxoniteCoreWebOnline/Online/App/Program.cs @@ -17,7 +17,16 @@ namespace Saxonite.Net.Core.Web.Online.App //------------------------------------------------------------------- public static void Main(string[] args) { - CreateHostBuilder(args).Build().Run(); + try + { + CreateHostBuilder(args).Build().Run(); + } + finally + { + // Cleanup - globals, async processes etc. + if (SaxApplication_Online.AppInstance != null) + SaxApplication_Online.AppInstance.Dispose(); + } } //------------------------------------------------------------------- diff --git a/SaxoniteCoreWebOnline/Online/App/SaxApplication_Online.cs b/SaxoniteCoreWebOnline/Online/App/SaxApplication_Online.cs index 8ed8c1b..a0d49f7 100644 --- a/SaxoniteCoreWebOnline/Online/App/SaxApplication_Online.cs +++ b/SaxoniteCoreWebOnline/Online/App/SaxApplication_Online.cs @@ -44,29 +44,43 @@ namespace Saxonite.Net.Core.Web.Online.App try { AppInstance = this; - SaxGlobals.App = this; - SaxGlobals.System = (System_Web = new SaxSystem_Web()); Configuration = configuration; - ConsoleHelper_Web = new SaxConsoleHelper_WebOnline(this); + ConsoleHelper_Online = new SaxConsoleHelper_Online(this); } catch (Exception exc) { SaLogger.Error(GetType().Name + ".CTR " + ExceptionUtil.GetErrorStateMessage(exc)); - throw exc; + throw; } } + protected override void Dispose(bool disposing) + { + if (disposing) + { + // Clear globals. + AppInstance = null; + // Console helper. + if (ConsoleHelper_Online!= null) + ConsoleHelper_Online.Dispose(); + ConsoleHelper_Online= null; + } + base.Dispose(disposing); + } //------------------------------------------------------------------- - // Properties. + // Globals. public static SaxApplication_Online AppInstance { get; private set; } + public override SaxConsoleHelper ConsoleHelper { get { return ConsoleHelper_Online; } } + public SaxConsoleHelper_Online ConsoleHelper_Online { get; private set; } + // Database. public override ISacsDatabase GetDatabase(SaxSessionContext sessionContext, Sax.Database databaseType) { return sessionContexts.GetValue(sessionContext.Index, null)?.Databases.GetValue(databaseType, null) ?? null; } - public override String SessionCookieName { get { return Sax.Config.Online.Session.Cookie.Name; } } // Configuration. public override SaxAppConfig AppConfig { get { return appConfigs.Values.ToList().getConfigForApplication(); } } public override SaxEmailConfig EmailConfig { get { return emailConfigs.Values.ToList().getConfigForSession(AppConfig); } } + public override String SessionCookieName { get { return Sax.Config.Online.Session.Cookie.Name; } } //------------------------------------------------------------------- // Initialisation. diff --git a/SaxoniteCoreWebOnline/Online/Consoles/SaxConsoleHelper_WebOnline.cs b/SaxoniteCoreWebOnline/Online/Consoles/SaxConsoleHelper_Online.cs similarity index 82% rename from SaxoniteCoreWebOnline/Online/Consoles/SaxConsoleHelper_WebOnline.cs rename to SaxoniteCoreWebOnline/Online/Consoles/SaxConsoleHelper_Online.cs index e88ab22..a9373a1 100644 --- a/SaxoniteCoreWebOnline/Online/Consoles/SaxConsoleHelper_WebOnline.cs +++ b/SaxoniteCoreWebOnline/Online/Consoles/SaxConsoleHelper_Online.cs @@ -10,7 +10,7 @@ using System.Threading.Tasks; namespace Saxonite.Net.Core.Web.Online.Consoles { //======================================================================= - public class SaxConsoleHelper_WebOnline : SaxConsoleHelper_Web + public class SaxConsoleHelper_Online : SaxConsoleHelper_Web { //------------------------------------------------------------------- // Data members. @@ -18,10 +18,25 @@ namespace Saxonite.Net.Core.Web.Online.Consoles //------------------------------------------------------------------- // construction. - public SaxConsoleHelper_WebOnline(SaxApplication_Online app) + public SaxConsoleHelper_Online(SaxApplication_Online app) { this.app = app; } + protected override void Dispose(bool disposing) + { + if (disposing) + { + // Data cache consoles. + foreach (SaxDataCacheConsole_Online dataCacheConsole in dataCacheConsoles.Values) + dataCacheConsole.Dispose(); + dataCacheConsoles.Clear(); + // Session data consoles. + foreach (SaxSessionDataConsole_Online sessionDataConsole in sessionDataConsoles.Values) + sessionDataConsole.Dispose(); + sessionDataConsoles.Clear(); + } + base.Dispose(disposing); + } //------------------------------------------------------------------- // Data cache console. diff --git a/SaxoniteCoreWebOnline/Online/Models/SaxModel_Online.cs b/SaxoniteCoreWebOnline/Online/Models/SaxModel_Online.cs index 5193217..f474501 100644 --- a/SaxoniteCoreWebOnline/Online/Models/SaxModel_Online.cs +++ b/SaxoniteCoreWebOnline/Online/Models/SaxModel_Online.cs @@ -1,4 +1,5 @@ -using Sarand.Net.Standard.Base.App; +using Sacs.Web.Components; +using Sarand.Net.Standard.Base.App; using Sarand.Net.Standard.Base.Bus; using Sarand.Net.Standard.Base.Helpers; using Sarand.Net.Standard.Base.Utils; @@ -172,7 +173,7 @@ namespace Saxonite.Net.Core.Web.Online.Models return new SysId(); if (StringUtil.IsNumeric(encryptedId)) return encryptedId.ToSysId(); - NameValueCollection idObfuscationParams = SaxController_Base.DecryptParameters(encryptedId, Base64IV_IdObfuscation, Base64Key_IdObfuscation); + NameValueCollection idObfuscationParams = SaController.DecryptParameters(encryptedId, Base64IV_IdObfuscation, Base64Key_IdObfuscation); return idObfuscationParams.Get(ParamName_IdObfuscation_Id).ToSysId(); } diff --git a/SaxoniteCoreWebOnline/SaxoniteCoreWebOnline.csproj b/SaxoniteCoreWebOnline/SaxoniteCoreWebOnline.csproj index 78f6672..91771e1 100644 --- a/SaxoniteCoreWebOnline/SaxoniteCoreWebOnline.csproj +++ b/SaxoniteCoreWebOnline/SaxoniteCoreWebOnline.csproj @@ -1,7 +1,7 @@ - netcoreapp3.1 + net6.0 false Saxonite.Net.Core.Web Saxonite.Net.Core.Web @@ -9,7 +9,7 @@ - + @@ -23,11 +23,6 @@
- - - - - PreserveNewest diff --git a/SaxoniteCoreWebOnline/Views/Shared/_Layout.cshtml b/SaxoniteCoreWebOnline/Views/Shared/_Layout.cshtml index 35972c6..8c8de4f 100644 --- a/SaxoniteCoreWebOnline/Views/Shared/_Layout.cshtml +++ b/SaxoniteCoreWebOnline/Views/Shared/_Layout.cshtml @@ -88,6 +88,7 @@ sax.msg.mandatoryValue = '@Localiser.GetJavascriptText("msg.validation.mandatory.value")'; sax.msg.invalidValue = '@Localiser.GetJavascriptText("msg.validation.invalid.value")'; sax.maxProfileImageSize = 2 * 10 ** 6; + sax.maxProfileDataFileSize = 2 * 10 ** 9; sax.session.cookieName = '@Model.SessionCookieName'; sax.awaitingLogin = @awaitingLogin; sax.kli.active = @keepLoggedIn; diff --git a/SaxoniteStandardBase/Base/App/Sax-Version.cs b/SaxoniteStandardBase/Base/App/Sax-Version.cs index 0885ea3..f297260 100644 --- a/SaxoniteStandardBase/Base/App/Sax-Version.cs +++ b/SaxoniteStandardBase/Base/App/Sax-Version.cs @@ -18,7 +18,7 @@ namespace Saxonite.Net.Standard.Base.App //------------------------------------------------------------------- public static class Version { - private static String version { get; } = "1.2.11.1"; + private static String version { get; } = "1.2.11.2"; // Retrieve version. public static String GetFullVersion () { return version; } public static String GetAppVersion () @@ -41,7 +41,7 @@ namespace Saxonite.Net.Standard.Base.App // Version history. //------------------------------------------------------------------- // 1.2.11 6-Jul-2023 - // - Organisation associations. + // - Organisation associations, admin download [Feb24]. // 1.2.10 11-Oct-2022 // - Online organisations display issue fixed. // - User Sport 'Preparing' options. diff --git a/SaxoniteStandardBase/Base/App/SaxApplication_Base.cs b/SaxoniteStandardBase/Base/App/SaxApplication_Base.cs index 2cb5ce9..f2fb44b 100644 --- a/SaxoniteStandardBase/Base/App/SaxApplication_Base.cs +++ b/SaxoniteStandardBase/Base/App/SaxApplication_Base.cs @@ -4,22 +4,33 @@ using Saxonite.Net.Standard.Base.App; using System; using System.Collections.Generic; using System.Text; +using Sarand.Net.Standard.Base.Core; namespace Saxonite.Net.Standard.Base.App { //======================================================================= - public abstract class SaxApplication_Base + public abstract class SaxApplication_Base : SacsDisposable { //------------------------------------------------------------------- // Protected construction. protected SaxApplication_Base() { + App = this; + } + protected override void Dispose(bool disposing) + { + if (disposing) + { + App = null; + } } //------------------------------------------------------------------- - // Properties. + // Globals. + public static SaxApplication_Base App { get; private set; } public abstract SaxSystem System { get; } public abstract SaxConsoleHelper ConsoleHelper { get; } + // Database. public abstract ISacsDatabase GetDatabase(SaxSessionContext sessionContext, Sax.Database databaseType); public abstract IDatabaseInfo CreateDbInfo(SaxSessionContext sessionContext, Sax.Database databaseType); // Application instance. diff --git a/SaxoniteStandardBase/Base/App/SaxGlobals.cs b/SaxoniteStandardBase/Base/App/SaxGlobals.cs index 0fdcbe5..f5d536e 100644 --- a/SaxoniteStandardBase/Base/App/SaxGlobals.cs +++ b/SaxoniteStandardBase/Base/App/SaxGlobals.cs @@ -10,11 +10,11 @@ namespace Saxonite.Net.Standard.Base.App { //------------------------------------------------------------------- // System. - public static SaxSystem System { get; set; } + public static SaxSystem System { get { return App.System; } } //------------------------------------------------------------------- // Application. - public static SaxApplication_Base App { get; set; } + public static SaxApplication_Base App { get { return SaxApplication_Base.App; } } public static SaxConsoleHelper ConsoleHelper { get { return App.ConsoleHelper; } } //------------------------------------------------------------------- diff --git a/SaxoniteStandardBase/Base/Consoles/SaxConsoleHelper.cs b/SaxoniteStandardBase/Base/Consoles/SaxConsoleHelper.cs index 48f06a5..a7e53f6 100644 --- a/SaxoniteStandardBase/Base/Consoles/SaxConsoleHelper.cs +++ b/SaxoniteStandardBase/Base/Consoles/SaxConsoleHelper.cs @@ -1,4 +1,5 @@ -using Saxonite.Net.Standard.Base.App; +using Sarand.Net.Standard.Base.Core; +using Saxonite.Net.Standard.Base.App; using System; using System.Collections.Generic; using System.Text; @@ -6,13 +7,16 @@ using System.Text; namespace Saxonite.Net.Standard.Base.Consoles { //======================================================================= - public abstract class SaxConsoleHelper + public abstract class SaxConsoleHelper : SacsDisposable { //------------------------------------------------------------------- // Protected construction. protected SaxConsoleHelper() { } + protected override void Dispose(bool disposing) + { + } //------------------------------------------------------------------- // Data cache console. diff --git a/SaxoniteStandardBase/Base/Consoles/SaxProcessConsole.cs b/SaxoniteStandardBase/Base/Consoles/SaxProcessConsole.cs new file mode 100644 index 0000000..671811b --- /dev/null +++ b/SaxoniteStandardBase/Base/Consoles/SaxProcessConsole.cs @@ -0,0 +1,38 @@ +using Sarand.Net.Standard.Base.Bus; +using Saxonite.Net.Standard.Base.App; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Saxonite.Net.Standard.Base.Consoles +{ + //======================================================================= + public class SaxProcessConsole : SaErrorStateItem + { + //------------------------------------------------------------------- + // Data members. + private SaxInternals internals = null; + + //------------------------------------------------------------------- + // Construction. + protected SaxProcessConsole(SaxSessionContext sessionContext) + { + SessionContext = sessionContext; + } + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (internals != null) internals.Dispose(); internals = null; + } + } + + //------------------------------------------------------------------- + // Properties. + protected SaxSessionContext SessionContext { get; } + protected SaxInternals Internals { get { if (internals == null) internals = new SaxInternals(SessionContext); return internals; } } + + //------------------------------------------------------------------- + } + //======================================================================= +} diff --git a/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-Organisation.cs b/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-Organisation.cs index 4cc2f50..3b07d5c 100644 --- a/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-Organisation.cs +++ b/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-Organisation.cs @@ -1,4 +1,5 @@ -using Sarand.Net.Standard.Base.App; +using NPOI.SS.Formula.Functions; +using Sarand.Net.Standard.Base.App; using Sarand.Net.Standard.Base.Bus; using Sarand.Net.Standard.Base.Core; using Sarand.Net.Standard.Base.Dbas; @@ -255,23 +256,35 @@ namespace Saxonite.Net.Standard.Base.Mgr //----------------------------------------------------------- // Organisation type. case SaxOrganisation.Column.OrganisationType_Desc: - String organisationType = values.GetValue(column.DataName).ToString(); - // ToDo Translate organisation type. - value = new DataValue(); + { + String organisationTypeCode = values.GetValue(column.DataName).ToString(); + TypeCodeDesc organisationType = OrganisationTypeCodes.Find(item => item.Code.Equals(organisationTypeCode)); + if (!StringUtil.IsBlank(organisationType?.Desc ?? "")) + value = new DataValue(organisationType.Desc); + else + value = new DataValue(organisationTypeCode); + } break; //----------------------------------------------------------- // Organisation category. case SaxOrganisation.Column.OrganisationCategory_Desc: - String organisationCategory = values.GetValue(column.DataName).ToString(); - // ToDo Translate organisation category. - value = new DataValue(); + { + String organisationCategoryCode = values.GetValue(column.DataName).ToString(); + TypeCodeDesc organisationCategory = OrganisationCategoryCodes.Find(item => item.Code.Equals(organisationCategoryCode)); + if (!StringUtil.IsBlank(organisationCategory?.Desc ?? "")) + value = new DataValue(organisationCategory.Desc); + else + value = new DataValue(organisationCategoryCode); + } break; //----------------------------------------------------------- // Notes - limit to 200 chars. case SaxOrganisation.Column.Notes: - value = new DataValue(SacsHierarchyItem.FormatForDisplay(values.GetValue(column.DataName).ToString(), 200)); + { + value = new DataValue(SacsHierarchyItem.FormatForDisplay(values.GetValue(column.DataName).ToString(), 200)); + } break; //----------------------------------------------------------- @@ -288,7 +301,13 @@ namespace Saxonite.Net.Standard.Base.Mgr } break; - //----------------------------------------------------------- + //----------------------------------------------------------- + default: + { + value = values.GetValue(column.DataName); + } + break; + } return value; } diff --git a/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserConfig.cs b/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserConfig.cs index 82c3774..b277a15 100644 --- a/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserConfig.cs +++ b/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserConfig.cs @@ -124,7 +124,11 @@ namespace Saxonite.Net.Standard.Base.Mgr } break; - //----------------------------------------------------------- + //----------------------------------------------------------- + default: + value = values.GetValue(column.DataName); + break; + } return value; } diff --git a/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserEligibility.cs b/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserEligibility.cs index 487e988..53a01bf 100644 --- a/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserEligibility.cs +++ b/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserEligibility.cs @@ -242,7 +242,11 @@ namespace Saxonite.Net.Standard.Base.Mgr } break; - //----------------------------------------------------------- + //----------------------------------------------------------- + default: + value = values.GetValue(column.DataName); + break; + } return value; } diff --git a/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserOnline.cs b/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserOnline.cs index 249851d..7e83f59 100644 --- a/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserOnline.cs +++ b/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserOnline.cs @@ -117,6 +117,10 @@ namespace Saxonite.Net.Standard.Base.Mgr break; //----------------------------------------------------------- + default: + value = values.GetValue(column.DataName); + break; + } return value; } diff --git a/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserOrganisation.cs b/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserOrganisation.cs index 2d24711..fbf10a7 100644 --- a/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserOrganisation.cs +++ b/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserOrganisation.cs @@ -325,7 +325,11 @@ namespace Saxonite.Net.Standard.Base.Mgr } break; - //----------------------------------------------------------- + //----------------------------------------------------------- + default: + value = values.GetValue(column.DataName); + break; + } return value; } diff --git a/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserRole.cs b/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserRole.cs index 72cf8a6..cc0c49e 100644 --- a/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserRole.cs +++ b/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserRole.cs @@ -235,6 +235,10 @@ namespace Saxonite.Net.Standard.Base.Mgr break; //----------------------------------------------------------- + default: + value = values.GetValue(column.DataName); + break; + } return value; } diff --git a/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserSession.cs b/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserSession.cs index aad753b..574b88e 100644 --- a/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserSession.cs +++ b/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserSession.cs @@ -110,6 +110,10 @@ namespace Saxonite.Net.Standard.Base.Mgr break; //----------------------------------------------------------- + default: + value = values.GetValue(column.DataName); + break; + } return value; } diff --git a/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserSport.cs b/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserSport.cs index 4a33775..1bf641a 100644 --- a/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserSport.cs +++ b/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserSport.cs @@ -267,7 +267,11 @@ namespace Saxonite.Net.Standard.Base.Mgr } break; - //----------------------------------------------------------- + //----------------------------------------------------------- + default: + value = values.GetValue(column.DataName); + break; + } return value; } diff --git a/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserSportExperience.cs b/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserSportExperience.cs index aa9d1a3..4c628d4 100644 --- a/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserSportExperience.cs +++ b/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserSportExperience.cs @@ -256,7 +256,11 @@ namespace Saxonite.Net.Standard.Base.Mgr } break; - //----------------------------------------------------------- + //----------------------------------------------------------- + default: + value = values.GetValue(column.DataName); + break; + } return value; } diff --git a/SaxoniteStandardBase/Base/Mgr/SaxActivityMgr-ActivityFeedback.cs b/SaxoniteStandardBase/Base/Mgr/SaxActivityMgr-ActivityFeedback.cs index 590db48..1da8c55 100644 --- a/SaxoniteStandardBase/Base/Mgr/SaxActivityMgr-ActivityFeedback.cs +++ b/SaxoniteStandardBase/Base/Mgr/SaxActivityMgr-ActivityFeedback.cs @@ -180,7 +180,11 @@ namespace Saxonite.Net.Standard.Base.Mgr } break; - //----------------------------------------------------------- + //----------------------------------------------------------- + default: + value = values.GetValue(column.DataName); + break; + } return value; } diff --git a/SaxoniteStandardBase/Base/Mgr/SaxActivityMgr-ActivityIndex.cs b/SaxoniteStandardBase/Base/Mgr/SaxActivityMgr-ActivityIndex.cs index 5bd656e..2e96f85 100644 --- a/SaxoniteStandardBase/Base/Mgr/SaxActivityMgr-ActivityIndex.cs +++ b/SaxoniteStandardBase/Base/Mgr/SaxActivityMgr-ActivityIndex.cs @@ -191,7 +191,11 @@ namespace Saxonite.Net.Standard.Base.Mgr } break; - //----------------------------------------------------------- + //----------------------------------------------------------- + default: + value = values.GetValue(column.DataName); + break; + } return value; } diff --git a/SaxoniteStandardBase/Base/Mgr/SaxActivityMgr-Article.cs b/SaxoniteStandardBase/Base/Mgr/SaxActivityMgr-Article.cs index 3e73aeb..333e695 100644 --- a/SaxoniteStandardBase/Base/Mgr/SaxActivityMgr-Article.cs +++ b/SaxoniteStandardBase/Base/Mgr/SaxActivityMgr-Article.cs @@ -256,7 +256,11 @@ namespace Saxonite.Net.Standard.Base.Mgr } break; - //----------------------------------------------------------- + //----------------------------------------------------------- + default: + value = values.GetValue(column.DataName); + break; + } return value; } diff --git a/SaxoniteStandardBase/Base/Mgr/SaxActivityMgr-OrganisationEvent.cs b/SaxoniteStandardBase/Base/Mgr/SaxActivityMgr-OrganisationEvent.cs index 7e8bdda..9ed723d 100644 --- a/SaxoniteStandardBase/Base/Mgr/SaxActivityMgr-OrganisationEvent.cs +++ b/SaxoniteStandardBase/Base/Mgr/SaxActivityMgr-OrganisationEvent.cs @@ -249,7 +249,11 @@ namespace Saxonite.Net.Standard.Base.Mgr } break; - //----------------------------------------------------------- + //----------------------------------------------------------- + default: + value = values.GetValue(column.DataName); + break; + } return value; } diff --git a/SaxoniteStandardBase/Base/Mgr/SaxActivityMgr-OrganisationOpportunity.cs b/SaxoniteStandardBase/Base/Mgr/SaxActivityMgr-OrganisationOpportunity.cs index 83ec0ea..e411327 100644 --- a/SaxoniteStandardBase/Base/Mgr/SaxActivityMgr-OrganisationOpportunity.cs +++ b/SaxoniteStandardBase/Base/Mgr/SaxActivityMgr-OrganisationOpportunity.cs @@ -235,7 +235,11 @@ namespace Saxonite.Net.Standard.Base.Mgr } break; - //----------------------------------------------------------- + //----------------------------------------------------------- + default: + value = values.GetValue(column.DataName); + break; + } return value; } diff --git a/SaxoniteStandardBase/Base/Mgr/SaxActivityMgr-UserActivity.cs b/SaxoniteStandardBase/Base/Mgr/SaxActivityMgr-UserActivity.cs index da962ff..1936c51 100644 --- a/SaxoniteStandardBase/Base/Mgr/SaxActivityMgr-UserActivity.cs +++ b/SaxoniteStandardBase/Base/Mgr/SaxActivityMgr-UserActivity.cs @@ -172,7 +172,11 @@ namespace Saxonite.Net.Standard.Base.Mgr } break; - //----------------------------------------------------------- + //----------------------------------------------------------- + default: + value = values.GetValue(column.DataName); + break; + } return value; } diff --git a/SaxoniteStandardBase/Base/Mgr/SaxItemDataMgr-ItemAddress.cs b/SaxoniteStandardBase/Base/Mgr/SaxItemDataMgr-ItemAddress.cs index a252adb..16696d3 100644 --- a/SaxoniteStandardBase/Base/Mgr/SaxItemDataMgr-ItemAddress.cs +++ b/SaxoniteStandardBase/Base/Mgr/SaxItemDataMgr-ItemAddress.cs @@ -262,7 +262,11 @@ namespace Saxonite.Net.Standard.Base.Mgr } break; - //----------------------------------------------------------- + //----------------------------------------------------------- + default: + value = values.GetValue(column.DataName); + break; + } return value; } diff --git a/SaxoniteStandardBase/Base/Mgr/SaxItemDataMgr-ItemAssociation.cs b/SaxoniteStandardBase/Base/Mgr/SaxItemDataMgr-ItemAssociation.cs index 757af96..9f1e784 100644 --- a/SaxoniteStandardBase/Base/Mgr/SaxItemDataMgr-ItemAssociation.cs +++ b/SaxoniteStandardBase/Base/Mgr/SaxItemDataMgr-ItemAssociation.cs @@ -203,7 +203,11 @@ namespace Saxonite.Net.Standard.Base.Mgr } break; - //----------------------------------------------------------- + //----------------------------------------------------------- + default: + value = values.GetValue(column.DataName); + break; + } return value; } diff --git a/SaxoniteStandardBase/Base/Mgr/SaxItemDataMgr-ItemImage.cs b/SaxoniteStandardBase/Base/Mgr/SaxItemDataMgr-ItemImage.cs index abd9b2a..9d90e3b 100644 --- a/SaxoniteStandardBase/Base/Mgr/SaxItemDataMgr-ItemImage.cs +++ b/SaxoniteStandardBase/Base/Mgr/SaxItemDataMgr-ItemImage.cs @@ -221,7 +221,11 @@ namespace Saxonite.Net.Standard.Base.Mgr } break; - //----------------------------------------------------------- + //----------------------------------------------------------- + default: + value = values.GetValue(column.DataName); + break; + } return value; } diff --git a/SaxoniteStandardBase/Base/Mgr/SaxItemDataMgr-ItemSport.cs b/SaxoniteStandardBase/Base/Mgr/SaxItemDataMgr-ItemSport.cs index ebae57b..510fa5e 100644 --- a/SaxoniteStandardBase/Base/Mgr/SaxItemDataMgr-ItemSport.cs +++ b/SaxoniteStandardBase/Base/Mgr/SaxItemDataMgr-ItemSport.cs @@ -181,7 +181,11 @@ namespace Saxonite.Net.Standard.Base.Mgr } break; - //----------------------------------------------------------- + //----------------------------------------------------------- + default: + value = values.GetValue(column.DataName); + break; + } return value; } diff --git a/SaxoniteStandardBase/Base/Mgr/SaxReferenceMgr-Region.cs b/SaxoniteStandardBase/Base/Mgr/SaxReferenceMgr-Region.cs index 76684ac..0b91a14 100644 --- a/SaxoniteStandardBase/Base/Mgr/SaxReferenceMgr-Region.cs +++ b/SaxoniteStandardBase/Base/Mgr/SaxReferenceMgr-Region.cs @@ -209,7 +209,11 @@ namespace Saxonite.Net.Standard.Base.Mgr } break; - //----------------------------------------------------------- + //----------------------------------------------------------- + default: + value = values.GetValue(column.DataName); + break; + } return value; } diff --git a/SaxoniteStandardBase/Base/Mgr/SaxReferenceMgr-Sport.cs b/SaxoniteStandardBase/Base/Mgr/SaxReferenceMgr-Sport.cs index f348fc7..7278743 100644 --- a/SaxoniteStandardBase/Base/Mgr/SaxReferenceMgr-Sport.cs +++ b/SaxoniteStandardBase/Base/Mgr/SaxReferenceMgr-Sport.cs @@ -195,7 +195,11 @@ namespace Saxonite.Net.Standard.Base.Mgr } break; - //----------------------------------------------------------- + //----------------------------------------------------------- + default: + value = values.GetValue(column.DataName); + break; + } return value; } diff --git a/SaxoniteStandardBase/Base/Mgr/Support/SaxSupportMgr-UserReport.cs b/SaxoniteStandardBase/Base/Mgr/Support/SaxSupportMgr-UserReport.cs index 5f0d1e3..bcd9ee0 100644 --- a/SaxoniteStandardBase/Base/Mgr/Support/SaxSupportMgr-UserReport.cs +++ b/SaxoniteStandardBase/Base/Mgr/Support/SaxSupportMgr-UserReport.cs @@ -179,7 +179,11 @@ namespace Saxonite.Net.Standard.Base.Mgr.Support } break; - //----------------------------------------------------------- + //----------------------------------------------------------- + default: + value = values.GetValue(column.DataName); + break; + } return value; } diff --git a/SaxoniteStandardBase/Base/Procs/SaxProcess_Base.cs b/SaxoniteStandardBase/Base/Procs/SaxProcess_Base.cs new file mode 100644 index 0000000..832f267 --- /dev/null +++ b/SaxoniteStandardBase/Base/Procs/SaxProcess_Base.cs @@ -0,0 +1,59 @@ +using Sarand.Net.Standard.Proc.Procs; +using Saxonite.Net.Standard.Base.Mgr.Support; +using Saxonite.Net.Standard.Base.Mgr; +using System; +using System.Collections.Generic; +using System.Text; +using Saxonite.Net.Standard.Base.App; + +namespace Saxonite.Net.Standard.Base.Procs +{ + //======================================================================= + public abstract class SaxProcess_Base : SaProcess + { + //------------------------------------------------------------------- + // Definitions. + public new class Config : SaProcess.Config + { + protected Config(SaxSessionContext sessionContext) : base() + { + SessionContext = sessionContext; + } + public SaxSessionContext SessionContext { get; } + } + + //------------------------------------------------------------------- + // Data members. + private SaxInternals internals; + private Config config; + + //------------------------------------------------------------------- + // Construction. + protected SaxProcess_Base(Config config) : base(config) + { + this.config = config; + } + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (internals != null) internals.Dispose(); + internals = null; + } + base.Dispose(disposing); + } + + //------------------------------------------------------------------- + // Properties. + public SaxInternals Internals { get { if (internals == null) internals = new SaxInternals(config.SessionContext); return internals; } } + // 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; } } + public SaxSupportMgr SupportMgr { get { return Internals.SupportMgr; } } + + //------------------------------------------------------------------- + } + //======================================================================= +} diff --git a/SaxoniteStandardBase/SaxoniteStandardBase.csproj b/SaxoniteStandardBase/SaxoniteStandardBase.csproj index 664d1cf..b2c5ecd 100644 --- a/SaxoniteStandardBase/SaxoniteStandardBase.csproj +++ b/SaxoniteStandardBase/SaxoniteStandardBase.csproj @@ -1,12 +1,13 @@  - netstandard2.1 + net6.0 Saxonite.Net.Standard + diff --git a/SaxoniteStandardDbas/Dbas/Mgr/SaxAccountMgr_Dbas-Organisation.cs b/SaxoniteStandardDbas/Dbas/Mgr/SaxAccountMgr_Dbas-Organisation.cs index eb07306..123d176 100644 --- a/SaxoniteStandardDbas/Dbas/Mgr/SaxAccountMgr_Dbas-Organisation.cs +++ b/SaxoniteStandardDbas/Dbas/Mgr/SaxAccountMgr_Dbas-Organisation.cs @@ -290,7 +290,7 @@ namespace Saxonite.Net.Standard.Dbas.Mgr columnDfn.ReadOnly = true; break; case SaxOrganisation.Column.Name: - SetOrganisationColDfn(columnDfn, "Name", 120, SaxDbDfn_Core.ColumnIdx.Organisation_Name); + SetOrganisationColDfn(columnDfn, "Name", 180, SaxDbDfn_Core.ColumnIdx.Organisation_Name); break; case SaxOrganisation.Column.OrganisationType: SetOrganisationColDfn(columnDfn, "Type", 80, SaxDbDfn_Core.ColumnIdx.Organisation_OrganisationType, ColumnAlignment.Center, DataValue.DataType.String); diff --git a/SaxoniteStandardDbas/SaxoniteStandardDbas.csproj b/SaxoniteStandardDbas/SaxoniteStandardDbas.csproj index 4bd5ac6..c58064c 100644 --- a/SaxoniteStandardDbas/SaxoniteStandardDbas.csproj +++ b/SaxoniteStandardDbas/SaxoniteStandardDbas.csproj @@ -1,7 +1,7 @@ - netstandard2.1 + net6.0 Saxonite.Net.Standard diff --git a/SaxoniteStandardMob/SaxoniteStandardMob.csproj b/SaxoniteStandardMob/SaxoniteStandardMob.csproj index 508d0d1..ece7062 100644 --- a/SaxoniteStandardMob/SaxoniteStandardMob.csproj +++ b/SaxoniteStandardMob/SaxoniteStandardMob.csproj @@ -1,7 +1,7 @@ - netstandard2.1 + net6.0 Saxonite.Net.Standard