From 0d7be69e1e70c6a387286e2c7eb864c77bd31ea3 Mon Sep 17 00:00:00 2001 From: Andy Howard Date: Fri, 23 Jul 2021 14:01:49 +0100 Subject: [PATCH] v1.1.3.0 --- ReadMe.md | 20 + .../{SaxSessionConfig.cs => SaxAppConfig.cs} | 6 +- .../Base/App/SaxApplication_Web.cs | 12 +- SaxoniteCoreWeb/Base/App/SaxStartup.cs | 4 +- .../Base/Controllers/SaxController_Base.cs | 43 +- ...nConfigHelper.cs => SaxAppConfigHelper.cs} | 44 +- .../Base/Helpers/SaxEmailConfigHelper.cs | 6 +- SaxoniteCoreWeb/Base/Models/SaxModel_Base.cs | 4 +- .../Admin/App/SaxApplication_Admin.cs | 24 +- .../Admin/Controllers/HomeController.cs | 2 +- .../OrganisationController-Activation-Ajax.cs | 161 ++-- .../OrganisationController-Ajax.cs | 9 +- .../OrganisationController-Members-Ajax.cs | 9 +- .../Admin/Controllers/SaxController_Admin.cs | 10 +- .../Controllers/SecurityController-Ajax.cs | 4 +- .../Admin/Controllers/SecurityController.cs | 16 +- .../Models/Model_OrganisationActivation.cs | 44 ++ .../Localisation/SaxAdminProperties.resx | 21 + .../Views/Organisation/Index.cshtml | 6 +- .../Panel-ActivationDialog.cshtml | 69 +- .../Views/Security/ChangePassword.cshtml | 6 +- .../Views/Security/RequestPassword.cshtml | 2 +- .../Views/Security/Signin.cshtml | 4 +- .../appsettings.Development.json | 6 +- .../appsettings.Machine.Sarand106.json | 57 -- .../appsettings.Machine.Sarand107.json | 8 +- .../appsettings.Machine.Sarand601.json | 10 +- .../appsettings.Machine.Sarand602.json | 60 -- .../appsettings.Production.json | 6 +- SaxoniteCoreWebAdmin/wwwroot/css/sax-data.css | 2 +- SaxoniteCoreWebAdmin/wwwroot/css/sax-form.css | 2 +- SaxoniteCoreWebAdmin/wwwroot/css/sax-main.css | 7 + SaxoniteCoreWebAdmin/wwwroot/js/sax-main.js | 10 +- .../wwwroot/js/sax-organisation.js | 48 +- .../Localisation/SaxOnlineProperties.resx | 65 +- .../Online/App/SaxApplication_Online.cs | 24 +- .../AccountController-Ajax-Organisations.cs | 87 ++- .../AccountController-Ajax-Sports.cs | 4 +- .../Online/Controllers/HomeController.cs | 2 +- .../Controllers/JournalistController-Ajax.cs | 4 +- .../OrganisationController-Ajax-Members.cs | 707 ++++++++++++++++++ ...ganisationController-Ajax-Opportunities.cs | 353 ++++----- .../OrganisationController-Ajax-Request.cs | 190 +++++ .../OrganisationController-Ajax.cs | 163 ---- .../Controllers/OrganisationController.cs | 87 ++- .../Controllers/SaxController_Online.cs | 10 +- .../Controllers/SecurityController-Ajax.cs | 4 +- .../Models/Model_Organisation_Members.cs | 118 +++ .../Online/Models/SaxModel_Online.cs | 19 + .../SaxoniteCoreWebOnline.csproj | 10 - .../Views/Account/Profile-Contact.cshtml | 19 +- .../Account/Profile-Organisations.cshtml | 8 +- .../Views/Account/Profile-Sports.cshtml | 29 +- .../Views/Account/Profile.cshtml | 2 + .../Views/Common/CodeSelect.cshtml | 56 ++ .../Views/Common/OrganisationRequest.cshtml | 6 +- .../Journalist/Panel-ArticleDialog.cshtml | 4 +- .../Views/Organisation/Members.cshtml | 115 +++ .../Organisation/Panel-MemberDialog.cshtml | 119 +++ .../Panel-OpportunityDialog.cshtml | 12 +- .../Views/Security/ChangePassword.cshtml | 6 +- .../Views/Security/RequestPassword.cshtml | 2 +- .../Views/Security/Signin.cshtml | 4 +- .../Views/Shared/_Layout.cshtml | 13 +- .../appsettings.Development.json | 6 +- .../appsettings.Machine.Sarand106.json | 57 -- .../appsettings.Machine.Sarand107.json | 8 +- .../appsettings.Machine.Sarand601.json | 8 +- .../appsettings.Machine.Sarand602.json | 60 -- .../appsettings.Production.json | 6 +- .../wwwroot/css/sax-account.css | 10 + .../wwwroot/css/sax-code-sel.css | 37 + .../wwwroot/css/sax-data.css | 48 +- .../wwwroot/css/sax-form.css | 14 +- .../wwwroot/css/sax-main.css | 6 +- .../wwwroot/js/sax-account.js | 10 +- .../wwwroot/js/sax-code-sel.js | 123 +++ SaxoniteCoreWebOnline/wwwroot/js/sax-main.js | 31 +- .../wwwroot/js/sax-org-mbr.js | 322 ++++++++ .../wwwroot/js/sax-org-req.js | 2 +- SaxoniteStandardBase/Base/App/Sax-Version.cs | 5 +- .../Base/App/SaxDbDfn_Core.cs | 6 +- .../Base/Bus/SaxUserAccount.cs | 1 + .../Base/Bus/SaxUserOrganisation.cs | 114 ++- .../Base/Mgr/SaxAccountMgr-Organisation.cs | 1 + .../Base/Mgr/SaxAccountMgr-UserAccount.cs | 2 +- .../Mgr/SaxAccountMgr-UserOrganisation.cs | 19 +- .../Security/sax-email-account-activated.html | 2 +- .../Email/Shared/sax-email-footer.html | 4 +- .../Email/Shared/sax-email-header.html | 2 +- .../Resources/Email/Style/sax-email-main.css | 22 +- .../Dbas/Internal/ActivityIndex_Db.cs | 2 +- .../Dbas/Internal/ItemAddress_Db.cs | 2 +- .../Dbas/Internal/ItemImage_Db.cs | 2 +- .../Dbas/Internal/ItemSport_Db.cs | 2 +- .../Dbas/Internal/JournalistArticle_Db.cs | 2 +- .../Internal/OrganisationOpportunity_Db.cs | 2 +- .../Dbas/Internal/Organisation_Db.cs | 2 +- .../Dbas/Internal/Region_Db.cs | 2 +- .../Dbas/Internal/Sport_Db.cs | 2 +- .../Dbas/Internal/UserAccount_Db.cs | 2 +- .../Dbas/Internal/UserActivity_Db.cs | 2 +- .../Dbas/Internal/UserOrganisation_Db.cs | 8 +- .../Dbas/Internal/UserRole_Db.cs | 2 +- .../Dbas/Internal/UserSession_Db.cs | 2 +- .../Dbas/Internal/UserSport_Db.cs | 2 +- .../Mgr/SaxAccountMgr_Dbas-UserAccount.cs | 2 + .../SaxAccountMgr_Dbas-UserOrganisation.cs | 101 ++- .../Resources/Database/SaxDbDfn_Core.xml | 55 +- 109 files changed, 3161 insertions(+), 943 deletions(-) rename SaxoniteCoreWeb/Base/App/{SaxSessionConfig.cs => SaxAppConfig.cs} (97%) rename SaxoniteCoreWeb/Base/Helpers/{SaxSessionConfigHelper.cs => SaxAppConfigHelper.cs} (51%) create mode 100644 SaxoniteCoreWebAdmin/Admin/Models/Model_OrganisationActivation.cs delete mode 100644 SaxoniteCoreWebAdmin/appsettings.Machine.Sarand106.json delete mode 100644 SaxoniteCoreWebAdmin/appsettings.Machine.Sarand602.json create mode 100644 SaxoniteCoreWebOnline/Online/Controllers/OrganisationController-Ajax-Members.cs create mode 100644 SaxoniteCoreWebOnline/Online/Controllers/OrganisationController-Ajax-Request.cs create mode 100644 SaxoniteCoreWebOnline/Online/Models/Model_Organisation_Members.cs create mode 100644 SaxoniteCoreWebOnline/Views/Common/CodeSelect.cshtml create mode 100644 SaxoniteCoreWebOnline/Views/Organisation/Members.cshtml create mode 100644 SaxoniteCoreWebOnline/Views/Organisation/Panel-MemberDialog.cshtml delete mode 100644 SaxoniteCoreWebOnline/appsettings.Machine.Sarand106.json delete mode 100644 SaxoniteCoreWebOnline/appsettings.Machine.Sarand602.json create mode 100644 SaxoniteCoreWebOnline/wwwroot/css/sax-code-sel.css create mode 100644 SaxoniteCoreWebOnline/wwwroot/js/sax-code-sel.js create mode 100644 SaxoniteCoreWebOnline/wwwroot/js/sax-org-mbr.js diff --git a/ReadMe.md b/ReadMe.md index e7ec5f7..7619052 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -30,8 +30,28 @@ tinymce.get(editorId).show() | hide(); ``` +### Styling + +* Web resources: + * Colour analysis, including complementary etc. https://www.crispedge.com/color/556B2F + +#### Online + +* Colours: + * Base: #556B2F Dark Olive Green + * Darker: #445525, #33401c. Lighter: #768858, #99a682, #a6c275, #b7ce90. + + ### Graphics +#### Icons + +* Where to find icons: https://www.canva.com/learn/free-icons-download/ +* https://icons8.com +* https://www.smashingmagazine.com/category/freebies +* https://goodstuffnononsense.com/icons-category/free/ +* https://www.iconfinder.com/ + #### Animated gifs * https://icons8.com/preloaders/ diff --git a/SaxoniteCoreWeb/Base/App/SaxSessionConfig.cs b/SaxoniteCoreWeb/Base/App/SaxAppConfig.cs similarity index 97% rename from SaxoniteCoreWeb/Base/App/SaxSessionConfig.cs rename to SaxoniteCoreWeb/Base/App/SaxAppConfig.cs index 369c2cb..eb7aabf 100644 --- a/SaxoniteCoreWeb/Base/App/SaxSessionConfig.cs +++ b/SaxoniteCoreWeb/Base/App/SaxAppConfig.cs @@ -9,7 +9,7 @@ using System.Text; namespace Saxonite.Net.Core.Web.Base.App { //======================================================================= - public class SaxSessionConfig + public class SaxAppConfig { //--------------------------------------------------------------- // Data members. @@ -17,7 +17,7 @@ namespace Saxonite.Net.Core.Web.Base.App //--------------------------------------------------------------- // Construction. - public SaxSessionConfig (String context, ConfigurationSection configSection, SaxSessionConfig defaultConfig) + public SaxAppConfig (String context, ConfigurationSection configSection, SaxAppConfig defaultConfig) { Context = context; populateDefaults(defaultConfig); @@ -25,7 +25,7 @@ namespace Saxonite.Net.Core.Web.Base.App } //--------------------------------------------------------------- - private void populateDefaults(SaxSessionConfig defaultConfig) + private void populateDefaults(SaxAppConfig defaultConfig) { if (defaultConfig == null) return; Security.Password.MinLength = defaultConfig.Security.Password.MinLength; diff --git a/SaxoniteCoreWeb/Base/App/SaxApplication_Web.cs b/SaxoniteCoreWeb/Base/App/SaxApplication_Web.cs index 8b206b2..2da2629 100644 --- a/SaxoniteCoreWeb/Base/App/SaxApplication_Web.cs +++ b/SaxoniteCoreWeb/Base/App/SaxApplication_Web.cs @@ -44,7 +44,7 @@ namespace Saxonite.Net.Core.Web.Base.App // Application components. public abstract String SessionCookieName { get; } public virtual SaxSessionContext SessionContext { get; protected set; } - public abstract SaxSessionConfig SessionConfig { get; } + public abstract SaxAppConfig AppConfig { get; } public override SaxConsoleHelper ConsoleHelper { get { return ConsoleHelper_Web; } } public SaxConsoleHelper_Web ConsoleHelper_Web { get; protected set; } public override IDatabaseInfo CreateCoreDbInfo(SaxSessionContext sessionContext) { return new DbDataItem.DatabaseInfo(GetCoreDatabase(sessionContext)); } @@ -60,14 +60,14 @@ namespace Saxonite.Net.Core.Web.Base.App List applicationParams = new List(); applicationParams.Add("Host", SaxSystem.ComputerName); applicationParams.Add("Version", Sax.Version.GetFullVersion()); - applicationParams.Add("Instance", InstanceType.ToString() + (!StringUtil.IsEmpty(SessionConfig.Instance) ? " [" + SessionConfig.Instance + "]" : "")); + applicationParams.Add("Instance", InstanceType.ToString() + (!StringUtil.IsEmpty(AppConfig.Instance) ? " [" + AppConfig.Instance + "]" : "")); applicationParams.Add("Configuration", SaxSystem.DebugMode ? "Debug" : "Release"); applicationParams.Add("Environment", EnvironmentName); - applicationParams.Add("Database", SessionConfig.CoreDatabase.Location); + applicationParams.Add("Database", AppConfig.CoreDatabase.Location); applicationParams.Add("Application root", SaxSystem.ApplicationFolder); - if (!StringUtil.IsEmpty(SessionConfig.Documents.RootFolder)) - applicationParams.Add("Documents root", SessionConfig.Documents.RootFolder); - applicationParams.Add("Email context", SessionConfig.Email.Context + (!StringUtil.IsEmpty(EmailConfig.SMTP.Server) ? " [" + EmailConfig.SMTP.Server + "]" : "")); + if (!StringUtil.IsEmpty(AppConfig.Documents.RootFolder)) + applicationParams.Add("Documents root", AppConfig.Documents.RootFolder); + applicationParams.Add("Email context", AppConfig.Email.Context + (!StringUtil.IsEmpty(EmailConfig.SMTP.Server) ? " [" + EmailConfig.SMTP.Server + "]" : "")); SaLogger.Info(fn + " Application start." + applicationParams.Select(item => StringUtil.PadRight(item.Item1 + ":", 20, ' ') + item.Item2).Concat("", "\n\t"), true); if (StringUtil.IsEmpty(EmailConfig.SMTP.Server) || StringUtil.IsEmpty(EmailConfig.Message.AddressFrom)) SaLogger.Warn(fn + " Incomplete email configuration.", true); diff --git a/SaxoniteCoreWeb/Base/App/SaxStartup.cs b/SaxoniteCoreWeb/Base/App/SaxStartup.cs index 22f7361..bb193b9 100644 --- a/SaxoniteCoreWeb/Base/App/SaxStartup.cs +++ b/SaxoniteCoreWeb/Base/App/SaxStartup.cs @@ -110,7 +110,9 @@ namespace Saxonite.Net.Core.Web.Base.App .AddViewLocalization( LanguageViewLocationExpanderFormat.Suffix, opts => { opts.ResourcesPath = resourcesPath; }) - .AddDataAnnotationsLocalization(); + .AddDataAnnotationsLocalization() + // Ignore null values during object serisliation. + .AddJsonOptions(options => { options.JsonSerializerOptions.IgnoreNullValues = true; }); // Configure localisation middleware. config.services.Configure diff --git a/SaxoniteCoreWeb/Base/Controllers/SaxController_Base.cs b/SaxoniteCoreWeb/Base/Controllers/SaxController_Base.cs index d37b332..41b2148 100644 --- a/SaxoniteCoreWeb/Base/Controllers/SaxController_Base.cs +++ b/SaxoniteCoreWeb/Base/Controllers/SaxController_Base.cs @@ -61,7 +61,7 @@ namespace Saxonite.Net.Core.Web.Base.Controllers // Properties. public bool DebugMode { get; } = SacsSystem.DebugMode; protected SaxApplication_Web App { get { return SaxApplication_Web.WebApp; } } - public SaxSessionConfig SessionConfig { get { return App.SessionConfig; } } + public SaxAppConfig AppConfig { get { return App.AppConfig; } } public SaxSessionContext SessionContext { get { return App.SessionContext; } } protected SaxLocaliser Localiser { get; } /// @@ -164,10 +164,10 @@ namespace Saxonite.Net.Core.Web.Base.Controllers if (StoreSessionData) { String sessionDataJson = JsonConvert.SerializeObject(SessionData); - HttpContext.Session.Set(SessionStorage.SessionKey_SessionConfig, SessionStorage.sessionDataEncoding.GetBytes(sessionDataJson)); + HttpContext.Session.Set(SessionStorage.SessionKey_SessionData, SessionStorage.sessionDataEncoding.GetBytes(sessionDataJson)); } } - else HttpContext.Session.Remove(SessionStorage.SessionKey_SessionConfig); + else HttpContext.Session.Remove(SessionStorage.SessionKey_SessionData); // Response headers. if (baseModel != null && baseModel.ProhibitCache) @@ -293,7 +293,7 @@ namespace Saxonite.Net.Core.Web.Base.Controllers public class SessionStorage { public SaxSessionData SessionData { get; set; } - public static String SessionKey_SessionConfig { get; } = "SaxSessionData"; + public static String SessionKey_SessionData { get; } = "SaxSessionData"; public static Encoding sessionDataEncoding { get; } = Encoding.UTF8; //JSON Serialisation. public class SaxSessionDataContractResolver : DefaultContractResolver @@ -334,7 +334,7 @@ namespace Saxonite.Net.Core.Web.Base.Controllers if (sessionStorage.SessionData == null) { byte[] sessionDataBytes = null; - HttpContext.Session.TryGetValue(SessionStorage.SessionKey_SessionConfig, out sessionDataBytes); + HttpContext.Session.TryGetValue(SessionStorage.SessionKey_SessionData, out sessionDataBytes); if (sessionDataBytes != null) { String sessionDataJson = SessionStorage.sessionDataEncoding.GetString(sessionDataBytes); @@ -408,10 +408,37 @@ namespace Saxonite.Net.Core.Web.Base.Controllers //------------------------------------------------------------------- public class AjaxResponse_Data : AjaxResponse_Base { + //--------------------------------------------------------------- // Construction. protected AjaxResponse_Data(SaxModel_Base model) { } + + //--------------------------------------------------------------- + // Item data. + public class ItemData_Base + { + // Common item properties. + public String Id { 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; } + } + + //--------------------------------------------------------------- // Form data. public class FormControlValue { @@ -464,6 +491,8 @@ namespace Saxonite.Net.Core.Web.Base.Controllers } return false; } + + //--------------------------------------------------------------- // Notification - briefly displays after response received. public class NotificationData { @@ -477,6 +506,8 @@ namespace Saxonite.Net.Core.Web.Base.Controllers Notification = new NotificationData() { Title = title, Message = message }; return this; } + + //--------------------------------------------------------------- // Paged data. public class PagingData { @@ -502,6 +533,8 @@ namespace Saxonite.Net.Core.Web.Base.Controllers { Paging = new PagingData(model); } + + //--------------------------------------------------------------- } //------------------------------------------------------------------- diff --git a/SaxoniteCoreWeb/Base/Helpers/SaxSessionConfigHelper.cs b/SaxoniteCoreWeb/Base/Helpers/SaxAppConfigHelper.cs similarity index 51% rename from SaxoniteCoreWeb/Base/Helpers/SaxSessionConfigHelper.cs rename to SaxoniteCoreWeb/Base/Helpers/SaxAppConfigHelper.cs index 15d5228..e96410d 100644 --- a/SaxoniteCoreWeb/Base/Helpers/SaxSessionConfigHelper.cs +++ b/SaxoniteCoreWeb/Base/Helpers/SaxAppConfigHelper.cs @@ -10,60 +10,60 @@ using Sarand.Net.Standard.Base.Utils; namespace Saxonite.Net.Core.Web.Base.Helpers { //======================================================================= - public static class SaxSessionConfigHelper + public static class SaxAppConfigHelper { //------------------------------------------------------------------- - public static Dictionary ExtractSessionConfigs(IConfigurationSection sessionConfigsSection) + public static Dictionary ExtractAppConfigs(IConfigurationSection appConfigsSection) { // NB The set of configurations will only allow one per context. // Only the last configuration found for each context will be stored (NB usually only one of each apart from 'Defaults'). - Dictionary sessionConfigs = new Dictionary(); + Dictionary appConfigs = new Dictionary(); // Start the list with a default 'Defaults' configuration - values set to application defaults (defined in Sax). // NB This can be overridden in each level of configuration. // Settings from the 'Defaults' configuration provide values where they are missing from other configurations. // NB Coverage may not be 100% - just where it's proven needed. - SaxSessionConfig defaultsSessionConfig = new SaxSessionConfig(Sax.Config.Context_Defaults, null, null); - sessionConfigs.Add(defaultsSessionConfig.Context, defaultsSessionConfig); + SaxAppConfig defaultsAppConfig = new SaxAppConfig(Sax.Config.Context_Defaults, null, null); + appConfigs.Add(defaultsAppConfig.Context, defaultsAppConfig); // Load session configurations. - foreach (ConfigurationSection sessionConfigSection in sessionConfigsSection.GetChildren()) + foreach (ConfigurationSection appConfigSection in appConfigsSection.GetChildren()) { - if (StringUtil.IsEmpty(sessionConfigSection.Key)) continue; + if (StringUtil.IsEmpty(appConfigSection.Key)) continue; // Extract the configuration, supplying current 'Defaults' to supply missing values, and add to the list. - SaxSessionConfig sessionConfig = new SaxSessionConfig(sessionConfigSection.Key, sessionConfigSection, defaultsSessionConfig); - sessionConfigs.Remove(sessionConfigSection.Key); - sessionConfigs.Add(sessionConfigSection.Key, sessionConfig); + SaxAppConfig appConfig = new SaxAppConfig(appConfigSection.Key, appConfigSection, defaultsAppConfig); + appConfigs.Remove(appConfigSection.Key); + appConfigs.Add(appConfigSection.Key, appConfig); // Replace the 'Defaults' configuration if this one matches the context. - if (sessionConfig.Context.EqualsCI(Sax.Config.Context_Defaults)) - defaultsSessionConfig = sessionConfig; + if (appConfig.Context.EqualsCI(Sax.Config.Context_Defaults)) + defaultsAppConfig = appConfig; } - return sessionConfigs; + return appConfigs; } //------------------------------------------------------------------- } //======================================================================= - public static class SessionConfigList + public static class AppConfigList { - public static SaxSessionConfig getConfigForApplication(this List sessionConfigs) + public static SaxAppConfig getConfigForApplication(this List appConfigs) { - SaxSessionConfig defaultsSessionConfig = new SaxSessionConfig("", null, null); + SaxAppConfig defaultsAppConfig = new SaxAppConfig("", null, null); SaxApplication_Base app = SaxGlobals.App; if (app != null) { - foreach (SaxSessionConfig sessionConfig in sessionConfigs) + foreach (SaxAppConfig appConfig in appConfigs) { - if (sessionConfig.Environments.ContainsCI(app.EnvironmentName)) - return sessionConfig; - if (sessionConfig.Context.EqualsCI(Sax.Config.Context_Defaults)) - defaultsSessionConfig = sessionConfig; + if (appConfig.Environments.ContainsCI(app.EnvironmentName)) + return appConfig; + if (appConfig.Context.EqualsCI(Sax.Config.Context_Defaults)) + defaultsAppConfig = appConfig; } } - return defaultsSessionConfig; + return defaultsAppConfig; } } diff --git a/SaxoniteCoreWeb/Base/Helpers/SaxEmailConfigHelper.cs b/SaxoniteCoreWeb/Base/Helpers/SaxEmailConfigHelper.cs index d6e3452..3cdbf7e 100644 --- a/SaxoniteCoreWeb/Base/Helpers/SaxEmailConfigHelper.cs +++ b/SaxoniteCoreWeb/Base/Helpers/SaxEmailConfigHelper.cs @@ -76,13 +76,13 @@ namespace Saxonite.Net.Core.Web.Base.Helpers //======================================================================= public static class EmailConfigList { - public static SaxEmailConfig getConfigForSession(this List emailConfigs, SaxSessionConfig sessionConfig) + public static SaxEmailConfig getConfigForSession(this List emailConfigs, SaxAppConfig appConfig) { - if (sessionConfig != null && !StringUtil.IsEmpty(sessionConfig.Email.Context)) + if (appConfig != null && !StringUtil.IsEmpty(appConfig.Email.Context)) { foreach (SaxEmailConfig emailConfig in emailConfigs) { - if (emailConfig.Context.EqualsCI(sessionConfig.Email.Context)) + if (emailConfig.Context.EqualsCI(appConfig.Email.Context)) return emailConfig; } } diff --git a/SaxoniteCoreWeb/Base/Models/SaxModel_Base.cs b/SaxoniteCoreWeb/Base/Models/SaxModel_Base.cs index 6bc441f..b69b9cf 100644 --- a/SaxoniteCoreWeb/Base/Models/SaxModel_Base.cs +++ b/SaxoniteCoreWeb/Base/Models/SaxModel_Base.cs @@ -35,7 +35,7 @@ namespace Saxonite.Net.Core.Web.Base.Models //------------------------------------------------------------------- // Application properties. private SaxApplication_Web App { get { return SaxApplication_Web.WebApp; } } - protected SaxSessionConfig SessionConfig { get { return App.SessionConfig; } } + protected SaxAppConfig AppConfig { get { return App.AppConfig; } } protected SaxSystem_Web System { get { return App.System_Web; } } public String SessionCookieName { get { return App.SessionCookieName; } } public SaxSessionData SessionData { get; set; } @@ -49,7 +49,7 @@ namespace Saxonite.Net.Core.Web.Base.Models public bool OK { get { return !HasMessages(); } } public virtual bool ProhibitCache { get; set; } = true; public String PageCaption { get; set; } - public String InstanceVersion { get { return !StringUtil.IsEmpty(App.SessionConfig.Instance) ? App.SessionConfig.Instance + " v" + Sax.Version.GetFullVersion() : "v" + Sax.Version.GetAppVersion(); } } + public 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; //------------------------------------------------------------------- diff --git a/SaxoniteCoreWebAdmin/Admin/App/SaxApplication_Admin.cs b/SaxoniteCoreWebAdmin/Admin/App/SaxApplication_Admin.cs index 078c612..6288ffe 100644 --- a/SaxoniteCoreWebAdmin/Admin/App/SaxApplication_Admin.cs +++ b/SaxoniteCoreWebAdmin/Admin/App/SaxApplication_Admin.cs @@ -32,7 +32,7 @@ namespace Saxonite.Net.Core.Web.Admin.App // Data members. private bool generateDatabaseDefinitions = false; private bool populateRegions = false; - private Dictionary sessionConfigs = new Dictionary(); + private Dictionary appConfigs = new Dictionary(); private Dictionary emailConfigs = new Dictionary(); // Session contexts. private class SessionContextData @@ -75,8 +75,8 @@ namespace Saxonite.Net.Core.Web.Admin.App } public override String SessionCookieName { get { return Sax.Config.Admin.Session.Cookie.Name; } } // Configuration. - public override SaxSessionConfig SessionConfig { get { return sessionConfigs.Values.ToList().getConfigForApplication(); } } - public override SaxEmailConfig EmailConfig { get { return emailConfigs.Values.ToList().getConfigForSession(SessionConfig); } } + public override SaxAppConfig AppConfig { get { return appConfigs.Values.ToList().getConfigForApplication(); } } + public override SaxEmailConfig EmailConfig { get { return emailConfigs.Values.ToList().getConfigForSession(AppConfig); } } //------------------------------------------------------------------- // Initialisation. @@ -107,15 +107,15 @@ namespace Saxonite.Net.Core.Web.Admin.App SaxConfiguration = builder.Build(); // Application configuration. - sessionConfigs = SaxSessionConfigHelper.ExtractSessionConfigs(SaxConfiguration.GetSection("SessionConfigs")); + appConfigs = SaxAppConfigHelper.ExtractAppConfigs(SaxConfiguration.GetSection("AppConfigs")); emailConfigs = SaxEmailConfigHelper.ExtractEmailConfigs(SaxConfiguration.GetSection("EmailConfigs")); - LoggingRootFolder = SessionConfig.Logging.RootFolder; + LoggingRootFolder = AppConfig.Logging.RootFolder; // Application instance. - if (SessionConfig.Instance.EqualsCI("Dev")) instanceType = Sax.InstanceType.Dev; - else if (SessionConfig.Instance.ContainsCI("Q")) instanceType = Sax.InstanceType.QC; - else if (SessionConfig.Instance.ContainsCI("UAT")) instanceType = Sax.InstanceType.UAT; - else if (SessionConfig.Instance.EqualsCI("PreProd")) instanceType = Sax.InstanceType.UAT; + if (AppConfig.Instance.EqualsCI("Dev")) instanceType = Sax.InstanceType.Dev; + else if (AppConfig.Instance.ContainsCI("Q")) instanceType = Sax.InstanceType.QC; + else if (AppConfig.Instance.ContainsCI("UAT")) instanceType = Sax.InstanceType.UAT; + else if (AppConfig.Instance.EqualsCI("PreProd")) instanceType = Sax.InstanceType.UAT; // Initialise hard-coded logging path. if (StringUtil.IsEmpty(LoggingRootFolder)) @@ -129,9 +129,9 @@ namespace Saxonite.Net.Core.Web.Admin.App // Configure database connection. SacsDatabaseConsole.OdbcConfig config = new SacsDatabaseConsole.OdbcConfig(); - config.location = SessionConfig.CoreDatabase.Location; - config.user = SessionConfig.CoreDatabase.User; - config.pword = SessionConfig.CoreDatabase.Password; + config.location = AppConfig.CoreDatabase.Location; + config.user = AppConfig.CoreDatabase.User; + config.pword = AppConfig.CoreDatabase.Password; SacsDatabase coreDatabase = SacsDatabaseConsole.OpenOdbcDatabase(config); if (coreDatabase.HasErrorState()) SaLogger.Warn(fn + " Database initialisation error: " + coreDatabase.ErrorStateMessage); diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/HomeController.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/HomeController.cs index adb97bb..b14b098 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/HomeController.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/HomeController.cs @@ -38,7 +38,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers // Check for 'Keep Logged In' cookie. // NB Only allowed if there isn't a current user session. - if (!ValidUserSession && SessionConfig.Security.SignIn.KeepLoggedIn.Allowed) + if (!ValidUserSession && AppConfig.Security.SignIn.KeepLoggedIn.Allowed) { String encryptedParams = GetRequestCookie(CookieName_KeepLoggedIn); KeepLoggedInParams keepLoggedInParams = ExtractKeepLoggedInParams(encryptedParams); diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Activation-Ajax.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Activation-Ajax.cs index 4a0e387..d0cbf9e 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Activation-Ajax.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Activation-Ajax.cs @@ -45,7 +45,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData); - if (StringUtil.IsEmpty(SessionConfig.URL.Online.Root)) + if (StringUtil.IsEmpty(AppConfig.URL.Online.Root)) return Json(responseData.SetMessage(messageTitle, "Root online URL not specified.")); // Identify item. @@ -74,13 +74,28 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers return Json(responseData); } + //------------------------------------------------------------------- + private class OrganisationMemberData : SacsDisposable + { + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (UserOrganisation != null) UserOrganisation.Dispose(); UserOrganisation = null; + if (UserAccount != null) UserAccount.Dispose(); UserAccount = null; + } + } + public SaxUserOrganisation UserOrganisation { get; set; } + public SaxUserAccount UserAccount { get; set; } + } + //------------------------------------------------------------------- // Activate Organisation ajax request. //------------------------------------------------------------------- [HttpPost] [Route("Organisation/Ajax/Activate")] [Route("Organisation/Ajax/ActivateItem")] - public IActionResult Ajax_Activate(Model_Organisation model) + public IActionResult Ajax_Activate(Model_OrganisationActivation model) { String fn = GetType().Name + ".Ajax_Activate"; String messageTitle = Localiser["organisation.caption.organisation"]; @@ -90,65 +105,115 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData); - // Validate item properties. - model.ValidateOrganisationData(responseData); - if (responseData.HasControlErrors()) - return Json(responseData); - // Retrieve existing item. SysId organisationId = model.OrganisationId.ToSysId(); - SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationId); - if (organisation == null && SysId.IsValid(organisationId)) + using (SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationId)) { - SaLogger.Warn(fn + " Failed to retrieve organisation for Id: '" + model.OrganisationId + "'"); - return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"])); - } - - // Activate organisation. - organisation.Status = Sarand.Net.Standard.Base.Core.SaStatus.Active; - if (!AccountMgr.UpdateOrganisation(organisation, DbUpdateInfo)) - { - SaLogger.Warn(fn + " Failed to update organisation [Id:" + SysId.ToString(organisation.Id) + "]: " + AccountMgr.ErrorStateMessage); - return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + AccountMgr.ErrorStateMessage)); - } - - // Activate awaiting members and send welcome email to each. - SaxUserOrganisation.Params prmUserOrganisation = new SaxUserOrganisation.Params(); - prmUserOrganisation.OrganisationIdsIncluded.Add(organisationId); - prmUserOrganisation.UserAccountStatusCat = SaxUserAccount.StatusCat.AwaitingOnly; - prmUserOrganisation.StatusCat = SaxUserOrganisation.StatusCat.ActiveOnly; - List userOrganisations = new List(); - foreach (SaxUserOrganisation userOrganisation in userOrganisations) - { - using (SaxUserAccount userAccount = AccountMgr.GetUserAccountById(userOrganisation.UserAccountId)) + if (organisation == null && SysId.IsValid(organisationId)) { - if (userAccount == null) continue; - userAccount.Status = SaStatus.Active; - if (AccountMgr.UpdateUserAccount(userAccount, DbUpdateInfo)) + SaLogger.Warn(fn + " Failed to retrieve organisation for. : '" + model.OrganisationId + "'"); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"])); + } + + // Retrieve users. + using (DisposableCollection organisationMembersToActivate = new DisposableCollection()) + { + foreach (String userOrganisationId in model.OrganisationMemberIds) { - // Send welcome email. - SaEmailData emailData = new SaEmailData(); - emailData.Config = EmailConfig; - emailData.AddressesTo.Add(userAccount.EmailAddress); - emailData.Subject = Localiser["security.caption.request.password"]; - PopulateAccountActivatedEmailBody(emailData, userAccount); - if (EmailConsole.SendEmail(emailData)) - SaLogger.Info(fn + " Password request email sent to " + userAccount.EmailAddress, true); - else SaLogger.Warn(fn + " Failed to send password request email to " + userAccount.EmailAddress); + if (userOrganisationId == null) continue; + OrganisationMemberData organisationMember = new OrganisationMemberData(); + organisationMember.UserOrganisation = AccountMgr.GetUserOrganisationById(userOrganisationId.ToSysId()); + organisationMember.UserAccount = AccountMgr.GetUserAccountById(organisationMember.UserOrganisation?.UserAccountId ?? null); + if (organisationMember.UserOrganisation == null) + { + SaLogger.Warn(fn + " Failed to retrieve user organisation. Id: '" + userOrganisationId + "'"); + organisationMember.Dispose(); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"])); + } + if (organisationMember.UserAccount == null) + { + SaLogger.Warn(fn + " Failed to retrieve user account . Id: '" + organisationMember.UserOrganisation.UserAccountId + "'"); + organisationMember.Dispose(); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"])); + } + if (organisationMember.UserAccount.Status != SaStatus.Awaiting) + { + SaLogger.Warn(fn + " User organisation is not awaiting. Id: '" + userOrganisationId + "'. Status: " + organisationMember.UserAccount.StatusDesc); + organisationMember.Dispose(); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"])); + } + // Check if this user is to be activated. + bool activateUser = false; + String controlName_Activate = "MemberActivate_" + userOrganisationId; + foreach (String key in Request.Form.Keys) + { + if (key.EqualsCI(controlName_Activate)) + { + activateUser = true; + break; + } + } + if (activateUser) + organisationMembersToActivate.Add(organisationMember); + else organisationMember.Dispose(); + organisationMember = null; } - else + + if (organisationMembersToActivate.IsEmpty()) + { + // No users to be activated. + // Check that organisation already has active members. + if (AccountMgr.GetOrganisationMemberCount(organisation.Id) < 1) + { + SaLogger.Warn(fn + " Unable to activate Organisation - no active members. Id: '" + SysId.ToString(organisationId)); + return Json(responseData.SetMessage(messageTitle, Localiser["organisation.activate.msg.no.active.members"])); + } + } + + // Activate organisation. + organisation.Status = Sarand.Net.Standard.Base.Core.SaStatus.Active; + if (!AccountMgr.UpdateOrganisation(organisation, DbUpdateInfo)) { SaLogger.Warn(fn + " Failed to update organisation [Id:" + SysId.ToString(organisation.Id) + "]: " + AccountMgr.ErrorStateMessage); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + AccountMgr.ErrorStateMessage)); } + + // Activate awaiting members and send welcome email to each. + foreach (OrganisationMemberData organisationMember in organisationMembersToActivate) + { + // Ensure that User Organisation has 'Member Admin' role. + if (organisationMember.UserOrganisation == null) continue; + organisationMember.UserOrganisation.AddOrganisationRole(Sax.OrganisationRole.MemberAdmin); + if (AccountMgr.UpdateUserOrganisation(organisationMember.UserOrganisation, DbUpdateInfo)) + { + if (organisationMember.UserAccount == null) continue; + organisationMember.UserAccount.Status = SaStatus.Active; + if (AccountMgr.UpdateUserAccount(organisationMember.UserAccount, DbUpdateInfo)) + { + // Send activation email. + SaEmailData emailData = new SaEmailData(); + emailData.Config = EmailConfig; + emailData.AddressesTo.Add(organisationMember.UserAccount.EmailAddress); + emailData.Subject = Localiser["user.activate.email.subject"]; + PopulateAccountActivatedEmailBody(emailData, organisationMember.UserAccount); + if (EmailConsole.SendEmail(emailData)) + SaLogger.Info(fn + " User Activation email sent to " + organisationMember.UserAccount.EmailAddress, true); + else SaLogger.Warn(fn + " Failed to send User Activation email to " + organisationMember.UserAccount.EmailAddress); + } + else + SaLogger.Warn(fn + " Failed to update user account [Id:" + SysId.ToString(organisationMember.UserAccount.Id) + "]: " + AccountMgr.ErrorStateMessage); + } + else + SaLogger.Warn(fn + " Failed to update user organisation [Id:" + SysId.ToString(organisationMember.UserOrganisation.Id) + "]: " + AccountMgr.ErrorStateMessage); + } + + // Construct response. + PopulateOrganisationResponseData(organisation, responseData.organisation); } } - // Construct response. - PopulateOrganisationResponseData(organisation, responseData.organisation); - // Complete response. responseData.Success = true; - return Json(responseData); } @@ -163,7 +228,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers EmailConsole.ReplaceDynamicEmailBodyTags(emailData, null, userAccount); // Replace dynamic tag for link back to change password page. - SaEmailConsole.ReplaceDynamicEmailTag(emailData, "[#Link.RequestPassword]", SecurityController.GenerateRequestPasswordLink(this, userAccount.Id, userAccount.EmailAddress, "", SessionConfig.URL.Online.Root)); + SaEmailConsole.ReplaceDynamicEmailTag(emailData, "[#Link.RequestPassword]", SecurityController.GenerateRequestPasswordLink(this, userAccount.Id, userAccount.EmailAddress, "", AppConfig.URL.Online.Root)); SaEmailConsole.ReplaceDynamicEmailTag(emailData, "[#Link.ExpiresMinutes]", SecurityController.GetRequestPasswordEmailLinkExpiresMinutes(this).ToString()); } diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Ajax.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Ajax.cs index c8940f9..ecbeebb 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Ajax.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Ajax.cs @@ -21,9 +21,8 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers { //------------------------------------------------------------------- // Organisation data. - private class OrganisationData_List + private class OrganisationData_List : AjaxResponse_Data.ItemData_Base { - public String Id { get; set; } public String Name { get; set; } public String TypeCode { get; set; } public String TypeDesc { get; set; } @@ -38,12 +37,6 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers public String RegionNameCountry { get; set; } // Address. public String AddressSummary { get; set; } - // Common properties. - public String StatusCode { get; set; } - public String StatusDesc { get; set; } - public Boolean Awaiting { get { return StatusCode.EqualsCI(SacsSystem.GetStatusCode(SaStatus.Awaiting)); } } - public Boolean Active { get { return StatusCode.EqualsCI(SacsSystem.GetStatusCode(SaStatus.Active)); } } - public Boolean Deleted { get { return StatusCode.EqualsCI(SacsSystem.GetStatusCode(SaStatus.Deleted)); } } } private class OrganisationData_Full : OrganisationData_List { diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Members-Ajax.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Members-Ajax.cs index 0644381..9df804c 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Members-Ajax.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Members-Ajax.cs @@ -46,9 +46,9 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers data.OrganisationId = SysId.ToString(organisation.Id); data.OrganisationName = organisation.Name; // Exclude 'Member' role. - foreach (Sax.OrganisationRole organisationRole in userOrganisation.OrganisationRoles.FindAll(item => item != Sax.OrganisationRole.Member)) + foreach (Sax.OrganisationRole organisationRole in userOrganisation.GetOrganisationRoles().ToList().FindAll(item => item != Sax.OrganisationRole.Member)) data.RoleCodes.Add(OrganisationRoleCodes.GetCodeForType(organisationRole)); - data.RoleCount = userOrganisation.OrganisationRoles.Count - 1; + data.RoleCount = userOrganisation.OrganisationRoleCount - 1; data.StatusCode = userOrganisation.StatusCode; data.StatusDesc = userOrganisation.StatusDesc; } @@ -113,7 +113,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers responseData.Success = true; return Json(responseData); } - private void GetOrganisationMembers (SaxModel_Admin model, SysId organisationId, List members) + private void GetOrganisationMembers(SaxModel_Admin model, SysId organisationId, List members) { // Item filter. SaxUserOrganisation.Params prmUserOrganisation = new SaxUserOrganisation.Params(); @@ -312,8 +312,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers // Populate item properties. if (model.MemberRoleCodes != null) userOrganisation.OrganisationRoles = SaxUserOrganisation.GetOrganisationRolesFromCodes(model.GetMemberRoleCodes()); - if (!userOrganisation.OrganisationRoles.Contains(Sax.OrganisationRole.Member)) - userOrganisation.OrganisationRoles.Add(Sax.OrganisationRole.Member); + userOrganisation.AddOrganisationRole(Sax.OrganisationRole.Member); if (model.MemberStatusCode != null) userOrganisation.Status = model.MemberStatus; diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/SaxController_Admin.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/SaxController_Admin.cs index c1918ec..242b97d 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/SaxController_Admin.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/SaxController_Admin.cs @@ -57,18 +57,18 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers if (userAccount == null) { SaLogger.Error(fn + " Unable to sign account in - null user account."); - errorMessage = Localiser["security.msg.signin.failed"]; + 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.failed"]; + errorMessage = Localiser["security.msg.signin.invalid"]; return false; } // 'Keep logged in' can be switched off by the application configuration. - keepLoggedIn = App.SessionConfig.Security.SignIn.KeepLoggedIn.Allowed && keepLoggedIn; + keepLoggedIn = App.AppConfig.Security.SignIn.KeepLoggedIn.Allowed && keepLoggedIn; // Log user in. userAccount.KeepLoggedIn_Admin = keepLoggedIn; @@ -78,7 +78,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers if (!AccountMgr.UpdateUserAccount(userAccount, DbUpdateInfo)) { SaLogger.Warn(fn + " Failed to update account for " + userAccount.EmailAddress + ": " + AccountMgr.ErrorStateMessage); - errorMessage = Localiser["security.msg.signin.failed"]; + errorMessage = Localiser["security.msg.signin.invalid"]; return false; } SaLogger.Info(fn + " User session opened for " + userAccount.EmailAddress, true); @@ -119,7 +119,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers protected void SetKeepLoggedInCookie() { String keepLoggedInCookieValue = GenerateKeepLoggedInCookieValue(); - int cookieExpiresDays = Math.Max(Sax.Config.Security.SignIn.KeepLoggedIn.CookieExpiresDays_Min, Math.Min(Sax.Config.Security.SignIn.KeepLoggedIn.CookieExpiresDays_Max, SessionConfig.Security.SignIn.KeepLoggedIn.CookieExpiresDays)); + int cookieExpiresDays = Math.Max(Sax.Config.Security.SignIn.KeepLoggedIn.CookieExpiresDays_Min, Math.Min(Sax.Config.Security.SignIn.KeepLoggedIn.CookieExpiresDays_Max, AppConfig.Security.SignIn.KeepLoggedIn.CookieExpiresDays)); SetResponseCookie(CookieName_KeepLoggedIn, keepLoggedInCookieValue, DateTime.Now.AddDays(cookieExpiresDays), true); SaLogger.Info(this.GetType().Name + ".SetKeepLoggedInCookie 'Keep Logged In' cookie set. Value: " + keepLoggedInCookieValue); } diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/SecurityController-Ajax.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/SecurityController-Ajax.cs index 222c651..da6805b 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/SecurityController-Ajax.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/SecurityController-Ajax.cs @@ -79,7 +79,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers if (!ValidUserSession) return Json(responseData.SetGoHome()); - if (!SessionConfig.Security.SignIn.KeepLoggedIn.Allowed) + if (!AppConfig.Security.SignIn.KeepLoggedIn.Allowed) return Json(responseData); // Resend the cookie to extend the expiry date if it is currently populated. @@ -394,7 +394,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers } internal static int GetRequestPasswordEmailLinkExpiresMinutes(SaxController_Admin controller) { - int expiresMinutes = controller.SessionConfig.Security.SignIn.PasswordRequest.EmailLinkExpiresMins; + int expiresMinutes = controller.AppConfig.Security.SignIn.PasswordRequest.EmailLinkExpiresMins; int expiresMinutes_Min = Sax.Config.Security.SignIn.PasswordRequest.EmailLinkExpiresMins_Min; int expiresMinutes_Max = Sax.Config.Security.SignIn.PasswordRequest.EmailLinkExpiresMins_Max; return Math.Max(expiresMinutes_Min, Math.Min(expiresMinutes_Max, expiresMinutes)); diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/SecurityController.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/SecurityController.cs index ccd2bb3..2df9baf 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/SecurityController.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/SecurityController.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Primitives; using Sarand.Net.Standard.Base.App; using Sarand.Net.Standard.Base.Bus; using Sarand.Net.Standard.Base.Core; @@ -16,6 +17,7 @@ using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; +using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Web; @@ -88,7 +90,19 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers } else SaLogger.Warn(fn + " 'Request Password' parameters supplied but invalid. " + logSuffix); } - else SaLogger.Warn(fn + " 'Request Password' parameters supplied but account is not 'Admin'. " + logSuffix); + else + { + // Account is active but not 'Admin'. + // Redirect to Online application if URL is available and not already redirected ('rpe2' parameter). + String redirectParamName = ParamName_RequestPasswordEncrypted + "2"; + SaLogger.Warn(fn + " 'Request Password' parameters supplied but account is not 'Admin'. " + logSuffix); + if (!Request.Query.Keys.Contains(redirectParamName) && !StringUtil.IsEmpty(AppConfig.URL.Online.Root)) + { + UriBuilder uri = new UriBuilder(Regex.Replace(AppConfig.URL.Online.Root,"/$","") + "/Security/ChangePassword"); + uri.Query = ParamName_RequestPasswordEncrypted + "=" + Uri.EscapeDataString(encryptedParams) + "&" + redirectParamName; + return Redirect(uri.Uri.OriginalString); + } + } } else SaLogger.Warn(fn + " 'Request Password' parameters supplied but account is not active. " + logSuffix); } diff --git a/SaxoniteCoreWebAdmin/Admin/Models/Model_OrganisationActivation.cs b/SaxoniteCoreWebAdmin/Admin/Models/Model_OrganisationActivation.cs new file mode 100644 index 0000000..615a342 --- /dev/null +++ b/SaxoniteCoreWebAdmin/Admin/Models/Model_OrganisationActivation.cs @@ -0,0 +1,44 @@ +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 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.Threading.Tasks; + +namespace Saxonite.Net.Core.Web.Admin.Models +{ + //======================================================================= + /// + /// Model for Organisation Activation. + /// NB Only used for Ajax activation request. + /// + public class Model_OrganisationActivation : SaxModel_Admin + { + //------------------------------------------------------------------- + // Construction. + public Model_OrganisationActivation(SaxLocaliser localiser) : + base(localiser) + { + } + + //------------------------------------------------------------------- + // Organisation properties. + public String OrganisationId { get; set; } + /// + /// List of User Organisation Ids that were offered for activation. + /// + public List OrganisationMemberIds { get; set; } + + //------------------------------------------------------------------- + } + //======================================================================= +} diff --git a/SaxoniteCoreWebAdmin/Localisation/SaxAdminProperties.resx b/SaxoniteCoreWebAdmin/Localisation/SaxAdminProperties.resx index 9654462..a970def 100644 --- a/SaxoniteCoreWebAdmin/Localisation/SaxAdminProperties.resx +++ b/SaxoniteCoreWebAdmin/Localisation/SaxAdminProperties.resx @@ -144,6 +144,12 @@ Accounts + + Activate + + + Address + Any @@ -189,6 +195,9 @@ Delete + + Edit + Email address @@ -381,6 +390,15 @@ Value must not exceed {0} characters + + On activation, the Organisation will become visible on the platform and authorised members will be able to post content associated with the organisation. + + + Organisation canot be activated without also activating at least one member. + + + Organisation to activate: + Organisation Member @@ -501,6 +519,9 @@ Database schema is valid. + + Account Activation + Select User Roles diff --git a/SaxoniteCoreWebAdmin/Views/Organisation/Index.cshtml b/SaxoniteCoreWebAdmin/Views/Organisation/Index.cshtml index 644b0c3..a396ae5 100644 --- a/SaxoniteCoreWebAdmin/Views/Organisation/Index.cshtml +++ b/SaxoniteCoreWebAdmin/Views/Organisation/Index.cshtml @@ -8,8 +8,8 @@
@@ -139,7 +139,7 @@
@* Burger dropdown item template - ul is ignored *@
    -
  • XXX
  • +
  • ...
diff --git a/SaxoniteCoreWebAdmin/Views/Organisation/Panel-ActivationDialog.cshtml b/SaxoniteCoreWebAdmin/Views/Organisation/Panel-ActivationDialog.cshtml index 3bfe60c..2c6273f 100644 --- a/SaxoniteCoreWebAdmin/Views/Organisation/Panel-ActivationDialog.cshtml +++ b/SaxoniteCoreWebAdmin/Views/Organisation/Panel-ActivationDialog.cshtml @@ -4,6 +4,35 @@ /* Organisations - Organisation dialog panel */ } +
@@ -12,11 +41,47 @@ { @Html.HiddenFor(model => model.OrganisationId, new { @id = "", @class = "OrganisationId" }) -
Introduction
+
@Localiser["organisation.activate.intro"]
-
Organisation details.
+
@Localiser["organisation.activate.organisation.header"]
+ + + + + + + + + + + + + + + + + + + + + + +
@Localiser["label.name"]
@Localiser["label.category"]
@Localiser["label.sport"]
@Localiser["label.region"]
@Localiser["label.address"]
Members awaiting activation.
+
+
+ + + + + + +
+ + +
+
} diff --git a/SaxoniteCoreWebAdmin/Views/Security/ChangePassword.cshtml b/SaxoniteCoreWebAdmin/Views/Security/ChangePassword.cshtml index 7551b20..4b8a301 100644 --- a/SaxoniteCoreWebAdmin/Views/Security/ChangePassword.cshtml +++ b/SaxoniteCoreWebAdmin/Views/Security/ChangePassword.cshtml @@ -17,7 +17,7 @@ @if (!Model.RequestPasswordMode) { /* Current password - required when setting password within user session. */ -
+
@Html.PasswordFor(model => model.PasswordCurrent, new { @id = "ifPasswordCurrent", @class = "sax-input", @maxlength = Model.PasswordMaxLength }) @@ -33,7 +33,7 @@
-
+
@Html.PasswordFor(model => model.Password, new { @id = "ifPassword", @class = "sax-input", @maxlength = Model.PasswordMaxLength }) @@ -45,7 +45,7 @@
-
+
@Html.PasswordFor(model => model.PasswordConfirm, new { @id = "ifPasswordConfirm", @class = "sax-input", @maxlength = Model.PasswordMaxLength }) diff --git a/SaxoniteCoreWebAdmin/Views/Security/RequestPassword.cshtml b/SaxoniteCoreWebAdmin/Views/Security/RequestPassword.cshtml index e10b335..0ce0462 100644 --- a/SaxoniteCoreWebAdmin/Views/Security/RequestPassword.cshtml +++ b/SaxoniteCoreWebAdmin/Views/Security/RequestPassword.cshtml @@ -14,7 +14,7 @@ @using (@Html.BeginForm(FormMethod.Post, new { @id = "frmRequestPassword", @class = "sax-form" })) { -
+
diff --git a/SaxoniteCoreWebAdmin/Views/Security/Signin.cshtml b/SaxoniteCoreWebAdmin/Views/Security/Signin.cshtml index b66da66..0b4a538 100644 --- a/SaxoniteCoreWebAdmin/Views/Security/Signin.cshtml +++ b/SaxoniteCoreWebAdmin/Views/Security/Signin.cshtml @@ -15,7 +15,7 @@ @using (@Html.BeginForm(FormMethod.Post, new { @id = "frmSignIn", @class = "sax-form" })) { -
+
@@ -25,7 +25,7 @@
-
+
diff --git a/SaxoniteCoreWebAdmin/appsettings.Development.json b/SaxoniteCoreWebAdmin/appsettings.Development.json index 389c684..89afa5c 100644 --- a/SaxoniteCoreWebAdmin/appsettings.Development.json +++ b/SaxoniteCoreWebAdmin/appsettings.Development.json @@ -9,7 +9,7 @@ "EmailConfigs": { "SAX-SF-Dev": { "Message": { - "AddressFrom": "DoNotReply@solumforti.com" + "AddressFrom": "DoNotReply@sportierre.com" }, "SMTP": { "Server": "smtp.ionos.co.uk", @@ -18,8 +18,8 @@ }, "Send": { "Using": 2, - "UserName": "info@solumforti.com", - "Password": "solfor-info-5Ekur3" + "UserName": "info@sportierre.com", + "Password": "5p0rTi3rre-info-2021!" } } } diff --git a/SaxoniteCoreWebAdmin/appsettings.Machine.Sarand106.json b/SaxoniteCoreWebAdmin/appsettings.Machine.Sarand106.json deleted file mode 100644 index 5956827..0000000 --- a/SaxoniteCoreWebAdmin/appsettings.Machine.Sarand106.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "SessionConfigs": { - "Saxonite-Dev": { - "Environments": [ "Development" ], - "Instance": "Dev", - "CoreDatabase": { - "Location": "MsSql:Sarand106\\SQL2014.Saxonite_Dev", - "User": "saxonite", - "Password": "sax-dev-2021" - }, - "Email": { - "Context": "SAX-SF-Dev", - "ContactUs": { - "AddressesTo": [ "dev@solumforti.com" ] - } - }, - "Documents": { - "RootFolder": "D:\\Saxonite\\Data\\Docs" - }, - "Logging": { - "RootFolder": "D:\\Sarand\\Projects\\User\\AHoward\\VS2017\\Saxonite\\Logs" - } - }, - "Saxonite-Test": { - "Environments": [ "Production" ], - "Instance": "QC", - "CoreDatabase": { - "Location": "MsSql:Sarand106\\SQL2014.Saxonite_Dev", - "User": "saxonite", - "Password": "sax-dev-2021" - }, - "Email": { - "Context": "SAX-SF-Dev", - "ContactUs": { - "AddressesTo": [ "qc@solumforti.com" ], - "AddressesBcc": [ ] - - } - }, - "Documents": { - "RootFolder": "D:\\Saxonite\\Data\\Docs" - }, - "Logging": { - "RootFolder": "D:\\Sarand\\Projects\\User\\AHoward\\VS2017\\Saxonite\\QC\\Logs" - }, - "Variants": [ - { - "ApplicationRootFolder": "D:\\Sarand\\Projects\\User\\AHoward\\VS2017\\Saxonite\\QC2\\Bin", - "Instance": "QC", - "Logging": { - "RootFolder": "D:\\Sarand\\Projects\\User\\AHoward\\VS2017\\Saxonite\\QC2\\Logs" - } - } - ] - } - } -} diff --git a/SaxoniteCoreWebAdmin/appsettings.Machine.Sarand107.json b/SaxoniteCoreWebAdmin/appsettings.Machine.Sarand107.json index 086d072..990c2e6 100644 --- a/SaxoniteCoreWebAdmin/appsettings.Machine.Sarand107.json +++ b/SaxoniteCoreWebAdmin/appsettings.Machine.Sarand107.json @@ -1,11 +1,11 @@ { - "SessionConfigs": { + "AppConfigs": { "Saxonite-Dev": { "Environments": [ "Development" ], "Instance": "Dev", "URLs": { "Online": { - "Root": "https://localhost:44351/" + "Root": "https://localhost:44364/" } }, "CoreDatabase": { @@ -16,7 +16,7 @@ "Email": { "Context": "SAX-SF-Dev", "ContactUs": { - "AddressesTo": [ "dev@solumforti.com" ] + "AddressesTo": [ "info@sportierre.com" ] } }, "Documents": { @@ -37,7 +37,7 @@ "Email": { "Context": "SAX-SF-Dev", "ContactUs": { - "AddressesTo": [ "qc@solumforti.com" ], + "AddressesTo": [ "info@sportierre.com" ], "AddressesBcc": [] } diff --git a/SaxoniteCoreWebAdmin/appsettings.Machine.Sarand601.json b/SaxoniteCoreWebAdmin/appsettings.Machine.Sarand601.json index 7f7c9f5..7584296 100644 --- a/SaxoniteCoreWebAdmin/appsettings.Machine.Sarand601.json +++ b/SaxoniteCoreWebAdmin/appsettings.Machine.Sarand601.json @@ -1,11 +1,11 @@ { - "SessionConfigs": { + "AppConfigs": { "SAX-Prod": { "Environments": [ "Production" ], "Instance": "", "URLs": { "Online": { - "Root": "https://sarand.cloudapp.net:55443/" + "Root": "https://sportierre.com/" } }, "CoreDatabase": { @@ -31,14 +31,14 @@ "Context": "SAX-SF-Prod", "ContactUs": { "AddressesTo": [], - "AddressesBcc": [ "dev@solumforti.com" ] + "AddressesBcc": [ "info@sportierre.com" ] }, "Signup": { "Activate": { - "AddressesBcc": [ "signup@solumforti.com" ] + "AddressesBcc": [ "info@sportierre.com" ] }, "Welcome": { - "AddressesBcc": [ "signup@solumforti.com" ] + "AddressesBcc": [ "info@sportierre.com" ] } } }, diff --git a/SaxoniteCoreWebAdmin/appsettings.Machine.Sarand602.json b/SaxoniteCoreWebAdmin/appsettings.Machine.Sarand602.json deleted file mode 100644 index 0cc3a11..0000000 --- a/SaxoniteCoreWebAdmin/appsettings.Machine.Sarand602.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "SessionConfigs": { - "SAX-Prod": { - "Environments": [ "Production" ], - "Instance": "", - "CoreDatabase": { - "Location": "MsSql:.\\SQL2016EXP.Saxonite_Core_Live", - "User": "sax-prod-user", - "Password": "sax-live-202104" - }, - "Security": { - "Password": { - "MinLength": 8 - }, - "SignIn": { - "KeepLoggedIn": { - "Allowed": true, - "CookieExpiresDays": 30 - }, - "PasswordRequest": { - "LinkExpiresMins": 30 - } - } - }, - "Email": { - "Context": "SAX-SF-Prod", - "ContactUs": { - "AddressesTo": [], - "AddressesBcc": [ "dev@solumforti.com" ] - }, - "Signup": { - "Activate": { - "AddressesBcc": [ "signup@solumforti.com" ] - }, - "Welcome": { - "AddressesBcc": [ "signup@solumforti.com" ] - } - } - }, - "Documents": { - "RootFolder": "C:\\Sarand\\Saxonite\\Prod\\SaxoniteAdmin\\Docs" - }, - "Logging": { - "RootFolder": "C:\\Sarand\\Saxonite\\Prod\\SaxoniteAdmin\\Logs" - }, - "Variants": [ - { - "ApplicationRootFolder": "C:\\Sarand\\Saxonite\\PreProd\\SaxoniteAdmin\\Bin", - "Instance": "PreProd", - "Documents": { - "RootFolder": "C:\\Sarand\\Saxonite\\PreProd\\SaxoniteAdmin\\Docs" - }, - "Logging": { - "RootFolder": "C:\\Sarand\\Saxonite\\PreProd\\SaxoniteAdmin\\Logs" - } - } - ] - } - } -} diff --git a/SaxoniteCoreWebAdmin/appsettings.Production.json b/SaxoniteCoreWebAdmin/appsettings.Production.json index 7860010..269024f 100644 --- a/SaxoniteCoreWebAdmin/appsettings.Production.json +++ b/SaxoniteCoreWebAdmin/appsettings.Production.json @@ -2,7 +2,7 @@ "EmailConfigs": { "SAX-SF-Prod": { "Message": { - "AddressFrom": "DoNotReply@solumforti.com" + "AddressFrom": "DoNotReply@sportierre.com" }, "SMTP": { "Server": "smtp.ionos.co.uk", @@ -11,8 +11,8 @@ }, "Send": { "Using": 2, - "UserName": "info@solumforti.com", - "Password": "solfor-info-5Ekur3" + "UserName": "info@sportierre.com", + "Password": "5p0rTi3rre-info-2021!" } } } diff --git a/SaxoniteCoreWebAdmin/wwwroot/css/sax-data.css b/SaxoniteCoreWebAdmin/wwwroot/css/sax-data.css index 6d5f234..c4c240e 100644 --- a/SaxoniteCoreWebAdmin/wwwroot/css/sax-data.css +++ b/SaxoniteCoreWebAdmin/wwwroot/css/sax-data.css @@ -132,7 +132,7 @@ .sax-panel-item { - margin: 8px auto; + margin: 6px auto; border: solid 1px #979797; border-radius: 6px; -webkit-border-radius: 6px; diff --git a/SaxoniteCoreWebAdmin/wwwroot/css/sax-form.css b/SaxoniteCoreWebAdmin/wwwroot/css/sax-form.css index 134d8f2..c058511 100644 --- a/SaxoniteCoreWebAdmin/wwwroot/css/sax-form.css +++ b/SaxoniteCoreWebAdmin/wwwroot/css/sax-form.css @@ -60,7 +60,7 @@ .sax-panel-form > .sax-content { max-height: 400px; - padding: 12px 12px; + padding: 4px 8px; background-color: #f8f8f8; overflow-y: auto; } diff --git a/SaxoniteCoreWebAdmin/wwwroot/css/sax-main.css b/SaxoniteCoreWebAdmin/wwwroot/css/sax-main.css index 26a14a5..ab9b6b8 100644 --- a/SaxoniteCoreWebAdmin/wwwroot/css/sax-main.css +++ b/SaxoniteCoreWebAdmin/wwwroot/css/sax-main.css @@ -122,6 +122,13 @@ button.accept-policy line-height: inherit; } +/* Information */ + +.sax-para +{ + margin: 8px 0; +} + /* Page caption */ .sax-page-caption { diff --git a/SaxoniteCoreWebAdmin/wwwroot/js/sax-main.js b/SaxoniteCoreWebAdmin/wwwroot/js/sax-main.js index e9abe1d..824af4a 100644 --- a/SaxoniteCoreWebAdmin/wwwroot/js/sax-main.js +++ b/SaxoniteCoreWebAdmin/wwwroot/js/sax-main.js @@ -388,9 +388,9 @@ //-------------------------------------------------------------- // Data entry. - sax.clearControlErrors = function () + sax.clearControlErrors = function (context) { - var $ctrls = $('.sax-form-control').removeClass('sax-error'); + var $ctrls = $('.sax-form-control', $(context || doc.body)).removeClass('sax-error'); $('.sax-message', $ctrls).empty(); } sax.clearControlError = function (sel) @@ -404,10 +404,10 @@ var $ctrl = $(sel).closest('.sax-form-control').addClass('sax-error'); $('.sax-message', $ctrl).text(error); } - sax.refreshControlValues = function (controlValues) + sax.refreshControlValues = function (controlValues, context) { - var $frmCtrls = $('.sax-form-control').removeClass('sax-error'); - sax.clearControlErrors(); + var $frmCtrls = $('.sax-form-control', $(context || doc.body)).removeClass('sax-error'); + sax.clearControlErrors(context); var values = (controlValues || []); var valuesLen = values.length; for (var valIdx = 0; valIdx < (controlValues || []).length; valIdx++) diff --git a/SaxoniteCoreWebAdmin/wwwroot/js/sax-organisation.js b/SaxoniteCoreWebAdmin/wwwroot/js/sax-organisation.js index 9ba590a..d92b48e 100644 --- a/SaxoniteCoreWebAdmin/wwwroot/js/sax-organisation.js +++ b/SaxoniteCoreWebAdmin/wwwroot/js/sax-organisation.js @@ -265,8 +265,10 @@ sax.refreshControlValues(response.controlValues); if (!response.success) return; sax.$dlg.organisation.dialog('close'); - - var item = response.organisation; + sax.refreshItem(response.organisation); + } + sax.refreshItem=function(item) + { var $item = $('#' + sax.itemIdPrefix + item.id); if (!$item.length) { @@ -287,14 +289,48 @@ sax.showActivationInfo = function (response) { sax.debug('showActivationInfo', response); + if (!response.success || !response.organisation) return; + let item = response.organisation; + let $dlg = sax.$dlg.activate; let $frm = $('#frmOrganisationActivation'); - $('.OrganisationId', $frm).val($('.OrganisationId', sax.$frm.organisationId).val()); - sax.openDialog(sax.$dlg.activate, 480, 640); + let popItemValue = function (sel, val) + { + $(sel, $frm).toggle(!!val).find('.sax-value').text(val); + } + popItemValue('.OrganisationName', item.name); + popItemValue('.OrganisationSport', item.sportName); + popItemValue('.OrganisationRegion', item.regionNameCountry); + popItemValue('.OrganisationCategory', item.categoryDesc); + popItemValue('.OrganisationAddress', item.addressSummary); + let $orgMbrs = $('.OrganisationMembers', $dlg).empty(); + let $mbrTmpl = $('.sax-panel-item-template tr.sax-item.Member'); + sax.debug(response.members, $orgMbrs, $mbrTmpl); + for (let idx = 0; idx < response.members.length; idx++) + { + let item = response.members[idx]; + let $item = $mbrTmpl.clone(); + $('.MemberId', $item).val(item.id); + $('.MemberActivate', $item).prop('name','MemberActivate_' + item.id) + $('.MemberEmail', $item).text(item.emailAddress); + $('.MemberName', $item).text(item.displayName); + $orgMbrs.append($item); + sax.debug($item); + } + sax.openDialog($dlg, 480, 640); } - sax.axctivateOrganisation = function () + sax.activateOrganisation = function () { let $frm = $('#frmOrganisationActivation'); + sax.ajax.activateOrganisation = sax.ajax.activateOrganisation || { url: sax.url.base + 'Organisation/Ajax/Activate', formId: $frm.prop('id'), callbackFn: sax.handleAtivateResponse }; + sax.doAjaxRequest(sax.ajax.activateOrganisation, 50); + } + sax.handleAtivateResponse = function (response) + { + sax.debug('handleAtivateResponse', response); + if (!response.success) return; + sax.$dlg.activate.dialog('close'); + sax.refreshItem(response.organisation); } //-------------------------------------------------------------- @@ -328,7 +364,7 @@ sax.$dlg.organisation = $('#dlgOrganisation').dialog(opt); var opt = sax.dlgOpts(); - opt.buttons[sax.dlg.btn.ok] = function () { sax.axctivateOrganisation(); }; + opt.buttons[sax.dlg.btn.ok] = function () { sax.activateOrganisation(); }; opt.buttons[sax.dlg.btn.cancel] = function () { $(this).dialog('close'); }; sax.$dlg.activate = $('#dlgOrganisationActivation').dialog(opt); diff --git a/SaxoniteCoreWebOnline/Localisation/SaxOnlineProperties.resx b/SaxoniteCoreWebOnline/Localisation/SaxOnlineProperties.resx index 7b4ac00..71a12ba 100644 --- a/SaxoniteCoreWebOnline/Localisation/SaxOnlineProperties.resx +++ b/SaxoniteCoreWebOnline/Localisation/SaxOnlineProperties.resx @@ -117,6 +117,18 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Account Activation + + + Account Activation + + + An activation email will be sent to the selected user inviting them to assign a password and access the platform. + + + Member account has been activated and welcome email sent. + Image Upload @@ -201,6 +213,9 @@ My Account + + Activate + Add @@ -228,6 +243,9 @@ Close + + Comment + Contact details @@ -243,6 +261,9 @@ Delete + + Edit + Effective @@ -279,6 +300,12 @@ Location + + Member + + + Members + Name @@ -360,6 +387,12 @@ Available regions + + Remove + + + Roles + Save @@ -403,7 +436,7 @@ Type - update + Update Very Good @@ -441,6 +474,15 @@ System error encountered. + + Item has been created. + + + Item has been removed. + + + Item has been updated. + Thank you for registering your organisation. You will be contacted by email once the details have been confirmed. @@ -482,6 +524,12 @@ You will then be able to publicise your sporting activities. Value must not exceed {0} characters + + Add Member + + + Update Member + Organisation Opportunities @@ -503,6 +551,21 @@ You will then be able to publicise your sporting activities. Organisations + + Remove Member + + + Are you sure that this member should be removed from the organisation? + + + Member Roles + + + Organisation Members + + + You cannot remove your own 'Member Admin' role. + Privacy diff --git a/SaxoniteCoreWebOnline/Online/App/SaxApplication_Online.cs b/SaxoniteCoreWebOnline/Online/App/SaxApplication_Online.cs index ff87878..175b517 100644 --- a/SaxoniteCoreWebOnline/Online/App/SaxApplication_Online.cs +++ b/SaxoniteCoreWebOnline/Online/App/SaxApplication_Online.cs @@ -27,7 +27,7 @@ namespace Saxonite.Net.Core.Web.Online.App { //------------------------------------------------------------------- // Data members. - private Dictionary sessionConfigs = new Dictionary(); + private Dictionary appConfigs = new Dictionary(); private Dictionary emailConfigs = new Dictionary(); // Session contexts. private class SessionContextData @@ -70,8 +70,8 @@ namespace Saxonite.Net.Core.Web.Online.App } public override String SessionCookieName { get { return Sax.Config.Online.Session.Cookie.Name; } } // Configuration. - public override SaxSessionConfig SessionConfig { get { return sessionConfigs.Values.ToList().getConfigForApplication(); } } - public override SaxEmailConfig EmailConfig { get { return emailConfigs.Values.ToList().getConfigForSession(SessionConfig); } } + public override SaxAppConfig AppConfig { get { return appConfigs.Values.ToList().getConfigForApplication(); } } + public override SaxEmailConfig EmailConfig { get { return emailConfigs.Values.ToList().getConfigForSession(AppConfig); } } //------------------------------------------------------------------- // Initialisation. @@ -102,15 +102,15 @@ namespace Saxonite.Net.Core.Web.Online.App SaxConfiguration = builder.Build(); // Application configuration. - sessionConfigs = SaxSessionConfigHelper.ExtractSessionConfigs(SaxConfiguration.GetSection("SessionConfigs")); + appConfigs = SaxAppConfigHelper.ExtractAppConfigs(SaxConfiguration.GetSection("AppConfigs")); emailConfigs = SaxEmailConfigHelper.ExtractEmailConfigs(SaxConfiguration.GetSection("EmailConfigs")); - LoggingRootFolder = SessionConfig.Logging.RootFolder; + LoggingRootFolder = AppConfig.Logging.RootFolder; // Application instance. - if (SessionConfig.Instance.EqualsCI("Dev")) instanceType = Sax.InstanceType.Dev; - else if (SessionConfig.Instance.ContainsCI("Q")) instanceType = Sax.InstanceType.QC; - else if (SessionConfig.Instance.ContainsCI("UAT")) instanceType = Sax.InstanceType.UAT; - else if (SessionConfig.Instance.EqualsCI("PreProd")) instanceType = Sax.InstanceType.UAT; + if (AppConfig.Instance.EqualsCI("Dev")) instanceType = Sax.InstanceType.Dev; + else if (AppConfig.Instance.ContainsCI("Q")) instanceType = Sax.InstanceType.QC; + else if (AppConfig.Instance.ContainsCI("UAT")) instanceType = Sax.InstanceType.UAT; + else if (AppConfig.Instance.EqualsCI("PreProd")) instanceType = Sax.InstanceType.UAT; // Initialise hard-coded logging path. if (StringUtil.IsEmpty(LoggingRootFolder)) @@ -124,9 +124,9 @@ namespace Saxonite.Net.Core.Web.Online.App // Configure database connection. SacsDatabaseConsole.OdbcConfig config = new SacsDatabaseConsole.OdbcConfig(); - config.location = SessionConfig.CoreDatabase.Location; - config.user = SessionConfig.CoreDatabase.User; - config.pword = SessionConfig.CoreDatabase.Password; + config.location = AppConfig.CoreDatabase.Location; + config.user = AppConfig.CoreDatabase.User; + config.pword = AppConfig.CoreDatabase.Password; SacsDatabase coreDatabase = SacsDatabaseConsole.OpenOdbcDatabase(config); if (coreDatabase.HasErrorState()) SaLogger.Warn(fn + " Database initialisation error: " + coreDatabase.ErrorStateMessage); diff --git a/SaxoniteCoreWebOnline/Online/Controllers/AccountController-Ajax-Organisations.cs b/SaxoniteCoreWebOnline/Online/Controllers/AccountController-Ajax-Organisations.cs index cf33cee..b70d4eb 100644 --- a/SaxoniteCoreWebOnline/Online/Controllers/AccountController-Ajax-Organisations.cs +++ b/SaxoniteCoreWebOnline/Online/Controllers/AccountController-Ajax-Organisations.cs @@ -12,6 +12,8 @@ using Saxonite.Net.Standard.Base.Bus; using Sarand.Net.Standard.Base.App; using Sarand.Net.Standard.Base.Bus; using Sarand.Net.Standard.Base.Utils; +using Sarand.Net.Standard.Base.Core; +using Sarand.Net.Standard.Base.Dbas; namespace Saxonite.Net.Core.Web.Online.Controllers { @@ -23,20 +25,23 @@ namespace Saxonite.Net.Core.Web.Online.Controllers //------------------------------------------------------------------- // Account - Get Organisations. //------------------------------------------------------------------- - private class ProfileOrganisationResponseData + private class ProfileOrganisationData { - public ProfileOrganisationResponseData(SaxOrganisation organisation) - { - Name = organisation.Name; - } - public String Name { get; } + public long Id { get; set; } + public String Name { get; set; } + public long SportId { get; set; } + public String SportName { get; set; } + public long RegionId { get; set; } + public String RegionName { get; set; } + public long MemberCount { get; set; } + public long OpportunityCount { get; set; } } private class AjaxResponse_ProfileOrganisationsData : AjaxResponse_Data { // Construction. public AjaxResponse_ProfileOrganisationsData(SaxModel_Online model) : base(model) { } // Organisation list. - public List Organisations { get; } = new List(); + public List Organisations { get; } = new List(); } // Ajax request. [HttpPost] @@ -48,40 +53,68 @@ namespace Saxonite.Net.Core.Web.Online.Controllers String messageTitle = Localiser["account.caption.profile.organisations"]; // Validate session and referrer. - if (!ValidReferer) - return Forbid(); - if (!ValidUserSession) - return GetAjaxResult_InvalidSession(responseData); + if (!ValidReferer) return Forbid(); + if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData); - // Retrieve users organisations. + // Query organisations that user is a member of. SaxUserOrganisation.Params prmUserOrganisation = new SaxUserOrganisation.Params(); prmUserOrganisation.UserAccountIdsIncluded.Add(SessionData.User.UserAccountId); + prmUserOrganisation.OrganisationStatusCat = SaxOrganisation.StatusCat.ActiveOnly; prmUserOrganisation.OrganisationMembersOnly = true; prmUserOrganisation.StatusCat = SaxUserOrganisation.StatusCat.ActiveOnly; List userOrganisations = AccountMgr.GetUserOrganisationList(prmUserOrganisation); List organisationIds = userOrganisations.GetOrganisationIds(); - // Retrieve the organisation details. - List organisations = new List(); - if (!organisationIds.IsEmpty()) + // Item columns. + ColumnDfn colId = AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.Id); + ColumnDfn colOrganisationId = AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.OrganisationId); + ColumnDfn colOrganisationName = AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.Organisation_Name); + ColumnDfn colOrganisationSportId = AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.Organisation_SportId); + ColumnDfn colOrganisationSportName = AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.Organisation_Sport_Name); + ColumnDfn colOrganisationRegionId = AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.Organisation_RegionId); + ColumnDfn colOrganisationRegionName = AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.Organisation_Region_Name); + ColumnDfn colMemberCount = AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.Organisation_Member_Count); + ColumnDfn colOpportunityCount = AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.Organisation_Opportunity_Count); + ColumnDfn colStatus = AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.Status); + ColumnDfn colCreatedOn = AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.CreateOn_Date); + List columns = new List() { - SaxOrganisation.Params prmOrganisation = new SaxOrganisation.Params(); - prmOrganisation.IdsIncluded.AddRange(organisationIds); - prmOrganisation.StatusCat = SaxOrganisation.StatusCat.ActiveOnly; - prmOrganisation.SortColumns.Add(AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.Name)); - organisations.AddRange(AccountMgr.GetOrganisationList(prmOrganisation)); - } + colId, colOrganisationId , colOrganisationName, + colOrganisationSportId, colOrganisationSportName, + colOrganisationRegionId, colOrganisationRegionName, + colMemberCount, colOpportunityCount, + colStatus, colStatus, colCreatedOn + }; - // Add the organisations to the response. - foreach (SaxUserOrganisation userOrganisation in userOrganisations) + // Sort columns. + colCreatedOn.SortAscending = false; + prmUserOrganisation.SortColumns.Add(colOrganisationName); + + // Query items. + using (DbResultSet resultSet = AccountMgr.ExecuteUserOrganisationQuery(columns, prmUserOrganisation)) { - SaxOrganisation organisation = organisations.GetOrganisationById(userOrganisation.OrganisationId); - if (organisation == null) continue; - ProfileOrganisationResponseData organisationData = new ProfileOrganisationResponseData(organisation); - responseData.Organisations.Add(organisationData); + while (resultSet != null && resultSet.HasRows && resultSet.NextRecord()) + { + NamedValue.Map values = resultSet.GetRecordValues(); + ProfileOrganisationData organisationData = new ProfileOrganisationData() + { + Id = values.GetLong(colId.DataName), + Name = values.GetString(colOrganisationName.DataName), + SportId = values.GetLong(colOrganisationSportId.DataName), + SportName = values.GetString(colOrganisationSportName.DataName), + RegionId = values.GetLong(colOrganisationRegionId.DataName), + RegionName = values.GetString(colOrganisationRegionName.DataName), + MemberCount = values.GetLong(colMemberCount.DataName), + OpportunityCount = values.GetLong(colOpportunityCount.DataName) + }; + responseData.Organisations.Add(organisationData); + } } + if (model.Paging.IsPaged) + model.Paging.ItemCount = (ushort)responseData.Organisations.Count; // Complete response. + responseData.SetPaging(model); responseData.Success = true; return Json(responseData); } diff --git a/SaxoniteCoreWebOnline/Online/Controllers/AccountController-Ajax-Sports.cs b/SaxoniteCoreWebOnline/Online/Controllers/AccountController-Ajax-Sports.cs index 2f21629..a41fbc8 100644 --- a/SaxoniteCoreWebOnline/Online/Controllers/AccountController-Ajax-Sports.cs +++ b/SaxoniteCoreWebOnline/Online/Controllers/AccountController-Ajax-Sports.cs @@ -276,7 +276,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers if (!AccountMgr.InsertUserSport(userSport, DbUpdateInfo)) { SaLogger.Warn(fn + " Failed to insert user sport: " + AccountMgr.ErrorStateMessage); - return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.insert.failed"] + "\n\n" + AccountMgr.ErrorStateMessage)); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.insert.failed"])); } } else @@ -284,7 +284,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers if (!AccountMgr.UpdateUserSport(userSport, DbUpdateInfo)) { SaLogger.Warn(fn + " Failed to update user sport [Id:" + SysId.ToString(userSport.Id) + "]: " + AccountMgr.ErrorStateMessage); - return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + AccountMgr.ErrorStateMessage)); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"])); } } diff --git a/SaxoniteCoreWebOnline/Online/Controllers/HomeController.cs b/SaxoniteCoreWebOnline/Online/Controllers/HomeController.cs index ee215b7..899a2da 100644 --- a/SaxoniteCoreWebOnline/Online/Controllers/HomeController.cs +++ b/SaxoniteCoreWebOnline/Online/Controllers/HomeController.cs @@ -36,7 +36,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers // Check for 'Keep Logged In' cookie. // NB Only allowed if there isn't a current user session. - if (!ValidUserSession && SessionConfig.Security.SignIn.KeepLoggedIn.Allowed) + if (!ValidUserSession && AppConfig.Security.SignIn.KeepLoggedIn.Allowed) { String encryptedParams = GetRequestCookie(CookieName_KeepLoggedIn); KeepLoggedInParams keepLoggedInParams = ExtractKeepLoggedInParams(encryptedParams); diff --git a/SaxoniteCoreWebOnline/Online/Controllers/JournalistController-Ajax.cs b/SaxoniteCoreWebOnline/Online/Controllers/JournalistController-Ajax.cs index 7ceebbb..c4b3349 100644 --- a/SaxoniteCoreWebOnline/Online/Controllers/JournalistController-Ajax.cs +++ b/SaxoniteCoreWebOnline/Online/Controllers/JournalistController-Ajax.cs @@ -454,7 +454,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers if (!ActivityMgr.InsertJournalistArticle(article, DbUpdateInfo)) { SaLogger.Warn(fn + " Failed to insert article: " + ActivityMgr.ErrorStateMessage); - return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.insert.failed"] + "\n\n" + ActivityMgr.ErrorStateMessage)); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.insert.failed"])); } } @@ -512,7 +512,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers if (!ActivityMgr.UpdateJournalistArticle(article, DbUpdateInfo)) { SaLogger.Warn(fn + " Failed to update article [Id:" + SysId.ToString(article.Id) + "]: " + ActivityMgr.ErrorStateMessage); - return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + ActivityMgr.ErrorStateMessage)); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"])); } // Store Article Activity Index data. diff --git a/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController-Ajax-Members.cs b/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController-Ajax-Members.cs new file mode 100644 index 0000000..80af8bc --- /dev/null +++ b/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController-Ajax-Members.cs @@ -0,0 +1,707 @@ +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.Dbas; +using Sarand.Net.Standard.Base.Email; +using Sarand.Net.Standard.Base.Utils; +using Saxonite.Net.Core.Web.Online.Models; +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.Online.Controllers +{ + //======================================================================= + // Online Organisation Controller - Members - Ajax actions. + //======================================================================= + partial class OrganisationController + { + //------------------------------------------------------------------- + // Organisation Member data. + private class MemberData_List : AjaxResponse_Data.ItemData_Base + { + public String UserAccountId { get; set; } + public bool? CurrentUser { get; set; } = null; + public String EmailAddress { get; set; } + public String DisplayName { get; set; } + public List RoleCodes { get; set; } = new List(); + public List Roles { get; set; } = new List(); + public String AdminComment { get; set; } + } + private class MemberData_Full : MemberData_List + { + public String Title { get; set; } + public String FirstName { get; set; } + public String LastName { get; set; } + } + private void PopulateMemberResponseData(Model_Organisation_Members model, SaxUserOrganisation userOrganisation, SaxUserAccount userAccount, SaxOrganisation organisation, MemberData_List data) + { + data.Id = SysId.ToString(userOrganisation.Id); + data.UserAccountId = SysId.ToString(userOrganisation.UserAccountId); + if (userOrganisation.UserAccountId.Equals(SessionData.User.UserAccountId)) data.CurrentUser = true; + data.EmailAddress = userAccount.EmailAddress; + data.DisplayName = userAccount.GetDisplayName(); + data.RoleCodes.Clear(); + data.Roles.Clear(); + foreach (String memberRoleCode in userOrganisation.GetOrganisationRoleCodes()) + { + if (StringUtil.IsEmpty(memberRoleCode.Trim())) continue; + Sax.OrganisationRole organisationRole = (Sax.OrganisationRole)OrganisationRoleCodes.GetTypeForCode(memberRoleCode); + if (organisationRole <= 0 || organisationRole == Sax.OrganisationRole.Member) continue; + data.RoleCodes.Add(memberRoleCode); + data.Roles.Add(memberRoleCode, Localiser.GetOrganisationRoleCaption(organisationRole)); + } + data.AdminComment = userOrganisation.AdminComment; + // NB UserAccount status (not UserOrganisation). + data.StatusCode = userAccount.StatusCode; + data.StatusDesc = userAccount.StatusDesc; + data.CreatedOnFmt = Localiser.FormatDate(userOrganisation.CreateStamp.On); + } + private void PopulateMemberResponseData(Model_Organisation_Members model, SaxUserOrganisation userOrganisation, SaxUserAccount userAccount, SaxOrganisation organisation, MemberData_Full data) + { + PopulateMemberResponseData(model, userOrganisation, userAccount, organisation, data as MemberData_List); + data.Title = userAccount.Title; + data.FirstName = userAccount.FirstName; + data.LastName = userAccount.LastName; + } + + //------------------------------------------------------------------- + // Get Member List ajax request. + //------------------------------------------------------------------- + private class AjaxResponse_MemberList : AjaxResponse_Data + { + // Construction. + public AjaxResponse_MemberList(SaxModel_Online model) : base(model) { } + // Member list. + public List Members { get; } = new List(); + // User profile. + public class UserData + { + public bool CanManage { get; set; } + } + public UserData User { get; } = new UserData(); + // Reference data. + public List Titles { get; } = new List(); + public List MemberRoles { get; } = new List(); + } + //------------------------------------------------------------------- + [HttpPost] + [Route("Organisation/Ajax/GetMemberList")] + [Route("Organisation/Ajax/GetMembers")] + public IActionResult Ajax_GetMemberList(Model_Organisation_Members model) + { + String fn = GetType().Name + ".Ajax_GetMemberList"; + String messageTitle = Localiser["organisation.members"]; + AjaxResponse_MemberList responseData = new AjaxResponse_MemberList(model); + + // Validate session and referrer. + if (!ValidReferer) return Forbid(); + if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData); + + // Organisation. + SysId organisationId = model.Filter.OrganisationId.ToSysId(); + SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationId); + if (organisation == null || !organisation.IsStatusActive) + { + SaLogger.Warn(fn + " " + (organisation == null ? "Invalid" : "Inactive") + " organisation for Member list. OrganisationId: '" + model.Filter.OrganisationId + "'"); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"])); + } + + if (organisation != null) + { + // Store filter data. + SessionData_Online.Filter.SetMemberOrganisation(organisation); + + // Reference data - titles. + foreach (TypeCodeDesc title in SaxSystem.GetCodesOfType(Sax.CodeType.Title)) + // ToDo Title translations. + responseData.Titles.Add(title.Code, title.Desc); + + // Reference data - member roles. + foreach (TypeCodeDesc memberRole in OrganisationRoleCodes.FindAll( item => item.Type != (int)Sax.OrganisationRole.Member)) + responseData.MemberRoles.Add(memberRole.Code, Localiser.GetOrganisationRoleCaption((Sax.OrganisationRole)memberRole.Type)); + + // Session User roles. + responseData.User.CanManage = AccountMgr.UserHasOrganisationRole(SessionData.User.UserAccountId, organisationId, Sax.OrganisationRole.MemberAdmin); + + // Item filter. + SaxUserOrganisation.Params prmMember = new SaxUserOrganisation.Params(); + prmMember.OrganisationIdsIncluded.Add(organisationId); + prmMember.UserAccountStatusCat = responseData.User.CanManage ? SaxUserAccount.StatusCat.AwaitingOrActive : SaxUserAccount.StatusCat.ActiveOnly; + prmMember.OrganisationMembersOnly = true; + //prmMember.MemberFilter = model.Filter.Member; + prmMember.StatusCat = SaxUserOrganisation.StatusCat.ActiveOnly; + model.Paging.ItemTotal = (ulong)AccountMgr.GetUserOrganisationCount(prmMember); + model.PopulatePagingTo(prmMember.ResultsPaging); + + // Item columns. + ColumnDfn colId = AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.Id); + ColumnDfn colUserAccountId = AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.UserAccountId); + ColumnDfn colEmailAddress = responseData.User.CanManage ? AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.User_EmailAddress) : new ColumnDfn(-1, ""); + ColumnDfn colDisplayName = AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.User_DisplayName); + ColumnDfn colOrganisationRoleCodes = responseData.User.CanManage ? AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.OrganisationRoleCodes) : new ColumnDfn(-1, ""); + ColumnDfn colUserStatus = AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.User_Status); + ColumnDfn colAdminComment = responseData.User.CanManage ? AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.AdminComment) : new ColumnDfn(-1, ""); + ColumnDfn colCreatedOn = AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.CreateOn_Date); + List columns = new List() + { + colId, colUserAccountId , colEmailAddress, colDisplayName, + colOrganisationRoleCodes, colUserStatus, + colAdminComment, colCreatedOn + }; + + // Sort columns. + colCreatedOn.SortAscending = false; + prmMember.SortColumns.Add(colDisplayName); + + // Query items. + using (DbResultSet resultSet = AccountMgr.ExecuteUserOrganisationQuery(columns, prmMember)) + { + while (resultSet != null && resultSet.HasRows && resultSet.NextRecord()) + { + NamedValue.Map values = resultSet.GetRecordValues(); + MemberData_List memberData = new MemberData_List() + { + Id = values.GetString(colId.DataName), + UserAccountId = values.GetString(colUserAccountId.DataName), + EmailAddress = values.GetString(colEmailAddress.DataName), + DisplayName = AccountMgr.GetUserOrganisationDataValue(colDisplayName, values).ToString(), + CreatedOnFmt = Localiser.FormatDate(values.GetDateTime(colCreatedOn.DataName)), + AdminComment = values.GetString(colAdminComment.DataName), + // NB UserAccount status (not UserOrganisation). + StatusCode = values.GetString(colUserStatus.DataName), + StatusDesc = SacsSystem.GetStatusDescForCode(values.GetString(colUserStatus.DataName)) + }; + if (memberData.UserAccountId.ToSysId().Equals(SessionData.User.UserAccountId)) memberData.CurrentUser = true; + String memberRoleCodes = values.GetString(colOrganisationRoleCodes.DataName); + foreach (String memberRoleCode in StringListUtil.Split(memberRoleCodes, SaxUserOrganisation.UserOrgnisationRoleDelim)) + { + if (StringUtil.IsEmpty(memberRoleCode.Trim())) continue; + Sax.OrganisationRole organisationRole = (Sax.OrganisationRole)OrganisationRoleCodes.GetTypeForCode(memberRoleCode); + if (organisationRole <= 0 || organisationRole == Sax.OrganisationRole.Member) continue; + memberData.RoleCodes.Add(memberRoleCode); + memberData.Roles.Add(memberRoleCode, Localiser.GetOrganisationRoleCaption(organisationRole)); + } + responseData.Members.Add(memberData); + } + } + } + if (model.Paging.IsPaged) + model.Paging.ItemCount = (ushort)responseData.Members.Count; + + // Complete response. + responseData.SetPaging(model); + responseData.Success = true; + return Json(responseData); + } + + //------------------------------------------------------------------- + // Get Member ajax request. + //------------------------------------------------------------------- + private class AjaxResponse_Member : AjaxResponse_Data + { + // Construction. + public AjaxResponse_Member(SaxModel_Online model) : base(model) { } + // Member details. + public MemberData_Full Member { get; } = new MemberData_Full(); + } + //------------------------------------------------------------------- + [HttpPost] + [Route("Organisation/Ajax/GetMember")] + public IActionResult Ajax_GetMember(Model_Organisation_Members model) + { + String fn = GetType().Name + ".Ajax_GetMember"; + String messageTitle = Localiser["label.member"]; + AjaxResponse_Member responseData = new AjaxResponse_Member(model); + + // Validate session and referrer. + if (!ValidReferer) return Forbid(); + if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData); + + // Identify item. + SysId memberId = model.MemberId.ToSysId(); + if (!SysId.IsValid(memberId)) + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.specified"])); + + // Retrieve item. + using (SaxUserOrganisation userOrganisation = AccountMgr.GetUserOrganisationById(memberId)) + { + if (userOrganisation == null) + { + SaLogger.Warn(fn + " Failed to retrieve Organisation Member for Id: '" + model.MemberId + "'"); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"])); + } + + // Validate organisation. + using (SaxOrganisation organisation = AccountMgr.GetOrganisationById(userOrganisation.OrganisationId)) + { + if (organisation == null || !organisation.IsStatusActive) + { + SaLogger.Warn(fn + " " + (organisation == null ? "Invalid" : "Inactive") + " Organisation for Member. Id: '" + userOrganisation.OrganisationId + "'"); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"])); + } + + // Session User roles. + bool userCanManageMembers = AccountMgr.UserHasOrganisationRole(SessionData.User.UserAccountId, organisation.Id, Sax.OrganisationRole.MemberAdmin); + + // Validate user account. + using (SaxUserAccount userAccount = AccountMgr.GetUserAccountById(userOrganisation.UserAccountId)) + { + bool userActive = userAccount.IsStatusActive || (userAccount.IsStatusAwaiting && userCanManageMembers); + if (userAccount == null || !userActive) + { + SaLogger.Warn(fn + " " + (organisation == null ? "Invalid" : "Inactive") + " User Account for Member. Id: '" + userOrganisation.UserAccountId + "'"); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"])); + } + + // Make a note of the selected Organisation. + SessionData_Online.Filter.SetMemberOrganisation(organisation); + + PopulateMemberResponseData(model, userOrganisation, userAccount, organisation, responseData.Member); + } + } + } + + // Construct response. + responseData.Success = true; + return Json(responseData); + } + + //------------------------------------------------------------------- + // Store Member ajax request. + //------------------------------------------------------------------- + private class AjaxResponse_StoreMember : AjaxResponse_Data + { + // Construction. + public AjaxResponse_StoreMember(SaxModel_Online model) : base(model) { } + // Member details. + public MemberData_Full Member { get; } = new MemberData_Full(); + // Feedback. + public bool? MemberNameRequired { get; set; } = null; + } + [HttpPost] + [Route("Organisation/Ajax/StoreMember")] + public IActionResult Ajax_StoreMember(Model_Organisation_Members model) + { + String fn = GetType().Name + ".Ajax_StoreMember"; + String messageTitle = Localiser["label.member"]; + AjaxResponse_StoreMember responseData = new AjaxResponse_StoreMember(model); + + // Validate session and referrer. + if (!ValidReferer) return Forbid(); + if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData); + + // Validate item properties. + model.ValidateMemberData(responseData); + if (responseData.HasControlErrors()) + return Json(responseData); + + //--------------------------------------------------------------- + // User Organisation identifiers. + // New Member: + // Organisation Id [MemberOrganisationId]. + // Email address [MemberEmailAddress]. + // Existing member: + // User Organisation Id [MemberId]. + //--------------------------------------------------------------- + + // Validate Member against Id against 'Exists' flag. + SysId memberId = model.MemberId.ToSysId(); + bool memberExists = StringUtil.ToBool(model.MemberExists); + if (memberExists && !SysId.IsValid(memberId)) + { + SaLogger.Warn(fn + " Id not specified for existing User Organisation."); + return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); + } + if (!memberExists && SysId.IsValid(memberId)) + { + SaLogger.Warn(fn + " Id specified for new User Organisation: '" + model.MemberId + "'"); + return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); + } + + // Record disposable items as they are created so all can be disposed when needed. + using (DisposableCollection disposableItems = new DisposableCollection()) + { + + // Retrieve existing item. + SaxUserOrganisation userOrganisation = AccountMgr.GetUserOrganisationById(memberId); + disposableItems.Add(userOrganisation); + if (userOrganisation == null && SysId.IsValid(memberId)) + { + SaLogger.Warn(fn + " Failed to retrieve User Organisation for Id: '" + model.MemberId + "'"); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"])); + } + + // Validate the Organisation. + SysId organisationId = userOrganisation != null ? userOrganisation.OrganisationId : model.MemberOrganisationId.ToSysId(); + if (!SysId.IsValid(organisationId)) + { + SaLogger.Warn(fn + " Organisation Id not specified for Member."); + return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); + } + SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationId); + disposableItems.Add(organisation); + if (organisation == null || !organisation.IsStatusActive) + { + SaLogger.Warn(fn + " " + (organisation == null ? "Invalid" : "Inactive") + " Organisation for User Organisation. OrganisationId: '" + model.MemberOrganisationId + "'"); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"])); + } + if (userOrganisation != null && !userOrganisation.OrganisationId.Equals(organisationId)) + { + SaLogger.Warn(fn + " Inconsistent Organisation Id specified for User Organisation. Expected: " + userOrganisation.OrganisationId + ". Found: " + organisationId); + return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); + } + + // Ensure that the user has the correct role to update Members for this organisation. + bool userCanManageMembers = AccountMgr.UserHasOrganisationRole(SessionData.User.UserAccountId, organisationId, Sax.OrganisationRole.MemberAdmin); + if (!userCanManageMembers) + { + SaLogger.Warn(fn + " User doesn't have 'Member Admin' role for Organisation Id: " + organisationId); + return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); + } + + // Validate the User Account for an existing member. + SaxUserAccount userAccount = null; + if (userOrganisation != null) + { + userAccount = AccountMgr.GetUserAccountById(userOrganisation.UserAccountId); + disposableItems.Add(userAccount); + bool userActive = userAccount.IsStatusActive || (userAccount.IsStatusAwaiting && userCanManageMembers); + if (userAccount == null || !userActive) + { + SaLogger.Warn(fn + " " + (userAccount == null ? "Invalid" : "Inactive") + " User Account for User Organisation. UserAccountId: '" + userOrganisation.UserAccountId + "'"); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"])); + } + if (userOrganisation != null && !userOrganisation.UserAccountId.Equals(userAccount.Id)) + { + SaLogger.Warn(fn + " Inconsistent User Account Id specified for User Organisation. Expected: " + userOrganisation.UserAccountId + ". Found: " + userAccount.Id); + return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); + } + } + + // New member - Lookup user by email and create new account if needed. + if (userAccount == null) + { + // Check if account exists for email. + userAccount = AccountMgr.GetUserAccountByEmailAddress(model.MemberEmailAddress); + if (userAccount != null) + { + // Existing User Account - get User Organisation if available. + userOrganisation = AccountMgr.GetUserOrganisationByUserOrganisation(userAccount.Id, organisation.Id); + disposableItems.Add(userOrganisation); + } + else + { + // If name hasn't been specified, return to page to complete details. + if (!StringUtil.ToBool(model.MemberNameSupplied)) + { + responseData.SetControlError(nameof(model.MemberFirstName), Localiser["msg.validation.mandatory.value"]); + responseData.SetControlError(nameof(model.MemberLastName), Localiser["msg.validation.mandatory.value"]); + responseData.MemberNameRequired = true; + responseData.Success = true; + return Json(responseData); + } + + // Create new User Account. + userAccount = AccountMgr.CreateUserAccount(); + userAccount.EmailAddress = model.MemberEmailAddress; + userAccount.Status = SaStatus.Awaiting; + } + disposableItems.Add(userAccount); + } + + // Store user account. + if (StringUtil.ToBool(model.MemberNameSupplied)) + { + if (model.MemberTitle != null) + userAccount.Title = model.MemberTitle; + if (model.MemberFirstName != null) + userAccount.FirstName = model.MemberFirstName; + if (model.MemberLastName != null) + userAccount.LastName = model.MemberLastName; + } + if (!SysId.IsValid(userAccount.Id)) + { + if (!AccountMgr.InsertUserAccount(userAccount, DbUpdateInfo)) + { + SaLogger.Warn(fn + " Failed to insert User Account: " + AccountMgr.ErrorStateMessage); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.insert.failed"])); + } + } + else + { + if (!AccountMgr.UpdateUserAccount(userAccount, DbUpdateInfo)) + { + SaLogger.Warn(fn + " Failed to insert User Account: " + AccountMgr.ErrorStateMessage); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"])); + } + } + + // Create new item if required. + if (userOrganisation == null) + { + userOrganisation = AccountMgr.CreateUserOrganisation(userAccount.Id, organisation.Id); + disposableItems.Add(userOrganisation); + } + + // User Organisation Roles. + if (model.MemberRoleCodes != null) + userOrganisation.OrganisationRoles = SaxUserOrganisation.GetOrganisationRolesFromCodes(model.GetMemberRoleCodes()); + // Ensure user has member role. + userOrganisation.AddOrganisationRole(Sax.OrganisationRole.Member); + + // Validate roles. + if (userOrganisation.UserAccountId.Equals(SessionData.User.UserAccountId)) + { + // User is modifying their own member. + if (!userOrganisation.HasOrganisationRole(Sax.OrganisationRole.MemberAdmin)) + { + SaLogger.Warn(fn + " Unable to store member - user is attempting to remove their own 'Member Admin' role."); + return Json(responseData.SetMessage(messageTitle, Localiser["organisation.members.msg.remove.own.admin.role"])); + } + } + + // User organisation properties. + if (model.MemberAdminComment != null) + userOrganisation.AdminComment = model.MemberAdminComment; + userOrganisation.Status = SaStatus.Active; + + // Store item. + if (!SysId.IsValid(userOrganisation.Id)) + { + if (!AccountMgr.InsertUserOrganisation(userOrganisation, DbUpdateInfo)) + { + SaLogger.Warn(fn + " Failed to insert User Organisation: " + AccountMgr.ErrorStateMessage); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.insert.failed"])); + } + } + else + { + if (!AccountMgr.UpdateUserOrganisation(userOrganisation, DbUpdateInfo)) + { + SaLogger.Warn(fn + " Failed to update User Organisation [Id:" + SysId.ToString(userOrganisation.Id) + "]: " + AccountMgr.ErrorStateMessage); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"])); + } + } + + // Construct response. + PopulateMemberResponseData(model, userOrganisation, userAccount, organisation, responseData.Member); + } + + // Complete response. + responseData.Success = true; + return Json(responseData); + } + + //------------------------------------------------------------------- + // Activate Member ajax request. + //------------------------------------------------------------------- + private class AjaxResponse_ActivateMember : AjaxResponse_Data + { + // Construction. + public AjaxResponse_ActivateMember(SaxModel_Online model) : base(model) { } + // Member details. + public MemberData_Full Member { get; } = new MemberData_Full(); + } + [HttpPost] + [Route("Organisation/Ajax/ActivateMember")] + public IActionResult Ajax_ActivateMember(Model_Organisation_Members model) + { + String fn = GetType().Name + ".Ajax_ActivateMember"; + String messageTitle = Localiser["label.member"]; + AjaxResponse_ActivateMember responseData = new AjaxResponse_ActivateMember(model); + + // Validate session and referrer. + if (!ValidReferer) return Forbid(); + 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()) + { + + // Retrieve User Organisation [MemberId). + SysId userOrganisationId = model.MemberId.ToSysId(); + SaxUserOrganisation userOrganisation = AccountMgr.GetUserOrganisationById(userOrganisationId); + disposableItems.Add(userOrganisation); + if (userOrganisation == null || !userOrganisation.IsStatusActive) + { + SaLogger.Warn(fn + " User Organisation not found or not active. Id: " + SysId.ToString(userOrganisationId)); + return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); + } + + // Retrieve the Organisation. + SaxOrganisation organisation = AccountMgr.GetOrganisationById(userOrganisation.OrganisationId); + disposableItems.Add(organisation); + if (organisation == null || !organisation.IsStatusActive) + { + SaLogger.Warn(fn + " Organisation not found or not active. Id: " + SysId.ToString(userOrganisation.OrganisationId)); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"])); + } + + // Ensure that the user has the correct role to update Members for this organisation. + bool userCanManageMembers = AccountMgr.UserHasOrganisationRole(SessionData.User.UserAccountId, organisation.Id, Sax.OrganisationRole.MemberAdmin); + if (!userCanManageMembers) + { + SaLogger.Warn(fn + " User doesn't have 'Member Admin' role for Organisation Id: " + organisation.Id); + return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); + } + + // Retrieve User Account. + SaxUserAccount userAccount = null; + userAccount = AccountMgr.GetUserAccountById(userOrganisation.UserAccountId); + disposableItems.Add(userAccount); + if (userAccount == null) + { + SaLogger.Warn(fn + " Failed to retrieve User Account. Id: " + userOrganisation.UserAccountId); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"])); + } + if (!userAccount.IsStatusAwaiting) + { + SaLogger.Warn(fn + " User Account is not awaiting. Id: " + userOrganisation.UserAccountId + ". Status: " + userAccount.StatusDesc); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"])); + } + + // Update user's status. + userAccount.Status = SaStatus.Active; + if (AccountMgr.UpdateUserAccount(userAccount, DbUpdateInfo)) + { + // Send activation email. + SaEmailData emailData = new SaEmailData(); + emailData.Config = EmailConfig; + emailData.AddressesTo.Add(userAccount.EmailAddress); + emailData.Subject = Localiser["account.activate.email.subject"]; + PopulateAccountActivatedEmailBody(emailData, userAccount); + if (EmailConsole.SendEmail(emailData)) + SaLogger.Info(fn + " User Activation email sent to " + userAccount.EmailAddress, true); + else SaLogger.Warn(fn + " Failed to send User Activation email to " + userAccount.EmailAddress); + // Treat the activation as successful regardless of whether the email was sent. + responseData.SetNotification(messageTitle, Localiser["account.activate.notify"]); + } + else + { + SaLogger.Warn(fn + " Failed to insert User Account: " + AccountMgr.ErrorStateMessage); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"])); + } + + // Construct response. + PopulateMemberResponseData(model, userOrganisation, userAccount, organisation, responseData.Member); + } + + // Complete response. + responseData.Success = true; + return Json(responseData); + } + + //------------------------------------------------------------------- + private void PopulateAccountActivatedEmailBody(SaEmailData emailData, SaxUserAccount userAccount) + { + // Will need to handle culture (language/country) as and when introduced. + emailData.MessageBody = SaxResourceHelper.GetTextResource(SaxResourceHelper.TextResource.Email_Security_AccountActivated_Html); + emailData.HtmlBody = true; + + // Replace dynamic tags. + EmailConsole.ReplaceDynamicEmailBodyTags(emailData, null, userAccount); + + // Replace dynamic tag for link back to change password page. + SaEmailConsole.ReplaceDynamicEmailTag(emailData, "[#Link.RequestPassword]", SecurityController.GenerateRequestPasswordLink(this, userAccount.Id, userAccount.EmailAddress, "")); + SaEmailConsole.ReplaceDynamicEmailTag(emailData, "[#Link.ExpiresMinutes]", SecurityController.GetRequestPasswordEmailLinkExpiresMinutes(this).ToString()); + } + + //------------------------------------------------------------------- + // Remove Member ajax request. + //------------------------------------------------------------------- + private class AjaxResponse_RemoveMember : AjaxResponse_Data + { + // Construction. + public AjaxResponse_RemoveMember(SaxModel_Online model) : base(model) { } + // Member details. + public MemberData_Full Member { get; } = new MemberData_Full(); + } + [HttpPost] + [Route("Organisation/Ajax/RemoveMember")] + public IActionResult Ajax_RemoveMember(Model_Organisation_Members model) + { + String fn = GetType().Name + ".Ajax_RemoveMember"; + String messageTitle = Localiser["label.member"]; + AjaxResponse_RemoveMember responseData = new AjaxResponse_RemoveMember(model); + + // Validate session and referrer. + if (!ValidReferer) return Forbid(); + 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()) + { + + // Retrieve User Organisation [MemberId). + SysId userOrganisationId = model.MemberId.ToSysId(); + SaxUserOrganisation userOrganisation = AccountMgr.GetUserOrganisationById(userOrganisationId); + disposableItems.Add(userOrganisation); + if (userOrganisation == null || !userOrganisation.IsStatusActive) + { + SaLogger.Warn(fn + " User Organisation not found or not active. Id: " + SysId.ToString(userOrganisationId)); + return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); + } + + // Retrieve the Organisation. + SaxOrganisation organisation = AccountMgr.GetOrganisationById(userOrganisation.OrganisationId); + disposableItems.Add(organisation); + if (organisation == null || !organisation.IsStatusActive) + { + SaLogger.Warn(fn + " Organisation not found or not active. Id: " + SysId.ToString(userOrganisation.OrganisationId)); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"])); + } + + // Ensure that the user has the correct role to update Members for this organisation. + bool userCanManageMembers = AccountMgr.UserHasOrganisationRole(SessionData.User.UserAccountId, organisation.Id, Sax.OrganisationRole.MemberAdmin); + if (!userCanManageMembers) + { + SaLogger.Warn(fn + " User doesn't have 'Member Admin' role for Organisation Id: " + organisation.Id); + return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); + } + + // Retrieve User Account. + SaxUserAccount userAccount = null; + userAccount = AccountMgr.GetUserAccountById(userOrganisation.UserAccountId); + disposableItems.Add(userAccount); + if (userAccount == null) + { + SaLogger.Warn(fn + " Failed to retrieve User Account. Id: " + userOrganisation.UserAccountId); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"])); + } + if (!userAccount.IsStatusAwaiting && !userAccount.IsStatusActive) + { + SaLogger.Warn(fn + " User Account is not awaiting or active. Id: " + userOrganisation.UserAccountId); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"])); + } + + // Remove User Organisation roles. + userOrganisation.ClearOrganisationRoles(); + if (AccountMgr.UpdateUserOrganisation(userOrganisation, DbUpdateInfo)) + { + responseData.SetNotification(messageTitle, Localiser["msg.item.removed"]); + } + else + { + SaLogger.Warn(fn + " Failed to update User Organisation: " + AccountMgr.ErrorStateMessage); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"])); + } + + // Construct response. + PopulateMemberResponseData(model, userOrganisation, userAccount, organisation, responseData.Member); + } + + // Complete response. + responseData.Success = true; + return Json(responseData); + } + + //------------------------------------------------------------------- + } + //======================================================================= +} diff --git a/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController-Ajax-Opportunities.cs b/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController-Ajax-Opportunities.cs index 75aee3c..4d00314 100644 --- a/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController-Ajax-Opportunities.cs +++ b/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController-Ajax-Opportunities.cs @@ -21,9 +21,8 @@ namespace Saxonite.Net.Core.Web.Online.Controllers { //------------------------------------------------------------------- // Organisation Opportunity data. - private class OpportunityData_List + private class OpportunityData_List : AjaxResponse_Data.ItemData_Base { - public String Id { get; set; } public String TypeCode { get; set; } public String TypeDesc { get; set; } public String Position { get; set; } @@ -32,9 +31,6 @@ namespace Saxonite.Net.Core.Web.Online.Controllers public String SportCaption { get; set; } public String PublishedOnFmt { get; set; } public String ExpiresOnFmt { get; set; } - public String CreatedOnFmt { get; set; } - public String StatusCode { get; set; } - public String StatusDesc { get; set; } } private class OpportunityData_Full : OpportunityData_List { @@ -52,9 +48,9 @@ namespace Saxonite.Net.Core.Web.Online.Controllers data.Outline = opportunity.Outline; data.RegionCaption = model.GetRegionCaption(opportunity.RegionIdPrimary); data.SportCaption = model.GetSportCaption(opportunity.SportIdPrimary); - data.CreatedOnFmt = DateUtil.Format(opportunity.CreateStamp.On, "d-MMM-yyyy"); - data.PublishedOnFmt = DateUtil.Format(opportunity.PublishedAt, "d-MMM-yyyy"); - data.ExpiresOnFmt = DateUtil.Format(opportunity.ExpiresAt, "d-MMM-yyyy"); + data.CreatedOnFmt = Localiser.FormatDate(opportunity.CreateStamp.On); + data.PublishedOnFmt = Localiser.FormatDate(opportunity.PublishedAt); + data.ExpiresOnFmt = Localiser.FormatDate(opportunity.ExpiresAt); data.StatusCode = opportunity.StatusCode; data.StatusDesc = opportunity.StatusDesc; } @@ -103,88 +99,93 @@ namespace Saxonite.Net.Core.Web.Online.Controllers // Organisation. SysId organisationId = model.Filter.OrganisationId.ToSysId(); - SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationId); - if (organisation == null || !organisation.IsStatusActive) + using (SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationId)) { - SaLogger.Warn(fn + " " + (organisation == null ? "Invalid" : "Inactive") + " organisation for Opportunity list. OrganisationId: '" + model.Filter.OrganisationId + "'"); - return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"])); - } - - if (organisation != null) - { - // Store filter data. - SessionData_Online.Filter.SetMemberOrganisation(organisation); - - // Reference data - opportunity types. - foreach (TypeCodeDesc opportunityType in OpportunityTypeCodes) - responseData.OpportunityTypes.Add(opportunityType.Code, Localiser.GetOpportunityTypeCaption((Sax.OpportunityType)opportunityType.Type)); - - // User organisation. - SaxUserOrganisation.Params prmUserOrganisation = new SaxUserOrganisation.Params(); - prmUserOrganisation.UserAccountIdsIncluded.Add(SessionData.User.UserAccountId); - prmUserOrganisation.OrganisationIdsIncluded.Add(organisationId); - prmUserOrganisation.StatusCat = SaxUserOrganisation.StatusCat.ActiveOnly; - SaxUserOrganisation userOrganisation = AccountMgr.GetUserOrganisation(prmUserOrganisation); - - // User details. - responseData.User.CanManage = userOrganisation?.OrganisationRoles.Contains(Sax.OrganisationRole.OpportunityAdmin) ?? false; - - // Item filter. - SaxOrganisationOpportunity.Params prmOpportunity = new SaxOrganisationOpportunity.Params(); - prmOpportunity.UserAccountIdsIncluded.Add(SessionData.User.UserAccountId); - prmOpportunity.OrganisationIdsIncluded.Add(organisationId); - prmOpportunity.OpportunityFilter = model.Filter.Opportunity; - prmOpportunity.StatusCat = SaxOrganisationOpportunity.StatusCat.NotDeleted; - model.Paging.ItemTotal = (ulong)ActivityMgr.GetOrganisationOpportunityCount(prmOpportunity); - model.PopulatePagingTo(prmOpportunity.ResultsPaging); - - // Item columns. - ColumnDfn colId = ActivityMgr.GetOrganisationOpportunityColumnDfn(SaxOrganisationOpportunity.Column.Id); - ColumnDfn colOpportunityTypeCode = ActivityMgr.GetOrganisationOpportunityColumnDfn(SaxOrganisationOpportunity.Column.OpportunityTypeCode); - ColumnDfn colPosition = ActivityMgr.GetOrganisationOpportunityColumnDfn(SaxOrganisationOpportunity.Column.Position); - ColumnDfn colOutline = ActivityMgr.GetOrganisationOpportunityColumnDfn(SaxOrganisationOpportunity.Column.Outline); - ColumnDfn colRegionId = ActivityMgr.GetOrganisationOpportunityColumnDfn(SaxOrganisationOpportunity.Column.RegionIdPrimary); - ColumnDfn colRegionName = ActivityMgr.GetOrganisationOpportunityColumnDfn(SaxOrganisationOpportunity.Column.Region_Name); - ColumnDfn colSportId = ActivityMgr.GetOrganisationOpportunityColumnDfn(SaxOrganisationOpportunity.Column.SportIdPrimary); - ColumnDfn colSportName = ActivityMgr.GetOrganisationOpportunityColumnDfn(SaxOrganisationOpportunity.Column.Sport_Name); - ColumnDfn colPublishedOn = ActivityMgr.GetOrganisationOpportunityColumnDfn(SaxOrganisationOpportunity.Column.PublishedAt_Date); - ColumnDfn colExpiresOn = ActivityMgr.GetOrganisationOpportunityColumnDfn(SaxOrganisationOpportunity.Column.ExpiresAt_Date); - ColumnDfn colStatus = ActivityMgr.GetOrganisationOpportunityColumnDfn(SaxOrganisationOpportunity.Column.Status); - ColumnDfn colCreatedOn = ActivityMgr.GetOrganisationOpportunityColumnDfn(SaxOrganisationOpportunity.Column.CreateOn_Date); - List columns = new List() + if (organisation == null || !organisation.IsStatusActive) { - colId, colOpportunityTypeCode, colPosition, colOutline, - colRegionId, colRegionName, colSportId, colSportName, - colPublishedOn, colExpiresOn, - colStatus, colCreatedOn - }; + SaLogger.Warn(fn + " " + (organisation == null ? "Invalid" : "Inactive") + " organisation for Opportunity list. OrganisationId: '" + model.Filter.OrganisationId + "'"); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"])); + } - // Sort columns. - colCreatedOn.SortAscending = false; - prmOpportunity.SortColumns.Add(colCreatedOn); - - // Query items. - using (DbResultSet resultSet = ActivityMgr.ExecuteOrganisationOpportunityQuery(columns, prmOpportunity)) + if (organisation != null) { - while (resultSet != null && resultSet.HasRows && resultSet.NextRecord()) + // Store filter data. + SessionData_Online.Filter.SetMemberOrganisation(organisation); + + // Reference data - opportunity types. + foreach (TypeCodeDesc opportunityType in OpportunityTypeCodes) + responseData.OpportunityTypes.Add(opportunityType.Code, Localiser.GetOpportunityTypeCaption((Sax.OpportunityType)opportunityType.Type)); + + // User organisation. + SaxUserOrganisation.Params prmUserOrganisation = new SaxUserOrganisation.Params(); + prmUserOrganisation.UserAccountIdsIncluded.Add(SessionData.User.UserAccountId); + prmUserOrganisation.OrganisationIdsIncluded.Add(organisationId); + prmUserOrganisation.UserAccountStatusCat = SaxUserAccount.StatusCat.ActiveOnly; + prmUserOrganisation.StatusCat = SaxUserOrganisation.StatusCat.ActiveOnly; + using (SaxUserOrganisation userOrganisation = AccountMgr.GetUserOrganisation(prmUserOrganisation)) { - NamedValue.Map values = resultSet.GetRecordValues(); - OpportunityData_List opportunityData = new OpportunityData_List() + + // User details. + responseData.User.CanManage = userOrganisation?.HasOrganisationRole(Sax.OrganisationRole.OpportunityAdmin) ?? false; + + // Item filter. + SaxOrganisationOpportunity.Params prmOpportunity = new SaxOrganisationOpportunity.Params(); + prmOpportunity.UserAccountIdsIncluded.Add(SessionData.User.UserAccountId); + prmOpportunity.OrganisationIdsIncluded.Add(organisationId); + prmOpportunity.OpportunityFilter = model.Filter.Opportunity; + prmOpportunity.StatusCat = SaxOrganisationOpportunity.StatusCat.NotDeleted; + model.Paging.ItemTotal = (ulong)ActivityMgr.GetOrganisationOpportunityCount(prmOpportunity); + model.PopulatePagingTo(prmOpportunity.ResultsPaging); + + // Item columns. + ColumnDfn colId = ActivityMgr.GetOrganisationOpportunityColumnDfn(SaxOrganisationOpportunity.Column.Id); + ColumnDfn colOpportunityTypeCode = ActivityMgr.GetOrganisationOpportunityColumnDfn(SaxOrganisationOpportunity.Column.OpportunityTypeCode); + ColumnDfn colPosition = ActivityMgr.GetOrganisationOpportunityColumnDfn(SaxOrganisationOpportunity.Column.Position); + ColumnDfn colOutline = ActivityMgr.GetOrganisationOpportunityColumnDfn(SaxOrganisationOpportunity.Column.Outline); + ColumnDfn colRegionId = ActivityMgr.GetOrganisationOpportunityColumnDfn(SaxOrganisationOpportunity.Column.RegionIdPrimary); + ColumnDfn colRegionName = ActivityMgr.GetOrganisationOpportunityColumnDfn(SaxOrganisationOpportunity.Column.Region_Name); + ColumnDfn colSportId = ActivityMgr.GetOrganisationOpportunityColumnDfn(SaxOrganisationOpportunity.Column.SportIdPrimary); + ColumnDfn colSportName = ActivityMgr.GetOrganisationOpportunityColumnDfn(SaxOrganisationOpportunity.Column.Sport_Name); + ColumnDfn colPublishedOn = ActivityMgr.GetOrganisationOpportunityColumnDfn(SaxOrganisationOpportunity.Column.PublishedAt_Date); + ColumnDfn colExpiresOn = ActivityMgr.GetOrganisationOpportunityColumnDfn(SaxOrganisationOpportunity.Column.ExpiresAt_Date); + ColumnDfn colStatus = ActivityMgr.GetOrganisationOpportunityColumnDfn(SaxOrganisationOpportunity.Column.Status); + ColumnDfn colCreatedOn = ActivityMgr.GetOrganisationOpportunityColumnDfn(SaxOrganisationOpportunity.Column.CreateOn_Date); + List columns = new List() { - Id = values.GetString(colId.DataName), - TypeCode = values.GetString(colOpportunityTypeCode.DataName), - TypeDesc = Localiser.GetOpportunityTypeCaption((Sax.OpportunityType)OpportunityTypeCodes.GetTypeForCode(values.GetString(colOpportunityTypeCode.DataName))), - Position = values.GetString(colPosition.DataName), - Outline = values.GetString(colOutline.DataName), - RegionCaption = model.GetRegionCaption(values.GetString(colRegionId.DataName).ToSysId()), - SportCaption = model.GetSportCaption(values.GetString(colSportId.DataName).ToSysId()), - CreatedOnFmt = DateUtil.Format(values.GetDateTime(colCreatedOn.DataName), "d-MMM-yyyy"), - PublishedOnFmt = DateUtil.Format(values.GetDateTime(colPublishedOn.DataName), "d-MMM-yyyy"), - ExpiresOnFmt = DateUtil.Format(values.GetDateTime(colExpiresOn.DataName), "d-MMM-yyyy"), - StatusCode = values.GetString(colStatus.DataName), - StatusDesc = SacsSystem.GetStatusDescForCode(values.GetString(colStatus.DataName)) + colId, colOpportunityTypeCode, colPosition, colOutline, + colRegionId, colRegionName, colSportId, colSportName, + colPublishedOn, colExpiresOn, + colStatus, colCreatedOn }; - responseData.Opportunities.Add(opportunityData); + + // Sort columns. + colCreatedOn.SortAscending = false; + prmOpportunity.SortColumns.Add(colCreatedOn); + + // Query items. + using (DbResultSet resultSet = ActivityMgr.ExecuteOrganisationOpportunityQuery(columns, prmOpportunity)) + { + while (resultSet != null && resultSet.HasRows && resultSet.NextRecord()) + { + NamedValue.Map values = resultSet.GetRecordValues(); + OpportunityData_List opportunityData = new OpportunityData_List() + { + Id = values.GetString(colId.DataName), + TypeCode = values.GetString(colOpportunityTypeCode.DataName), + TypeDesc = Localiser.GetOpportunityTypeCaption((Sax.OpportunityType)OpportunityTypeCodes.GetTypeForCode(values.GetString(colOpportunityTypeCode.DataName))), + Position = values.GetString(colPosition.DataName), + Outline = values.GetString(colOutline.DataName), + RegionCaption = model.GetRegionCaption(values.GetString(colRegionId.DataName).ToSysId()), + SportCaption = model.GetSportCaption(values.GetString(colSportId.DataName).ToSysId()), + CreatedOnFmt = Localiser.FormatDate(values.GetDateTime(colCreatedOn.DataName)), + PublishedOnFmt = Localiser.FormatDate(values.GetDateTime(colPublishedOn.DataName)), + ExpiresOnFmt = Localiser.FormatDate(values.GetDateTime(colExpiresOn.DataName)), + StatusCode = values.GetString(colStatus.DataName), + StatusDesc = SacsSystem.GetStatusDescForCode(values.GetString(colStatus.DataName)) + }; + responseData.Opportunities.Add(opportunityData); + } + } } } } @@ -228,26 +229,32 @@ namespace Saxonite.Net.Core.Web.Online.Controllers return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.specified"])); // Retrieve item. - SaxOrganisationOpportunity opportunity = ActivityMgr.GetOrganisationOpportunityById(opportunityId); - if (opportunity == null) + using (SaxOrganisationOpportunity opportunity = ActivityMgr.GetOrganisationOpportunityById(opportunityId)) { - SaLogger.Warn(fn + " Failed to retrieve Organisation Opportunity for Id: '" + model.OpportunityId + "'"); - return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"])); + if (opportunity == null) + { + SaLogger.Warn(fn + " Failed to retrieve Organisation Opportunity for Id: '" + model.OpportunityId + "'"); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"])); + } + + // Validate organisation. + using (SaxOrganisation organisation = AccountMgr.GetOrganisationById(opportunity.OrganisationId)) + { + if (organisation == null || !organisation.IsStatusActive) + { + SaLogger.Warn(fn + " " + (organisation == null ? "Invalid" : "Inactive") + " organisation for Opportunity. OrganisationId: '" + opportunity.OrganisationId + "'"); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"])); + } + + // Make a note of the selected Organisation. + SessionData_Online.Filter.SetMemberOrganisation(organisation); + + // Construct response. + PopulateOpportunityResponseData(model, opportunity, responseData.Opportunity); + } } - // Validate organisation. - SaxOrganisation organisation = AccountMgr.GetOrganisationById(opportunity.OrganisationId); - if (organisation == null || !organisation.IsStatusActive) - { - SaLogger.Warn(fn + " " + (organisation == null ? "Invalid" : "Inactive") + " organisation for Opportunity. OrganisationId: '" + opportunity.OrganisationId + "'"); - return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"])); - } - - // Make a note of the selected Organisation. - SessionData_Online.Filter.SetMemberOrganisation(organisation); - - // Construct response. - PopulateOpportunityResponseData(model, opportunity, responseData.Opportunity); + // Complete response. responseData.Success = true; return Json(responseData); } @@ -288,81 +295,93 @@ namespace Saxonite.Net.Core.Web.Online.Controllers return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); } - // Retrieve existing item. - SaxOrganisationOpportunity opportunity = ActivityMgr.GetOrganisationOpportunityById(opportunityId); - if (opportunity == null && SysId.IsValid(opportunityId)) + // Record disposable items as they are created so all can be disposed when needed. + using (DisposableCollection disposableItems = new DisposableCollection()) { - SaLogger.Warn(fn + " Failed to retrieve opportunity for Id: '" + model.OpportunityId + "'"); - return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"])); - } - // Validate the Organisation. - SysId organisationId = model.OpportunityOrganisationId.ToSysId(); - if (!SysId.IsValid(organisationId)) - { - SaLogger.Warn(fn + " Organisation Id not specified for Opportunity."); - return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); - } - SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationId); - if (organisation == null || !organisation.IsStatusActive) - { - SaLogger.Warn(fn + " " + (organisation == null ? "Invalid" : "Inactive") + " organisation for Opportunity. OrganisationId: '" + model.OpportunityOrganisationId + "'"); - return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"])); - } - if (opportunity != null && !opportunity.OrganisationId.Equals(organisationId)) - { - SaLogger.Warn(fn + " Inconsistent Organisation Id specified for Opportunity. Expected: " + opportunity.OrganisationId + ". Found: " + organisationId); - return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); - } - - // Ensure that the user has the correct role to update Opportunities for this organisation. - if (!AccountMgr.UserHasOrganisationRole(SessionData.User.UserAccountId, organisationId, Sax.OrganisationRole.OpportunityAdmin)) - { - SaLogger.Warn(fn + " User doesn not have 'Opportunity Admin' role for Organisation Id: " + organisationId); - return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); - } - - // Create new item if required. - if (opportunity == null) - opportunity = ActivityMgr.CreateOrganisationOpportunity(organisationId); - - // Populate item properties. - if (model.OpportunityRegionId != null) - opportunity.RegionIdPrimary = model.OpportunityRegionId.ToSysId(); - if (model.OpportunitySportId != null) - opportunity.SportIdPrimary = model.OpportunitySportId.ToSysId(); - if (model.OpportunityTypeCode != null) - opportunity.OpportunityType = (Sax.OpportunityType)OpportunityTypeCodes.GetTypeForCode(model.OpportunityTypeCode); - if (model.OpportunityPosition != null) - opportunity.Position = model.OpportunityPosition; - if (model.OpportunityOutline != null) - opportunity.Outline = model.OpportunityOutline; - if (model.OpportunityStatusCode != null) - opportunity.Status = model.OpportunityStatus; - - // Store item. - if (!SysId.IsValid(opportunity.Id)) - { - if (!ActivityMgr.InsertOrganisationOpportunity(opportunity, DbUpdateInfo)) + // Retrieve existing item. + SaxOrganisationOpportunity opportunity = ActivityMgr.GetOrganisationOpportunityById(opportunityId); + disposableItems.Add(opportunity); + if (opportunity == null && SysId.IsValid(opportunityId)) { - SaLogger.Warn(fn + " Failed to insert opportunity: " + ActivityMgr.ErrorStateMessage); - return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.insert.failed"] + "\n\n" + ActivityMgr.ErrorStateMessage)); + SaLogger.Warn(fn + " Failed to retrieve opportunity for Id: '" + model.OpportunityId + "'"); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"])); } - } - else - { - if (!ActivityMgr.UpdateOrganisationOpportunity(opportunity, DbUpdateInfo)) + + // Validate the Organisation. + SysId organisationId = model.OpportunityOrganisationId.ToSysId(); + if (!SysId.IsValid(organisationId)) { - SaLogger.Warn(fn + " Failed to update opportunity [Id:" + SysId.ToString(opportunity.Id) + "]: " + ActivityMgr.ErrorStateMessage); - return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + ActivityMgr.ErrorStateMessage)); + SaLogger.Warn(fn + " Organisation Id not specified for Opportunity."); + return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); } + SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationId); + disposableItems.Add(organisation); + if (organisation == null || !organisation.IsStatusActive) + { + SaLogger.Warn(fn + " " + (organisation == null ? "Invalid" : "Inactive") + " organisation for Opportunity. OrganisationId: '" + model.OpportunityOrganisationId + "'"); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"])); + } + if (opportunity != null && !opportunity.OrganisationId.Equals(organisationId)) + { + SaLogger.Warn(fn + " Inconsistent Organisation Id specified for Opportunity. Expected: " + opportunity.OrganisationId + ". Found: " + organisationId); + return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); + } + + // Ensure that the user has the correct role to update Opportunities for this organisation. + if (!AccountMgr.UserHasOrganisationRole(SessionData.User.UserAccountId, organisationId, Sax.OrganisationRole.OpportunityAdmin)) + { + SaLogger.Warn(fn + " User doesn't have 'Opportunity Admin' role for Organisation Id: " + organisationId); + return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); + } + + // Create new item if required. + if (opportunity == null) + { + opportunity = ActivityMgr.CreateOrganisationOpportunity(organisationId); + disposableItems.Add(opportunity); + } + + // Populate item properties. + if (model.OpportunityRegionId != null) + opportunity.RegionIdPrimary = model.OpportunityRegionId.ToSysId(); + if (model.OpportunitySportId != null) + opportunity.SportIdPrimary = model.OpportunitySportId.ToSysId(); + if (model.OpportunityTypeCode != null) + opportunity.OpportunityType = (Sax.OpportunityType)OpportunityTypeCodes.GetTypeForCode(model.OpportunityTypeCode); + if (model.OpportunityPosition != null) + opportunity.Position = model.OpportunityPosition; + if (model.OpportunityOutline != null) + opportunity.Outline = model.OpportunityOutline; + if (model.OpportunityStatusCode != null) + opportunity.Status = model.OpportunityStatus; + + // Store item. + if (!SysId.IsValid(opportunity.Id)) + { + if (!ActivityMgr.InsertOrganisationOpportunity(opportunity, DbUpdateInfo)) + { + SaLogger.Warn(fn + " Failed to insert opportunity: " + ActivityMgr.ErrorStateMessage); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.insert.failed"])); + } + } + else + { + if (!ActivityMgr.UpdateOrganisationOpportunity(opportunity, DbUpdateInfo)) + { + SaLogger.Warn(fn + " Failed to update opportunity [Id:" + SysId.ToString(opportunity.Id) + "]: " + ActivityMgr.ErrorStateMessage); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"])); + } + } + + // Store Opportunity Activity Index data. + ActivityConsole.StoreActivityIndex(opportunity, DbUpdateInfo); + + // Construct response. + PopulateOpportunityResponseData(model, opportunity, responseData.Opportunity); } - // Store Opportunity Activity Index data. - ActivityConsole.StoreActivityIndex(opportunity, DbUpdateInfo); - - // Construct response. - PopulateOpportunityResponseData(model, opportunity, responseData.Opportunity); + // Complete response. responseData.Success = true; return Json(responseData); } diff --git a/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController-Ajax-Request.cs b/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController-Ajax-Request.cs new file mode 100644 index 0000000..8f265a4 --- /dev/null +++ b/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController-Ajax-Request.cs @@ -0,0 +1,190 @@ +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.Utils; +using Saxonite.Net.Core.Web.Online.App; +using Saxonite.Net.Core.Web.Online.Models; +using Saxonite.Net.Standard.Base.App; +using Saxonite.Net.Standard.Base.Bus; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Saxonite.Net.Core.Web.Online.Controllers +{ + //======================================================================= + // Online Organisation Controller - Request - Ajax actions. + //======================================================================= + partial class OrganisationController + { + + //------------------------------------------------------------------- + // Request Organisation ajax request. + //------------------------------------------------------------------- + private class AjaxResponse_RequestOrganisation : AjaxResponse_Data + { + // Construction. + public AjaxResponse_RequestOrganisation(SaxModel_Online model) : base(model) { } + // Organisation list. + public List Organisations { get; } = new List(); + } + //------------------------------------------------------------------- + [HttpPost] + [Route("Organisation/Ajax/Request")] + [Route("Organisation/Ajax/Register")] + public IActionResult Ajax_RequestOrganisation(Model_Organisation model) + { + String fn = GetType().Name + ".Ajax_RequestOrganisation"; + String messageTitle = Localiser["organisation.caption.organisation.request"]; + AjaxResponse_RequestOrganisation responseData = new AjaxResponse_RequestOrganisation(model); + + // Validate session and referrer. User does not need to be logged in. + if (!ValidReferer) + return Forbid(); + SaxSessionData_Online sessionData = SessionData_Online; + + if (!model.ValidateOrganisationRequestData(responseData)) + return Json(responseData); + + // Check Regions. + SaxRegion regionCountry = Internals.GetRegion(model.OrganisationRegionIdCountry.ToSysId()); + if (SysId.IsValid(model.OrganisationRegionIdCountry.ToSysId()) && regionCountry == null) + { + SaLogger.Warn(fn + " Country region not found. Id: " + model.OrganisationRegionIdCountry + ". " + getLogSuffix()); + return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); + } + SaxRegion regionProvince = Internals.GetRegion(model.OrganisationAddressRegionIdProvince.ToSysId()); + if (SysId.IsValid(model.OrganisationAddressRegionIdProvince.ToSysId()) && regionProvince == null) + { + SaLogger.Warn(fn + " Province region not found. Id: " + model.OrganisationAddressRegionIdProvince + ". " + getLogSuffix()); + return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); + } + + // User does not need to be logged in, but user details must be supplied if not. + SaxUserAccount userAccount = null; + if (ValidUserSession) + { + // Check that an email address hasn't been supplied. + // NB Shouldn't happen as contact controls aren't displayed if user is logged in, + // hence 'System' error response and nothing more specific. + if (!StringUtil.IsEmpty(model.ContactEmailAddress)) + { + SaLogger.Warn(fn + " Contact Email supplied when user logged in. " + getLogSuffix()); + return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); + } + // Retrieve user account. + userAccount = AccountMgr.GetUserAccountById(sessionData.User.UserAccountId); + if (userAccount == null) + return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); + } + else + { + // Double check that contact details have been supplied - validated by model above. + if (StringUtil.IsEmpty(model.ContactEmailAddress) || StringUtil.IsEmpty(model.ContactFirstName) || StringUtil.IsEmpty(model.ContactLastName)) + { + SaLogger.Warn(fn + " Incomplete contact details supplied."); + return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); + } + // Check that the email doesn't exist. + // NB This is validated by the model and shouldn't be required here. + userAccount = AccountMgr.GetUserAccountByEmailAddress(model.ContactEmailAddress); + if (userAccount != null) + return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); + userAccount = AccountMgr.CreateUserAccount(); + userAccount.EmailAddress = model.ContactEmailAddress; + userAccount.FirstName = model.ContactFirstName; + userAccount.LastName = model.ContactLastName; + if (!AccountMgr.InsertUserAccount(userAccount, DbUpdateInfo)) + { + SaLogger.Warn(fn + " Failed to insert user contact - " + AccountMgr.ErrorStateMessage); + return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); + } + } + + if (SysId.IsValid(model.OrganisationSportId.ToSysId())) + { + // Add User Sport if not already present. + // NB This will also reactivate an inactive or deleted User Sport. + SaxUserSport.Params prmUserSport = new SaxUserSport.Params(); + prmUserSport.UserAccountIdsIncluded.Add(userAccount.Id); + prmUserSport.SportIdsIncluded.Add(model.OrganisationSportId.ToSysId()); + prmUserSport.StatusCat = SaxUserSport.StatusCat.Any; + SaxUserSport userSport = AccountMgr.GetUserSport(prmUserSport); + if (userSport == null) + userSport = AccountMgr.CreateUserSport(userAccount.Id, model.OrganisationSportId.ToSysId()); + userSport.Status = SaxUserSport.DefaultStatus; + if (!SysId.IsValid(userSport.Id)) + { + if (!AccountMgr.InsertUserSport(userSport, DbUpdateInfo)) + { + // Logg error but don't abort the request. + SaLogger.Warn(fn + " Failed to insert User Sport: " + AccountMgr.ErrorStateMessage); + } + } + else + { + if (!AccountMgr.UpdateUserSport(userSport, DbUpdateInfo)) + { + // Logg error but don't abort the request. + SaLogger.Warn(fn + " Failed to update User Sport: " + AccountMgr.ErrorStateMessage); + } + } + } + + // Create Organisation. + SaxOrganisation organisation = AccountMgr.CreateOrganisation(); + organisation.Name = model.OrganisationName; + organisation.OrganisationType = model.GetOrganisationType(); + organisation.OrganisationCategory = model.GetOrganisationCategory(); + organisation.SportIdPrimary = model.OrganisationSportId.ToSysId(); + organisation.RegionIdPrimary = model.OrganisationRegionIdCountry.ToSysId(); + organisation.PhoneMain = model.OrganisationPhoneMain; + organisation.Status = SaStatus.Awaiting; + if (!AccountMgr.InsertOrganisation(organisation, DbUpdateInfo)) + { + SaLogger.Warn(fn + " Failed to insert organisation - " + AccountMgr.ErrorStateMessage); + return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); + } + + // Add user as member of organisation. + SaxUserOrganisation userOrganisation = AccountMgr.CreateUserOrganisation(userAccount.Id, organisation.Id); + userOrganisation.OrganisationRoles = new List() { Sax.OrganisationRole.Member, Sax.OrganisationRole.MemberAdmin }; + if (!AccountMgr.InsertUserOrganisation(userOrganisation, DbUpdateInfo)) + { + SaLogger.Warn(fn + " Failed to insert user organisation - " + AccountMgr.ErrorStateMessage); + return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); + } + + // Create Organisation address. + SaxItemAddress organisationAddress = ItemDataMgr.CreateItemAddress(organisation.GetItemId(), Sax.AddressType.Office); + organisationAddress.Line1 = model.OrganisationAddressLine1; + organisationAddress.Line2 = model.OrganisationAddressLine2; + organisationAddress.Line3 = model.OrganisationAddressLine3; + organisationAddress.Town = model.OrganisationAddressTown; + organisationAddress.Province = model.OrganisationAddressProvince; + organisationAddress.RegionIdProvince = model.OrganisationAddressRegionIdProvince.ToSysId(); + if (SysId.IsValid(regionCountry.Id)) + { + organisationAddress.Country = regionCountry.Name; + organisationAddress.RegionIdCountry = regionCountry.Id; + } + else organisationAddress.Country = model.OrganisationAddressCountry; + organisationAddress.PostalCode = model.OrganisationAddressPostalCode; + if (!ItemDataMgr.InsertItemAddress(organisationAddress, DbUpdateInfo)) + { + SaLogger.Warn(fn + " Failed to insert organisation address - " + ItemDataMgr.ErrorStateMessage); + return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); + } + + // Complete response. + responseData.SetMessage(messageTitle, Localiser["msg.organisation.request.success"].ToString().ReplaceAll("\n", "
")); + responseData.Success = true; + return Json(responseData); + } + + //------------------------------------------------------------------- + } + //======================================================================= +} diff --git a/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController-Ajax.cs b/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController-Ajax.cs index 9ef694d..14d5221 100644 --- a/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController-Ajax.cs +++ b/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController-Ajax.cs @@ -106,169 +106,6 @@ namespace Saxonite.Net.Core.Web.Online.Controllers return Json(responseData); } - //------------------------------------------------------------------- - // Request Organisation ajax request. - //------------------------------------------------------------------- - private class AjaxResponse_RequestOrganisation : AjaxResponse_Data - { - // Construction. - public AjaxResponse_RequestOrganisation(SaxModel_Online model) : base(model) { } - // Organisation list. - public List Organisations { get; } = new List(); - } - //------------------------------------------------------------------- - [HttpPost] - [Route("Organisation/Ajax/RequestOrganisation")] - public IActionResult Ajax_RequestOrganisation(Model_Organisation model) - { - String fn = GetType().Name + ".Ajax_RequestOrganisation"; - String messageTitle = Localiser["organisation.caption.organisation.request"]; - AjaxResponse_RequestOrganisation responseData = new AjaxResponse_RequestOrganisation(model); - - // Validate session and referrer. User does not need to be logged in. - if (!ValidReferer) - return Forbid(); - SaxSessionData_Online sessionData = SessionData_Online; - - if (!model.ValidateOrganisationRequestData(responseData)) - return Json(responseData); - - // Check Regions. - SaxRegion regionCountry = Internals.GetRegion(model.OrganisationRegionIdCountry.ToSysId()); - if (SysId.IsValid(model.OrganisationRegionIdCountry.ToSysId()) && regionCountry == null) - { - SaLogger.Warn(fn + " Country region not found. Id: " + model.OrganisationRegionIdCountry + ". " + getLogSuffix()); - return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); - } - SaxRegion regionProvince = Internals.GetRegion(model.OrganisationAddressRegionIdProvince.ToSysId()); - if (SysId.IsValid(model.OrganisationAddressRegionIdProvince.ToSysId()) && regionProvince == null) - { - SaLogger.Warn(fn + " Province region not found. Id: " + model.OrganisationAddressRegionIdProvince + ". " + getLogSuffix()); - return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); - } - - // User does not need to be logged in, but user details must be supplied if not. - SaxUserAccount userAccount = null; - if (ValidUserSession) - { - // Check that an email address hasn't been supplied. - // NB Shouldn't happen as contact controls aren't displayed if user is logged in, - // hence 'System' error response and nothing more specific. - if (!StringUtil.IsEmpty(model.ContactEmailAddress)) - { - SaLogger.Warn(fn + " Contact Email supplied when user logged in. " + getLogSuffix()); - return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); - } - // Retrieve user account. - userAccount = AccountMgr.GetUserAccountById(sessionData.User.UserAccountId); - if (userAccount == null) - return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); - } - else - { - // Double check that contact details have been supplied - validated by model above. - if (StringUtil.IsEmpty(model.ContactEmailAddress) || StringUtil.IsEmpty(model.ContactFirstName) || StringUtil.IsEmpty(model.ContactLastName)) - { - SaLogger.Warn(fn + " Incomplete contact details supplied."); - return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); - } - // Check that the email doesn't exist. - // NB This is validated by the model and shouldn't be required here. - userAccount = AccountMgr.GetUserAccountByEmailAddress(model.ContactEmailAddress); - if (userAccount != null) - return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); - userAccount = AccountMgr.CreateUserAccount(); - userAccount.EmailAddress = model.ContactEmailAddress; - userAccount.FirstName = model.ContactFirstName; - userAccount.LastName = model.ContactLastName; - if (!AccountMgr.InsertUserAccount(userAccount, DbUpdateInfo)) - { - SaLogger.Warn(fn + " Failed to insert user contact - " + AccountMgr.ErrorStateMessage); - return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); - } - } - - if (SysId.IsValid(model.OrganisationSportId.ToSysId())) - { - // Add User Sport if not already present. - // NB This will also reactivate an inactive or deleted User Sport. - SaxUserSport.Params prmUserSport = new SaxUserSport.Params(); - prmUserSport.UserAccountIdsIncluded.Add(userAccount.Id); - prmUserSport.SportIdsIncluded.Add(model.OrganisationSportId.ToSysId()); - prmUserSport.StatusCat = SaxUserSport.StatusCat.Any; - SaxUserSport userSport = AccountMgr.GetUserSport(prmUserSport); - if (userSport == null) - userSport = AccountMgr.CreateUserSport(userAccount.Id, model.OrganisationSportId.ToSysId()); - userSport.Status = SaxUserSport.DefaultStatus; - if (!SysId.IsValid(userSport.Id)) - { - if (!AccountMgr.InsertUserSport(userSport, DbUpdateInfo)) - { - // Logg error but don't abort the request. - SaLogger.Warn(fn + " Failed to insert User Sport: " + AccountMgr.ErrorStateMessage); - } - } - else - { - if (!AccountMgr.UpdateUserSport(userSport, DbUpdateInfo)) - { - // Logg error but don't abort the request. - SaLogger.Warn(fn + " Failed to update User Sport: " + AccountMgr.ErrorStateMessage); - } - } - } - - // Create Organisation. - SaxOrganisation organisation = AccountMgr.CreateOrganisation(); - organisation.Name = model.OrganisationName; - organisation.OrganisationType = model.GetOrganisationType(); - organisation.OrganisationCategory = model.GetOrganisationCategory(); - organisation.SportIdPrimary = model.OrganisationSportId.ToSysId(); - organisation.RegionIdPrimary = model.OrganisationRegionIdCountry.ToSysId(); - organisation.PhoneMain = model.OrganisationPhoneMain; - organisation.Status = SaStatus.Awaiting; - if (!AccountMgr.InsertOrganisation(organisation, DbUpdateInfo)) - { - SaLogger.Warn(fn + " Failed to insert organisation - " + AccountMgr.ErrorStateMessage); - return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); - } - - // Add user as member of organisation. - SaxUserOrganisation userOrganisation = AccountMgr.CreateUserOrganisation(userAccount.Id, organisation.Id); - userOrganisation.OrganisationRoles.Add(Sax.OrganisationRole.Member); - if (!AccountMgr.InsertUserOrganisation(userOrganisation, DbUpdateInfo)) - { - SaLogger.Warn(fn + " Failed to insert user organisation - " + AccountMgr.ErrorStateMessage); - return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); - } - - // Create Organisation address. - SaxItemAddress organisationAddress = ItemDataMgr.CreateItemAddress(organisation.GetItemId(), Sax.AddressType.Office); - organisationAddress.Line1 = model.OrganisationAddressLine1; - organisationAddress.Line2 = model.OrganisationAddressLine2; - organisationAddress.Line3 = model.OrganisationAddressLine3; - organisationAddress.Town = model.OrganisationAddressTown; - organisationAddress.Province = model.OrganisationAddressProvince; - organisationAddress.RegionIdProvince = model.OrganisationAddressRegionIdProvince.ToSysId(); - if (SysId.IsValid(regionCountry.Id)) - { - organisationAddress.Country = regionCountry.Name; - organisationAddress.RegionIdCountry = regionCountry.Id; - } - else organisationAddress.Country = model.OrganisationAddressCountry; - organisationAddress.PostalCode = model.OrganisationAddressPostalCode; - if (!ItemDataMgr.InsertItemAddress(organisationAddress, DbUpdateInfo)) - { - SaLogger.Warn(fn + " Failed to insert organisation address - " + ItemDataMgr.ErrorStateMessage); - return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); - } - - // Complete response. - responseData.SetMessage(messageTitle, Localiser["msg.organisation.request.success"].ToString().ReplaceAll("\n", "
")); - responseData.Success = true; - return Json(responseData); - } - //------------------------------------------------------------------- // Get Member Organisation List ajax request. //------------------------------------------------------------------- diff --git a/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController.cs b/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController.cs index 86397e5..3a9c4ff 100644 --- a/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController.cs +++ b/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController.cs @@ -46,26 +46,80 @@ namespace Saxonite.Net.Core.Web.Online.Controllers if (!ValidUserSession) return RedirectToAction("", ""); + // Model. + Model_Organisation_Opportunities model = new Model_Organisation_Opportunities(Localiser); + + // Get a suitable organisation for the filter. + SysId filterOrganisationId = SessionData_Online.Filter.OrganisationIdMember; + String filterOrganisationCaption = SessionData_Online.Filter.OrganisationCaptionMember; + OrganisationData organisationData = GetOrganisationForFilter(filterOrganisationId, filterOrganisationCaption); + if (organisationData == null) + return RedirectToAction("", ""); + + // Make a note of the filter values. + model.Filter.OrganisationId = SysId.ToString(organisationData.Id); + model.Filter.OrganisationCaption = organisationData.Caption; + SessionData_Online.Filter.OrganisationIdMember = organisationData.Id; + SessionData_Online.Filter.OrganisationCaptionMember = organisationData.Caption; + + return View(model); + } + + //------------------------------------------------------------------- + [HttpGet] + public IActionResult Members() + { + // Validate session. + if (!ValidUserSession) + return RedirectToAction("", ""); + + // Model. + Model_Organisation_Members model = new Model_Organisation_Members(Localiser); + + // Get a suitable organisation for the filter. + SysId filterOrganisationId = SessionData_Online.Filter.OrganisationIdMember; + String filterOrganisationCaption = SessionData_Online.Filter.OrganisationCaptionMember; + OrganisationData organisationData = GetOrganisationForFilter(filterOrganisationId, filterOrganisationCaption); + if (organisationData == null) + return RedirectToAction("", ""); + + // Make a note of the filter values. + model.Filter.OrganisationId = SysId.ToString(organisationData.Id); + model.Filter.OrganisationCaption = organisationData.Caption; + SessionData_Online.Filter.OrganisationIdMember = organisationData.Id; + SessionData_Online.Filter.OrganisationCaptionMember = organisationData.Caption; + + return View(model); + } + + //------------------------------------------------------------------- + public class OrganisationData + { + public OrganisationData(SysId id) { Id = id; } + public SysId Id { get; } + public String Caption { get; set; } + } + private OrganisationData GetOrganisationForFilter(SysId filterOrganisationId, String filterOrganisationCaption) + { // Check that user is associated with organisations. SaxUserOrganisation.Params prmUserOrganisation = new SaxUserOrganisation.Params(); prmUserOrganisation.UserAccountIdsIncluded.Add(SessionData.User.UserAccountId); prmUserOrganisation.StatusCat = SaxUserOrganisation.StatusCat.ActiveOnly; if (AccountMgr.GetUserOrganisationCount(prmUserOrganisation) < 1) - return RedirectToAction("", ""); - - // Model. - Model_Organisation_Opportunities model = new Model_Organisation_Opportunities(Localiser); + return null; // Filter organisation. - model.Filter.OrganisationId = SysId.ToString(SessionData_Online.Filter.OrganisationIdMember); - model.Filter.OrganisationCaption = SessionData_Online.Filter.OrganisationCaptionMember; - if (StringUtil.IsEmpty(model.Filter.OrganisationId)) + OrganisationData organisationData = new OrganisationData(filterOrganisationId); + organisationData.Caption = filterOrganisationCaption; + if (!SysId.IsValid(filterOrganisationId) || StringUtil.IsEmpty(filterOrganisationCaption)) { // Pick the first Organisation that the user is a member of. ColumnDfn colOrganisationId = AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.OrganisationId); ColumnDfn colOrganisationName = AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.Organisation_Name); prmUserOrganisation = new SaxUserOrganisation.Params(); prmUserOrganisation.UserAccountIdsIncluded.Add(SessionData.User.UserAccountId); + if (SysId.IsValid(filterOrganisationId)) + prmUserOrganisation.OrganisationIdsIncluded.Add(filterOrganisationId); prmUserOrganisation.OrganisationMembersOnly = true; prmUserOrganisation.StatusCat = SaxUserOrganisation.StatusCat.ActiveOnly; prmUserOrganisation.SortColumns.Add(colOrganisationName); @@ -76,29 +130,26 @@ namespace Saxonite.Net.Core.Web.Online.Controllers while (resultSet != null && resultSet.HasRows && resultSet.NextRecord()) { NamedValue.Map values = resultSet.GetRecordValues(); - model.Filter.OrganisationId = SysId.ToString(new SysId(values.GetLong(colOrganisationId.DataName))); - if (!StringUtil.IsEmpty(model.Filter.OrganisationId)) + SysId organisationId = new SysId(values.GetLong(colOrganisationId.DataName)); + if (SysId.IsValid(organisationId)) { - model.Filter.OrganisationCaption = values.GetString(colOrganisationName.DataName); + organisationData = new OrganisationData(organisationId); + organisationData.Caption = values.GetString(colOrganisationName.DataName); break; } } } } - if (SysId.IsValid(model.Filter.OrganisationId.ToSysId()) && StringUtil.IsEmpty(model.Filter.OrganisationCaption)) + if (SysId.IsValid(organisationData.Id) && StringUtil.IsEmpty(organisationData.Caption)) { // Organisation has been identified but no caption available as yet. - using (SaxOrganisation organisation = AccountMgr.GetOrganisationById(model.Filter.OrganisationId.ToSysId())) + using (SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationData.Id)) { if (organisation != null) - model.Filter.OrganisationCaption = organisation.Name; + organisationData.Caption = organisation.Name; } } - // Make a note of the filter values. - SessionData_Online.Filter.OrganisationIdMember = model.Filter.OrganisationId.ToSysId(); - SessionData_Online.Filter.OrganisationCaptionMember = model.Filter.OrganisationCaption; - - return View(model); + return organisationData; } //------------------------------------------------------------------- diff --git a/SaxoniteCoreWebOnline/Online/Controllers/SaxController_Online.cs b/SaxoniteCoreWebOnline/Online/Controllers/SaxController_Online.cs index ac759a3..58c6099 100644 --- a/SaxoniteCoreWebOnline/Online/Controllers/SaxController_Online.cs +++ b/SaxoniteCoreWebOnline/Online/Controllers/SaxController_Online.cs @@ -60,18 +60,18 @@ namespace Saxonite.Net.Core.Web.Online.Controllers if (userAccount == null) { SaLogger.Error(fn + " Unable to sign account in - null user account."); - errorMessage = Localiser["security.msg.signin.failed"]; + 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.failed"]; + errorMessage = Localiser["security.msg.signin.invalid"]; return false; } // 'Keep logged in' can be switched off by the application configuration. - keepLoggedIn = App.SessionConfig.Security.SignIn.KeepLoggedIn.Allowed && keepLoggedIn; + keepLoggedIn = App.AppConfig.Security.SignIn.KeepLoggedIn.Allowed && keepLoggedIn; // Log user in. userAccount.KeepLoggedIn_Online = keepLoggedIn; @@ -81,7 +81,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers if (!AccountMgr.UpdateUserAccount(userAccount, DbUpdateInfo)) { SaLogger.Warn(fn + " Failed to update account for " + userAccount.EmailAddress + ": " + AccountMgr.ErrorStateMessage); - errorMessage = Localiser["security.msg.signin.failed"]; + errorMessage = Localiser["security.msg.signin.invalid"]; return false; } SaLogger.Info(fn + " User session opened for " + userAccount.EmailAddress, true); @@ -176,7 +176,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers protected void SetKeepLoggedInCookie() { String keepLoggedInCookieValue = GenerateKeepLoggedInCookieValue(); - int cookieExpiresDays = Math.Max(Sax.Config.Security.SignIn.KeepLoggedIn.CookieExpiresDays_Min, Math.Min(Sax.Config.Security.SignIn.KeepLoggedIn.CookieExpiresDays_Max, SessionConfig.Security.SignIn.KeepLoggedIn.CookieExpiresDays)); + int cookieExpiresDays = Math.Max(Sax.Config.Security.SignIn.KeepLoggedIn.CookieExpiresDays_Min, Math.Min(Sax.Config.Security.SignIn.KeepLoggedIn.CookieExpiresDays_Max, AppConfig.Security.SignIn.KeepLoggedIn.CookieExpiresDays)); SetResponseCookie(CookieName_KeepLoggedIn, keepLoggedInCookieValue, DateTime.Now.AddDays(cookieExpiresDays), true); SaLogger.Info(this.GetType().Name + ".SetKeepLoggedInCookie 'Keep Logged In' cookie set. Value: " + keepLoggedInCookieValue); } diff --git a/SaxoniteCoreWebOnline/Online/Controllers/SecurityController-Ajax.cs b/SaxoniteCoreWebOnline/Online/Controllers/SecurityController-Ajax.cs index 6cdd155..cafe364 100644 --- a/SaxoniteCoreWebOnline/Online/Controllers/SecurityController-Ajax.cs +++ b/SaxoniteCoreWebOnline/Online/Controllers/SecurityController-Ajax.cs @@ -86,7 +86,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers if (!ValidUserSession) return Json(responseData.SetGoHome()); - if (!SessionConfig.Security.SignIn.KeepLoggedIn.Allowed) + if (!AppConfig.Security.SignIn.KeepLoggedIn.Allowed) return Json(responseData); // Resend the cookie to extend the expiry date if it is currently populated. @@ -397,7 +397,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers } internal static int GetRequestPasswordEmailLinkExpiresMinutes(SaxController_Online controller) { - int expiresMinutes = controller.SessionConfig.Security.SignIn.PasswordRequest.EmailLinkExpiresMins; + int expiresMinutes = controller.AppConfig.Security.SignIn.PasswordRequest.EmailLinkExpiresMins; int expiresMinutes_Min = Sax.Config.Security.SignIn.PasswordRequest.EmailLinkExpiresMins_Min; int expiresMinutes_Max = Sax.Config.Security.SignIn.PasswordRequest.EmailLinkExpiresMins_Max; return Math.Max(expiresMinutes_Min, Math.Min(expiresMinutes_Max, expiresMinutes)); diff --git a/SaxoniteCoreWebOnline/Online/Models/Model_Organisation_Members.cs b/SaxoniteCoreWebOnline/Online/Models/Model_Organisation_Members.cs new file mode 100644 index 0000000..1977437 --- /dev/null +++ b/SaxoniteCoreWebOnline/Online/Models/Model_Organisation_Members.cs @@ -0,0 +1,118 @@ +using Sarand.Net.Standard.Base.App; +using Sarand.Net.Standard.Base.Bus; +using Sarand.Net.Standard.Base.Core; +using Sarand.Net.Standard.Base.Helpers; +using Sarand.Net.Standard.Base.Utils; +using Saxonite.Base.App; +using Saxonite.Net.Core.Web.Base.App; +using Saxonite.Net.Core.Web.Base.Controllers; +using Saxonite.Net.Standard.Base.Bus; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Saxonite.Net.Core.Web.Online.Models +{ + //======================================================================= + public class Model_Organisation_Members : SaxModel_Online + { + //------------------------------------------------------------------- + // Construction. + public Model_Organisation_Members(SaxLocaliser localiser) : + base(localiser) + { + //AdditionalFiles_CSS.Add("css/sax-org-mbr.css"); + AdditionalFiles_JS.Add("js/sax-org-mbr.js"); + } + + //------------------------------------------------------------------- + // List configuration. + public class MemberFilterData + { + public String OrganisationId { get; set; } + public String OrganisationCaption { get; set; } + public String Member { get; set; } + public String OrganisationRole { get; set; } + } + public MemberFilterData Filter { get; } = new MemberFilterData(); + + //------------------------------------------------------------------- + // Member properties. + public String MemberId { get; set; } + public String MemberExists { get; set; } + // Member properties. + public String MemberOrganisationId { get; set; } + public String MemberEmailAddress { get; set; } + public String MemberNameSupplied{ get; set; } + public String MemberTitle { get; set; } + public String MemberFirstName { get; set; } + public String MemberLastName { get; set; } + public String MemberRoleCodeDelim = ","; + public List MemberRoleCodes { get; set; } + public List GetMemberRoleCodes() + { + List codes = new List(); + foreach (String code in MemberRoleCodes) + { + if (StringUtil.IsEmpty(code)) continue; + codes.AddRange(code.Split(MemberRoleCodeDelim)); + } + return codes; + } + public String MemberAdminComment { get; set; } + + //------------------------------------------------------------------- + // Validation. + //------------------------------------------------------------------- + // Member. + public bool ValidateMemberData(SaxController_Base.AjaxResponse_Data responseData) + { + // Member Email Address. + if (MemberEmailAddress != null) + { + responseData.SetControlValue(nameof(MemberEmailAddress), MemberEmailAddress); + if (StringUtil.IsEmpty(MemberEmailAddress)) + responseData.SetControlError(nameof(MemberEmailAddress), Localiser["msg.validation.mandatory.value"]); + else if (StringUtil.GetLength(MemberEmailAddress) > GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.UserAccount_EmailAddress)) + responseData.SetControlError(nameof(MemberEmailAddress), Localiser["msg.validation.maximum.length", GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.UserAccount_EmailAddress)]); + else if (!SacsDataValidationHelper.IsValidEmailAddress(MemberEmailAddress)) + responseData.SetControlError(nameof(MemberEmailAddress), Localiser["msg.validation.invalid.value"]); + } + + // Member First Name. + bool memberNameSupplied = StringUtil.ToBool(MemberNameSupplied); + if (MemberFirstName != null && memberNameSupplied) + { + responseData.SetControlValue(nameof(MemberFirstName), MemberFirstName); + if (StringUtil.IsEmpty(MemberFirstName)) + responseData.SetControlError(nameof(MemberFirstName), Localiser["msg.validation.mandatory.value"]); + else if (StringUtil.GetLength(MemberFirstName) > GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.UserAccount_FirstName)) + responseData.SetControlError(nameof(MemberFirstName), Localiser["msg.validation.maximum.length", GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.UserAccount_FirstName)]); + } + + // Member Last Name. + if (MemberLastName != null && memberNameSupplied) + { + responseData.SetControlValue(nameof(MemberLastName), MemberLastName); + if (StringUtil.IsEmpty(MemberLastName)) + responseData.SetControlError(nameof(MemberLastName), Localiser["msg.validation.mandatory.value"]); + else if (StringUtil.GetLength(MemberLastName) > GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.UserAccount_LastName)) + responseData.SetControlError(nameof(MemberLastName), Localiser["msg.validation.maximum.length", GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.UserAccount_LastName)]); + } + + // Member Admin Comment. + if (MemberAdminComment != null) + { + responseData.SetControlValue(nameof(MemberAdminComment), MemberAdminComment); + if (!StringUtil.IsEmpty(MemberAdminComment) && StringUtil.GetLength(MemberAdminComment) > GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.UserOrganisation_AdminComment)) + responseData.SetControlError(nameof(MemberAdminComment), Localiser["msg.validation.maximum.length", GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.UserOrganisation_AdminComment)]); + } + + return !responseData.HasControlErrors(); + } + + //------------------------------------------------------------------- + } + //======================================================================= +} diff --git a/SaxoniteCoreWebOnline/Online/Models/SaxModel_Online.cs b/SaxoniteCoreWebOnline/Online/Models/SaxModel_Online.cs index e86f9db..6f780cf 100644 --- a/SaxoniteCoreWebOnline/Online/Models/SaxModel_Online.cs +++ b/SaxoniteCoreWebOnline/Online/Models/SaxModel_Online.cs @@ -16,6 +16,8 @@ namespace Saxonite.Net.Core.Web.Online.Models { //------------------------------------------------------------------- // Data members. + private bool userCanManageOrganisationMembers = false; + private bool userCanManageOrganisationMembersChecked = false; private bool userCanManageOrganisationOpportunities = false; private bool userCanManageOrganisationOpportunitiesChecked = false; @@ -31,6 +33,23 @@ namespace Saxonite.Net.Core.Web.Online.Models public SaxSessionData_Online SessionData_Online { get { return SessionData as SaxSessionData_Online; } } // Security profile. public bool UserIsJournalist { get { return SessionData != null && SessionData.User != null && SessionData.User.IsJournalist; } } + public bool UserCanManageOrganisationMembers + { + get + { + if (!userCanManageOrganisationMembersChecked && ValidUserSession) + { + SaxUserOrganisation.Params prmUserOrganisation = new SaxUserOrganisation.Params(); + prmUserOrganisation.UserAccountIdsIncluded.Add(SessionData.User.UserAccountId); + prmUserOrganisation.OrganisationMembersOnly = true; + prmUserOrganisation.OrganisationRoles.Add(Sax.OrganisationRole.OpportunityAdmin); + prmUserOrganisation.StatusCat = SaxUserOrganisation.StatusCat.ActiveOnly; + userCanManageOrganisationMembers = (AccountMgr.GetUserOrganisationCount(prmUserOrganisation) > 0); + userCanManageOrganisationMembersChecked = true; + } + return userCanManageOrganisationMembers; + } + } public bool UserCanManageOrganisationOpportunities { get diff --git a/SaxoniteCoreWebOnline/SaxoniteCoreWebOnline.csproj b/SaxoniteCoreWebOnline/SaxoniteCoreWebOnline.csproj index 1fc109e..2e00d7e 100644 --- a/SaxoniteCoreWebOnline/SaxoniteCoreWebOnline.csproj +++ b/SaxoniteCoreWebOnline/SaxoniteCoreWebOnline.csproj @@ -28,21 +28,11 @@ - - PreserveNewest - true - PreserveNewest - PreserveNewest true PreserveNewest - - PreserveNewest - true - PreserveNewest - diff --git a/SaxoniteCoreWebOnline/Views/Account/Profile-Contact.cshtml b/SaxoniteCoreWebOnline/Views/Account/Profile-Contact.cshtml index fa91a36..7d52cb4 100644 --- a/SaxoniteCoreWebOnline/Views/Account/Profile-Contact.cshtml +++ b/SaxoniteCoreWebOnline/Views/Account/Profile-Contact.cshtml @@ -9,7 +9,22 @@
@Localiser["account.caption.profile.contact"]
-
@(Localiser[
+ @* Burger menu *@ + + @* Filter item templates *@ +
+ @* Burger dropdown item template - ul is ignored *@ +
    +
  • ...
  • +
+
@@ -130,7 +145,7 @@
@Html.HiddenFor(model => model.AccountRegionIdCountry, new { @id = "ifAccountRegionIdCountry", @class = "AccountRegionIdCountry" })
-
+
diff --git a/SaxoniteCoreWebOnline/Views/Account/Profile-Organisations.cshtml b/SaxoniteCoreWebOnline/Views/Account/Profile-Organisations.cshtml index d99040c..6014d8e 100644 --- a/SaxoniteCoreWebOnline/Views/Account/Profile-Organisations.cshtml +++ b/SaxoniteCoreWebOnline/Views/Account/Profile-Organisations.cshtml @@ -33,8 +33,12 @@
-
-
+
+
+
+
+
@Localiser["label.members"]: 
+
@Localiser["label.opportunities"]: 
diff --git a/SaxoniteCoreWebOnline/Views/Account/Profile-Sports.cshtml b/SaxoniteCoreWebOnline/Views/Account/Profile-Sports.cshtml index f1b29f1..17df64b 100644 --- a/SaxoniteCoreWebOnline/Views/Account/Profile-Sports.cshtml +++ b/SaxoniteCoreWebOnline/Views/Account/Profile-Sports.cshtml @@ -9,7 +9,22 @@
@Localiser["account.caption.profile.sports"]
-
@(Localiser[
+ @* Burger menu *@ + + @* Filter item templates *@ +
+ @* Burger dropdown item template - ul is ignored *@ +
    +
  • ...
  • +
+
@@ -24,7 +39,15 @@
-
@(Localiser[
+ @* Burger menu *@ +
@@ -58,7 +81,7 @@
-
+
diff --git a/SaxoniteCoreWebOnline/Views/Account/Profile.cshtml b/SaxoniteCoreWebOnline/Views/Account/Profile.cshtml index 4830148..44f6780 100644 --- a/SaxoniteCoreWebOnline/Views/Account/Profile.cshtml +++ b/SaxoniteCoreWebOnline/Views/Account/Profile.cshtml @@ -34,7 +34,9 @@ sax.dlg.title.selectRegion = '@Localiser.GetJavascriptText("reference.caption.region.select")'; sax.dlg.title.selectSport = '@Localiser.GetJavascriptText("reference.caption.sport.select")'; + +
diff --git a/SaxoniteCoreWebOnline/Views/Common/CodeSelect.cshtml b/SaxoniteCoreWebOnline/Views/Common/CodeSelect.cshtml new file mode 100644 index 0000000..43f5980 --- /dev/null +++ b/SaxoniteCoreWebOnline/Views/Common/CodeSelect.cshtml @@ -0,0 +1,56 @@ +@model SaxModel_Online +@inject SaxLocaliser Localiser + + + + + +
+ + @* Filter *@ +
+ +
+ + @* Available codes *@ +
+ +
+ +
+ + @* Code selection *@ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ + @* Selected codes *@ +
+ +
+ +
+
+ +
+ +
+
+
+
+
+ +
diff --git a/SaxoniteCoreWebOnline/Views/Common/OrganisationRequest.cshtml b/SaxoniteCoreWebOnline/Views/Common/OrganisationRequest.cshtml index 274ba14..01f8410 100644 --- a/SaxoniteCoreWebOnline/Views/Common/OrganisationRequest.cshtml +++ b/SaxoniteCoreWebOnline/Views/Common/OrganisationRequest.cshtml @@ -75,7 +75,7 @@
- +
@@ -132,7 +132,7 @@
-
+
@@ -145,7 +145,7 @@
-
+
diff --git a/SaxoniteCoreWebOnline/Views/Journalist/Panel-ArticleDialog.cshtml b/SaxoniteCoreWebOnline/Views/Journalist/Panel-ArticleDialog.cshtml index e903d9f..028a36c 100644 --- a/SaxoniteCoreWebOnline/Views/Journalist/Panel-ArticleDialog.cshtml +++ b/SaxoniteCoreWebOnline/Views/Journalist/Panel-ArticleDialog.cshtml @@ -55,7 +55,7 @@
@Html.HiddenFor(model => model.ArticleSportId, new { @id = "ifArticleSportId", @class = "ArticleSportId" })
@Html.TextBoxFor(model => model.ArticleSportCaption, new { @id = "ifArticleSportCaption", @class = "ArticleSportCaption sax-input", @readonly = "readonly", @onDblClick = "sax.selectSport()" })
-
+
@@ -68,7 +68,7 @@
@Html.HiddenFor(model => model.ArticleRegionId, new { @id = "ifArticleRegionId", @class = "ArticleRegionId" })
@Html.TextBoxFor(model => model.ArticleRegionCaption, new { @id = "ifArticleRegionCaption", @class = "ArticleRegionCaption sax-input", @readonly = "readonly", @onDblClick = "sax.selectRegion()" })
-
+
diff --git a/SaxoniteCoreWebOnline/Views/Organisation/Members.cshtml b/SaxoniteCoreWebOnline/Views/Organisation/Members.cshtml new file mode 100644 index 0000000..c2e383c --- /dev/null +++ b/SaxoniteCoreWebOnline/Views/Organisation/Members.cshtml @@ -0,0 +1,115 @@ +@model Model_Organisation_Members +@inject SaxLocaliser Localiser +@{ + ViewData["Title"] = "Organisation - Members"; +} + +
+ + + + @* Filter panel *@ +
+ @* Filter header *@ +
+ @* Filter caption *@ +
@Localiser["organisation.members"]
+
+ @* Burger menu *@ + + @* Filter item templates *@ +
+ @* Burger dropdown item template - ul is ignored *@ +
    +
  • ...
  • +
+
+
+
+
+ @using (@Html.BeginForm(FormMethod.Post, new { @id = "frmMemberFilter", @class = "sax-form", @action = "#", @method = "POST", onsubmit = "return false;" })) + { +
+
@Localiser["label.organisation"]
+
+ @Html.HiddenFor(model => model.Filter.OrganisationId, new { @id = "ifFilterOrganisationId", @class = "FilterOrganisationId" }) +
+
+
+
+
+
@Localiser["label.search"]
+
+
+ } +
+
+ + @* Results Panel *@ +
+
+
+ + @* element templates *@ +
+ + @* Member item template *@ +
+
+
+ @* Member caption *@ +
+
+
+ @* Burger menu *@ + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + @* Burger dropdown item template - ul is ignored *@ +
    +
  • ...
  • +
+ +
+
+ +
+ + + + + diff --git a/SaxoniteCoreWebOnline/Views/Organisation/Panel-MemberDialog.cshtml b/SaxoniteCoreWebOnline/Views/Organisation/Panel-MemberDialog.cshtml new file mode 100644 index 0000000..19e8c48 --- /dev/null +++ b/SaxoniteCoreWebOnline/Views/Organisation/Panel-MemberDialog.cshtml @@ -0,0 +1,119 @@ +@model Model_Organisation_Members +@inject SaxLocaliser Localiser +@{ + /* Organisation Members - Member dialog panel */ +} + + +@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmMemberId", @class = "sax-form" })) +{ + +} +
+ + @using (@Html.BeginForm(FormMethod.Post, new { @id = "frmMember", @class = "sax-form", @onSubmit = "return false;" })) + { + + + + + + @* Member Email Address *@ +
+
+ +
+
+ +
+
+
+ + @* Member Display Name *@ +
+
+ +
+
+ +
+
+
+ + @* Member Name *@ +
+ +
+ @* User Title *@ +
+
+ +
+
+ +
+
+
+
+ +
+ @* User First Name *@ +
+
+ +
+
+ +
+
+
+
+ +
+ @* User Last Name *@ +
+
+ +
+
+ +
+
+
+
+ +
+ + @* Member roles *@ +
+
+ +
+
+ +
+
+
+
+
+ + @* Admin Comment *@ +
+
+ +
+
+ +
+
+
+ + } + +
diff --git a/SaxoniteCoreWebOnline/Views/Organisation/Panel-OpportunityDialog.cshtml b/SaxoniteCoreWebOnline/Views/Organisation/Panel-OpportunityDialog.cshtml index 18795fd..ae69f9c 100644 --- a/SaxoniteCoreWebOnline/Views/Organisation/Panel-OpportunityDialog.cshtml +++ b/SaxoniteCoreWebOnline/Views/Organisation/Panel-OpportunityDialog.cshtml @@ -1,7 +1,7 @@ @model Model_Organisation_Opportunities @inject SaxLocaliser Localiser @{ - /* Opportunities - Opportunity dialog panel */ + /* Organisation Opportunities - Opportunity dialog panel */ } + @@ -130,6 +133,10 @@ { } + @if (Model.UserCanManageOrganisationMembers) + { + + } @if (Model.UserCanManageOrganisationOpportunities) { diff --git a/SaxoniteCoreWebOnline/appsettings.Development.json b/SaxoniteCoreWebOnline/appsettings.Development.json index 389c684..89afa5c 100644 --- a/SaxoniteCoreWebOnline/appsettings.Development.json +++ b/SaxoniteCoreWebOnline/appsettings.Development.json @@ -9,7 +9,7 @@ "EmailConfigs": { "SAX-SF-Dev": { "Message": { - "AddressFrom": "DoNotReply@solumforti.com" + "AddressFrom": "DoNotReply@sportierre.com" }, "SMTP": { "Server": "smtp.ionos.co.uk", @@ -18,8 +18,8 @@ }, "Send": { "Using": 2, - "UserName": "info@solumforti.com", - "Password": "solfor-info-5Ekur3" + "UserName": "info@sportierre.com", + "Password": "5p0rTi3rre-info-2021!" } } } diff --git a/SaxoniteCoreWebOnline/appsettings.Machine.Sarand106.json b/SaxoniteCoreWebOnline/appsettings.Machine.Sarand106.json deleted file mode 100644 index 5956827..0000000 --- a/SaxoniteCoreWebOnline/appsettings.Machine.Sarand106.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "SessionConfigs": { - "Saxonite-Dev": { - "Environments": [ "Development" ], - "Instance": "Dev", - "CoreDatabase": { - "Location": "MsSql:Sarand106\\SQL2014.Saxonite_Dev", - "User": "saxonite", - "Password": "sax-dev-2021" - }, - "Email": { - "Context": "SAX-SF-Dev", - "ContactUs": { - "AddressesTo": [ "dev@solumforti.com" ] - } - }, - "Documents": { - "RootFolder": "D:\\Saxonite\\Data\\Docs" - }, - "Logging": { - "RootFolder": "D:\\Sarand\\Projects\\User\\AHoward\\VS2017\\Saxonite\\Logs" - } - }, - "Saxonite-Test": { - "Environments": [ "Production" ], - "Instance": "QC", - "CoreDatabase": { - "Location": "MsSql:Sarand106\\SQL2014.Saxonite_Dev", - "User": "saxonite", - "Password": "sax-dev-2021" - }, - "Email": { - "Context": "SAX-SF-Dev", - "ContactUs": { - "AddressesTo": [ "qc@solumforti.com" ], - "AddressesBcc": [ ] - - } - }, - "Documents": { - "RootFolder": "D:\\Saxonite\\Data\\Docs" - }, - "Logging": { - "RootFolder": "D:\\Sarand\\Projects\\User\\AHoward\\VS2017\\Saxonite\\QC\\Logs" - }, - "Variants": [ - { - "ApplicationRootFolder": "D:\\Sarand\\Projects\\User\\AHoward\\VS2017\\Saxonite\\QC2\\Bin", - "Instance": "QC", - "Logging": { - "RootFolder": "D:\\Sarand\\Projects\\User\\AHoward\\VS2017\\Saxonite\\QC2\\Logs" - } - } - ] - } - } -} diff --git a/SaxoniteCoreWebOnline/appsettings.Machine.Sarand107.json b/SaxoniteCoreWebOnline/appsettings.Machine.Sarand107.json index 57c2bfe..fa35e1e 100644 --- a/SaxoniteCoreWebOnline/appsettings.Machine.Sarand107.json +++ b/SaxoniteCoreWebOnline/appsettings.Machine.Sarand107.json @@ -1,5 +1,5 @@ { - "SessionConfigs": { + "AppConfigs": { "Saxonite-Dev": { "Environments": [ "Development" ], "Instance": "Dev", @@ -11,7 +11,7 @@ "Email": { "Context": "SAX-SF-Dev", "ContactUs": { - "AddressesTo": [ "dev@solumforti.com" ] + "AddressesTo": [ "info@sportierre.com" ] } }, "Documents": { @@ -32,8 +32,8 @@ "Email": { "Context": "SAX-SF-Dev", "ContactUs": { - "AddressesTo": [ "qc@solumforti.com" ], - "AddressesBcc": [ ] + "AddressesTo": [ "info@sportierre.com" ], + "AddressesBcc": [] } }, diff --git a/SaxoniteCoreWebOnline/appsettings.Machine.Sarand601.json b/SaxoniteCoreWebOnline/appsettings.Machine.Sarand601.json index 8f46427..137cc57 100644 --- a/SaxoniteCoreWebOnline/appsettings.Machine.Sarand601.json +++ b/SaxoniteCoreWebOnline/appsettings.Machine.Sarand601.json @@ -1,5 +1,5 @@ { - "SessionConfigs": { + "AppConfigs": { "SAX-Prod": { "Environments": [ "Production" ], "Instance": "", @@ -26,14 +26,14 @@ "Context": "SAX-SF-Prod", "ContactUs": { "AddressesTo": [], - "AddressesBcc": [ "dev@solumforti.com" ] + "AddressesBcc": [ "info@sportierre.com" ] }, "Signup": { "Activate": { - "AddressesBcc": [ "signup@solumforti.com" ] + "AddressesBcc": [ "info@sportierre.com" ] }, "Welcome": { - "AddressesBcc": [ "signup@solumforti.com" ] + "AddressesBcc": [ "info@sportierre.com" ] } } }, diff --git a/SaxoniteCoreWebOnline/appsettings.Machine.Sarand602.json b/SaxoniteCoreWebOnline/appsettings.Machine.Sarand602.json deleted file mode 100644 index ef1ce86..0000000 --- a/SaxoniteCoreWebOnline/appsettings.Machine.Sarand602.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "SessionConfigs": { - "SAX-Prod": { - "Environments": [ "Production" ], - "Instance": "", - "CoreDatabase": { - "Location": "MsSql:.\\SQL2016EXP.Saxonite_Core_Live", - "User": "sax-prod-user", - "Password": "sax-live-202104" - }, - "Security": { - "Password": { - "MinLength": 8 - }, - "SignIn": { - "KeepLoggedIn": { - "Allowed": true, - "CookieExpiresDays": 30 - }, - "PasswordRequest": { - "LinkExpiresMins": 30 - } - } - }, - "Email": { - "Context": "SAX-SF-Prod", - "ContactUs": { - "AddressesTo": [], - "AddressesBcc": [ "dev@solumforti.com" ] - }, - "Signup": { - "Activate": { - "AddressesBcc": [ "signup@solumforti.com" ] - }, - "Welcome": { - "AddressesBcc": [ "signup@solumforti.com" ] - } - } - }, - "Documents": { - "RootFolder": "C:\\Sarand\\Saxonite\\Prod\\SaxoniteOnline\\Docs" - }, - "Logging": { - "RootFolder": "C:\\Sarand\\Saxonite\\Prod\\SaxoniteOnline\\Logs" - }, - "Variants": [ - { - "ApplicationRootFolder": "C:\\Sarand\\Saxonite\\PreProd\\SaxoniteOnline\\Bin", - "Instance": "PreProd", - "Documents": { - "RootFolder": "C:\\Sarand\\Saxonite\\PreProd\\SaxoniteOnline\\Docs" - }, - "Logging": { - "RootFolder": "C:\\Sarand\\Saxonite\\PreProd\\SaxoniteOnline\\Logs" - } - } - ] - } - } -} diff --git a/SaxoniteCoreWebOnline/appsettings.Production.json b/SaxoniteCoreWebOnline/appsettings.Production.json index 7860010..269024f 100644 --- a/SaxoniteCoreWebOnline/appsettings.Production.json +++ b/SaxoniteCoreWebOnline/appsettings.Production.json @@ -2,7 +2,7 @@ "EmailConfigs": { "SAX-SF-Prod": { "Message": { - "AddressFrom": "DoNotReply@solumforti.com" + "AddressFrom": "DoNotReply@sportierre.com" }, "SMTP": { "Server": "smtp.ionos.co.uk", @@ -11,8 +11,8 @@ }, "Send": { "Using": 2, - "UserName": "info@solumforti.com", - "Password": "solfor-info-5Ekur3" + "UserName": "info@sportierre.com", + "Password": "5p0rTi3rre-info-2021!" } } } diff --git a/SaxoniteCoreWebOnline/wwwroot/css/sax-account.css b/SaxoniteCoreWebOnline/wwwroot/css/sax-account.css index 9e2b749..10dbce6 100644 --- a/SaxoniteCoreWebOnline/wwwroot/css/sax-account.css +++ b/SaxoniteCoreWebOnline/wwwroot/css/sax-account.css @@ -7,6 +7,8 @@ --color-profile: var(--color-alt1); } +/* Profile */ + .sax-profile { margin: 12px 0; @@ -39,6 +41,8 @@ background-color: #fff; } +/* Profile sections */ + .sax-profile .sax-panel-info, .sax-profile .sax-panel-form { @@ -63,6 +67,12 @@ color: var(--color-primary-dark); } +.sax-profile .sax-panel-info > .sax-content +{ + max-height: 240px; + overflow-y: auto; +} + .sax-profile .sax-panel-results .sax-panel-info > .sax-header { text-align: left; diff --git a/SaxoniteCoreWebOnline/wwwroot/css/sax-code-sel.css b/SaxoniteCoreWebOnline/wwwroot/css/sax-code-sel.css new file mode 100644 index 0000000..781675b --- /dev/null +++ b/SaxoniteCoreWebOnline/wwwroot/css/sax-code-sel.css @@ -0,0 +1,37 @@ +/* + Code Selection stylesheet. +*/ + +/* Panels */ + +.sax-codeSelect-codes +{ + padding:2px 0; + background-color:#f0f0f0; + border: 1px solid #b0b0b0; + border-radius: 6px; +} + +#dlgCodeSelect.sax-form button.sax-button +{ + min-width: 80px; + margin: 4px 0; +} + +.sax-panel-item.Code +{ + margin: 2px 4px; + padding: 2px 4px; + background-color:#fff; + border: 2px solid #d8d8ff; + border-radius: 4px; + cursor:pointer; +} + +.sax-panel-item.Code.sel +{ + background-color: #ececff; + border-color: #8a8aff; +} + +/* The End */ diff --git a/SaxoniteCoreWebOnline/wwwroot/css/sax-data.css b/SaxoniteCoreWebOnline/wwwroot/css/sax-data.css index 052d555..4ab6781 100644 --- a/SaxoniteCoreWebOnline/wwwroot/css/sax-data.css +++ b/SaxoniteCoreWebOnline/wwwroot/css/sax-data.css @@ -130,9 +130,9 @@ .sax-panel-item { - margin: 8px auto; + margin: 6px auto; background-color: #f8f8f8; - border: solid 2px #979797; + border: solid 1px #979797; border-radius: 6px; -webkit-border-radius: 6px; -moz-border-radius: 6px; @@ -154,6 +154,11 @@ font-weight: bold; } +.sax-panel-item > .sax-header .dropdown-menu +{ + padding: 4px 8px; +} + .sax-panel-item > .sax-header .sax-action { border-radius: 4px; @@ -177,7 +182,7 @@ .sax-panel-item > .sax-content { - margin: 6px 8px; + margin: 4px 8px; padding: 4px 4px; } @@ -191,7 +196,7 @@ .sax-panel-item.ProfileSport, .sax-panel-item.ProfileSport > .sax-header { - border-color: var(--color-alt2); + border-color: var(--color-alt4); } /* Organisations */ @@ -202,6 +207,14 @@ border-color: var(--color-alt3); } +/* Members */ + +.sax-panel-item.Member, +.sax-panel-item.Member > .sax-header +{ + border-color: var(--color-alt2); +} + /* Articles */ .sax-panel-item.Article, @@ -228,4 +241,31 @@ background-color: var(--color-opportunity-pale); } +/* Item statuses */ + +.sax-status-awa +{ + color: darkorange; +} + +.sax-status-atv +{ + color: green; +} + +.sax-status-ina +{ + color: darkred; +} + +.sax-status-hld +{ + color: dodgerblue; +} + +.sax-status-del +{ + color: purple; +} + /* The End */ diff --git a/SaxoniteCoreWebOnline/wwwroot/css/sax-form.css b/SaxoniteCoreWebOnline/wwwroot/css/sax-form.css index eff3471..259d4c0 100644 --- a/SaxoniteCoreWebOnline/wwwroot/css/sax-form.css +++ b/SaxoniteCoreWebOnline/wwwroot/css/sax-form.css @@ -87,7 +87,7 @@ .sax-panel-info > .sax-content, .sax-panel-form > .sax-content { - padding: 12px 12px; + padding: 4px 8px; background-color: #f8f8f8; } @@ -128,6 +128,13 @@ { } +/* Form controls */ + +.sax-form .sax-form-control +{ + margin: 0 0 8px 0; +} + .sax-form .sax-form-control .sax-caption, .sax-form .sax-form-control .sax-label { @@ -156,11 +163,6 @@ /* Controls */ -.sax-form .sax-form-control -{ - margin: 0 0 12px 0; -} - .sax-form .sax-input, .sax-form .sax-textarea, .sax-form .sax-select { padding: 3px 8px 0 8px; diff --git a/SaxoniteCoreWebOnline/wwwroot/css/sax-main.css b/SaxoniteCoreWebOnline/wwwroot/css/sax-main.css index b325459..9e175b4 100644 --- a/SaxoniteCoreWebOnline/wwwroot/css/sax-main.css +++ b/SaxoniteCoreWebOnline/wwwroot/css/sax-main.css @@ -6,11 +6,6 @@ :root { - /* - #556B2F Dark Olive Green - Ref. https://www.crispedge.com/color/556B2F - Darker: #445525, #33401c. Lighter: #768858, #99a682, #a6c275, #b7ce90. - */ --color-primary: #556B2F; --color-primary-dark: #425324; --color-primary-light: #a0ac8b; @@ -19,6 +14,7 @@ --color-alt1: #2f6b63; --color-alt2: #452f6b; --color-alt3: #6b2f37; + --color-alt4: #2f556b; /* Activity colours */ --color-article-dark: #7339ac; --color-article: #9966CC; diff --git a/SaxoniteCoreWebOnline/wwwroot/js/sax-account.js b/SaxoniteCoreWebOnline/wwwroot/js/sax-account.js index 55f37e1..1571d09 100644 --- a/SaxoniteCoreWebOnline/wwwroot/js/sax-account.js +++ b/SaxoniteCoreWebOnline/wwwroot/js/sax-account.js @@ -202,7 +202,7 @@ //sax.debug('addProfileSport'); let $dlg = sax.$dlg.profileSport; $('.UserSportId,.ProfileSportExists,#ifProfileSportId,input.sax-input,select.sax-select', $dlg).val(''); - $('#pbSelectSport', $dlg).show(); + $('.SelectSport', $dlg).show(); $dlg.dialog('option', 'title', sax.dlg.title.addUserSport); sax.openDialog($dlg, 480, 400); } @@ -224,7 +224,7 @@ $('#ifProfileSport', $dlg).val(item.sportName); $('#cbProfileSportParticipation', $dlg).val(item.sportParticipationCode); $('#cbProfileSportOfficiating', $dlg).val(item.sportOfficiatingCode); - $('#pbSelectSport', $dlg).hide(); + $('.SelectSport', $dlg).hide(); $dlg.dialog('option', 'title', sax.dlg.title.updateUserSport); sax.openDialog($dlg, 480, 400); } @@ -273,7 +273,7 @@ } sax.onRefreshProfileOrganisationsInfo = function (response) { - //sax.debug('onRefreshProfileOrganisationsInfo', response); + sax.debug('onRefreshProfileOrganisationsInfo', response); if (!response || !response.success) return; var $section = $('.sax-panel-section.ProfileOrganisations'); $('.sax-message', $section).stop(true, false).fadeOut(200).empty(); @@ -287,6 +287,10 @@ let item = items[itemIdx]; let $item = $tmpl.clone().prop('id', 'ProOrg_' + item.id); $('.OrganisationName', $item).text(item.name); + $('.OrganisationSport', $item).text(item.sportName); + $('.OrganisationRegion', $item).text(item.regionName); + $('.OrganisationMemberCount', $item).toggle(!!item.memberCount).find('.MemberCount').text(item.memberCount); + $('.OrganisationOpportunityCount', $item).toggle(!!item.opportunityCount).find('.MemberCount').text(item.opportunityCount); $items.append($item); } $items.show(); diff --git a/SaxoniteCoreWebOnline/wwwroot/js/sax-code-sel.js b/SaxoniteCoreWebOnline/wwwroot/js/sax-code-sel.js new file mode 100644 index 0000000..b01bcd8 --- /dev/null +++ b/SaxoniteCoreWebOnline/wwwroot/js/sax-code-sel.js @@ -0,0 +1,123 @@ +/* + * Code selection. + */ + +(function (win, doc, undef) +{ + "use strict"; + let cs = sax.codeSelect = sax.codeSelect || { fn: {}, $dlg: {}, codeIdPrefix: 'Code_' }; + + //-------------------------------------------------------------- + /* + * Code selection config: + * - caption - dialog title. + * - width: preferred dialog width (default: 400). + * - height: preferred dialog height (default: 400). + * - showFilter: whether to display filter control(s). + * - filterLabel: filter code field caption. + * - maxItems: maximum number of available items to list. + * - codes - list of {code,desc,comment} objects + * - selectedCodes - list of codes. + * - fnCallback - method to call on completion to store codes taking list of selected codes as parameter. + */ + cs.fn.openCodeSelect = function (cfg) + { + //sax.debug('openSelectCode', cfg); + cs.fn.init(); + cs.cfg = cfg; + cs.selectedCodes = cfg.selectedCodes || []; + cs.fn.displayCodes(cfg.codes); + sax.openDialog(cs.$dlg.dialog('option', { title: cfg.caption || 'Select Codes' }), cs.cfg.width || 400, cs.cfg.height || 400); + } + cs.fn.displayCodes = function (codes) + { + let $codesAvail = $('.sax-codeSelect-codes.AvailableCodes').empty(); + let $codesSelec = $('.sax-codeSelect-codes.SelectedCodes').empty(); + let $codeTmpl = $('.sax-panel-item-template .sax-panel-item.Code'); + for (let codeIdx = 0; codeIdx < codes.length; codeIdx++) + { + let code = codes[codeIdx]; + let selected = cs.selectedCodes.indexOf(code.code) >= 0; + let $code = $codeTmpl.clone(); + $($code).prop('id', cs.codeIdPrefix + code.code).attr('aria-code', code.code).on('click', function () { cs.fn.onClickCode(code.code); }).on('dblclick', function () { cs.fn.onDblClickCode(code.code); }).find('.CodeDesc').text(code.desc).end(); + if (selected) $codesSelec.append($code); + else $codesAvail.append($code); + } + } + cs.fn.onClickCode = function (code) + { + $('.sax-panel-item.Code').removeClass('sel'); + $('.sax-panel-item.Code[aria-code="' + code + '"]').addClass('sel'); + } + cs.fn.onDblClickCode = function (code) + { + let codeIdx = cs.selectedCodes.indexOf(code); + //sax.debug('onDblClickCode', code, codeIdx); + if (codeIdx >= 0) cs.selectedCodes.splice(codeIdx, 1); else cs.selectedCodes.push(code) + cs.fn.displayCodes(cs.cfg.codes) + $('.sax-panel-item.Code[aria-code="' + code + '"]').addClass('sel'); + } + cs.fn.onClickSelect = function () + { + let codes = []; + $('.sax-codeSelect-codes.AvailableCodes .sax-panel-item.Code.sel').each(function (idx, el) + { + let code = $(el).attr('aria-code'); + if (code) + { + codes.push(code); + cs.selectedCodes.push(code); + }; + }); + cs.fn.displayCodes(cs.cfg.codes) + for (const code of codes) + $('.sax-panel-item.Code[aria-code="' + code + '"]').addClass('sel'); + } + cs.fn.onClickRemove = function () + { + let codes = []; + $('.sax-codeSelect-codes.SelectedCodes .sax-panel-item.Code.sel').each(function (idx, el) + { + let code = $(el).attr('aria-code'); + if (code) + { + let codeIdx = cs.selectedCodes.indexOf(code); + if (codeIdx >= 0) + { + codes.push(code); + cs.selectedCodes.splice(codeIdx, 1); + } + }; + }); + cs.fn.displayCodes(cs.cfg.codes) + for (const code of codes) + $('.sax-panel-item.Code[aria-code="' + code + '"]').addClass('sel'); + } + cs.fn.storeSelectCode = function () + { + //sax.debug('storeSelectCode', cs.cfg, cs.selectedCodes); + if (cs.cfg.fnCallback) cs.cfg.fnCallback(cs.selectedCodes); + cs.$dlg.dialog('close'); + cs.fn.cleanUp(); + } + cs.fn.cleanUp = function () + { + delete cs.cfg; + delete cs.selectedCodes; + } + + //-------------------------------------------------------------- + cs.fn.init = function () + { + if (!cs.$dlg) return; + var opt = sax.dlgOpts(); + opt.buttons[sax.dlg.btn.ok] = function () { cs.fn.storeSelectCode(); }; + opt.buttons[sax.dlg.btn.cancel] = function () { cs.fn.cleanUp(); $(this).dialog('close'); }; + cs.$dlg = $('#dlgCodeSelect').dialog(opt); + $('.sax-button.Select', cs.$dlg).on('click', function () { cs.fn.onClickSelect(); }); + $('.sax-button.Remove', cs.$dlg).on('click', function () { cs.fn.onClickRemove(); }); + } + +}(window, window.document)); + +/* The End */ \ No newline at end of file diff --git a/SaxoniteCoreWebOnline/wwwroot/js/sax-main.js b/SaxoniteCoreWebOnline/wwwroot/js/sax-main.js index 2794bb2..924b4bd 100644 --- a/SaxoniteCoreWebOnline/wwwroot/js/sax-main.js +++ b/SaxoniteCoreWebOnline/wwwroot/js/sax-main.js @@ -70,6 +70,21 @@ } sax.debug = sax.debugMode ? win.console.log.bind(win.console) : function () { }; + //-------------------------------------------------------------- + sax.compareCI = function (a, b) + { + var comparison = a.toLowerCase().localeCompare(b.toLowerCase()); + if (comparison === 0) + return a.localeCompare(b); + return comparison; + } + sax.arraySortCI = function () + { + if (!this) return this; + this.sort(function (a, b) { return sax.compareCI(a, b); }); + return this; + }; + //-------------------------------------------------------------- sax.setOddDataRowClass = function (rowSel, cName) { @@ -387,7 +402,7 @@ // Data entry. sax.clearControlErrors = function (context) { - var $ctrls = $('.sax-form-control', $(context)).removeClass('sax-error'); + var $ctrls = $('.sax-form-control', $(context || doc.body)).removeClass('sax-error'); $('.sax-message', $ctrls).empty(); } sax.clearControlError = function (sel) @@ -402,8 +417,8 @@ } sax.refreshControlValues = function (controlValues, context) { - var $frmCtrls = $('.sax-form-control', $(context)).removeClass('sax-error'); - sax.clearControlErrors(); + var $frmCtrls = $('.sax-form-control', $(context || doc.body)).removeClass('sax-error'); + sax.clearControlErrors(context); var values = (controlValues || []); var valuesLen = values.length; for (var valIdx = 0; valIdx < valuesLen; valIdx++) @@ -516,4 +531,14 @@ }(window, window.document)); +//-------------------------------------------------------------- +(function () +{ + if (typeof Object.defineProperty === 'function') + { + try { Object.defineProperty(Array.prototype, 'sortCI', { value: sax.arraySortCI }); } catch (e) { } + } + if (!Array.prototype.sortBy) Array.prototype.sortCI = sax.arraySortCI; +})(); + /* The End */ \ No newline at end of file diff --git a/SaxoniteCoreWebOnline/wwwroot/js/sax-org-mbr.js b/SaxoniteCoreWebOnline/wwwroot/js/sax-org-mbr.js new file mode 100644 index 0000000..dc2d110 --- /dev/null +++ b/SaxoniteCoreWebOnline/wwwroot/js/sax-org-mbr.js @@ -0,0 +1,322 @@ +/* + * Organisation - Members page scripts. + */ + +(function (win, doc, undef) +{ + "use strict"; + sax.members = {}; + sax.itemIdPrefix = 'Itm_'; + + //-------------------------------------------------------------- + sax.refreshResults = function (delay) + { + sax.data.userCanManageMembers = false; + if (!sax.awaitingRefreshResponse) + { + var $items = $('.sax-panel-item-list', sax.$pnl.results).fadeOut(200).empty(); + var $msgs = $('.sax-panel-messages', sax.$pnl.results).hide().empty().append($('
' + sax.msg.dataQuerying + '
')).fadeIn(200); + } + sax.ajax.refreshResults = sax.ajax.refreshResults || { url: sax.url.base + 'Organisation/Ajax/GetMemberList', formId: 'frmMemberFilter', callbackFn: sax.doRefreshResults }; + sax.doAjaxRequest(sax.ajax.refreshResults, delay || 50); + sax.awaitingRefreshResponse = true; + } + sax.getFilterDropdownTemplate = function () + { + return $('.sax-panel-item-template .sax-dropdown-menu.Members li.sax-dropdown-item', sax.$pnl.filter).clone(); + } + sax.getItemTemplate = function () + { + return $('.sax-panel-item-template .sax-panel-item.Member', sax.$pnl.results).clone(); + } + sax.getItemDropdownTemplate = function () + { + return $('.sax-panel-item-template .sax-dropdown-menu.Member li.sax-dropdown-item', sax.$pnl.results).clone(); + } + sax.doRefreshResults = function (response) + { + sax.debug('doRefreshResults', response); + delete sax.awaitingRefreshResponse; + sax.refreshControlValues(response.controlValues); + let $mnu = $('.sax-burger ul.sax-dropdown-menu', sax.$pnl.filter).empty(); + var $msgs = $('.sax-panel-messages', sax.$pnl.results).hide().empty(); + var $items = $('.sax-panel-item-list', sax.$pnl.results).hide().empty(); + if (!response.success) return; + // Reference data - roles. + if (response.memberRoles && response.memberRoles.length) + sax.data.memberRoles = response.memberRoles; + // Menu. + $mnu.append(sax.getFilterMenuOption(sax.lbl.refresh, function () { sax.refreshResults(); })); + if (sax.data.userCanManageMembers = (response.user && response.user.canManage)) + $mnu.append(sax.getFilterMenuOption(sax.lbl.add, function () { sax.createItem(); })); + $('.sax-burger', sax.$pnl.filter).toggle(!!$('.sax-dropdown-item', $mnu).length); + // Items. + var items = response.members; + if (!items.length) + return $msgs.append($('
' + sax.msg.dataNoneFound + '
')).fadeIn(500); + for (let itemIdx = 0; itemIdx < items.length; itemIdx++) + { + let item = items[itemIdx]; + let $item = sax.getItemTemplate(); + sax.populateItem($item, item); + $items.append($item); + } + $items.fadeIn(50); + } + sax.getFilterMenuOption = function (lbl, fn) + { + return sax.getMenuOption(sax.getFilterDropdownTemplate(), lbl, fn); + } + sax.getMenuOption = function ($opt, lbl, fn) + { + return $opt.find('div.sax-label').text(lbl).end().on('click', fn); + } + sax.populateItem = function ($item, item) + { + if (!item) return; + $item.prop('id', sax.itemIdPrefix + item.id); + $('.MemberEmail', $item).text(item.emailAddress); + $('.MemberDisplayName', $item).text(item.displayName); + $('.MemberAdminComment', $item).text(item.adminComment); + $('.MemberStatus', $item).text(item.statusDesc).addClass('sax-status-' + item.statusCode.toLowerCase()); + let $mnu = $('ul.sax-dropdown-menu', $item).empty(); + if (sax.data.userCanManageMembers) + { + let fnUpd = function () { sax.updateItem(item.id) }; + $mnu.append(sax.getItemMenuOption(sax.lbl.edit, fnUpd)); + if (!item.currentUser) + $mnu.append(sax.getItemMenuOption(sax.lbl.remove, function () { sax.removeItem(item.id) })); + if (item.awaiting) + $mnu.append(sax.getItemMenuOption(sax.lbl.activate, function () { sax.activateItem(item.id) })); + $('.sax-caption .sax-action', $item).off('click').on('click', fnUpd); + } + else $('.sax-caption .sax-action', $item).removeClass('sax-action'); + $('.sax-burger', $item).toggle(!!$('.sax-dropdown-menu .sax-dropdown-item', $item).length); + $('.sax-panel-messages', sax.$pnl.results).hide().empty(); + } + sax.getItemMenuOption = function (lbl, fn) + { + return sax.getMenuOption(sax.getItemDropdownTemplate(), lbl, fn); + } + + //-------------------------------------------------------------- + sax.selectFilterOrganisation = function () + { + if (!sax.data.memberOrganisations) + sax.getMemberOrganisations(sax.showSelectOrganisation); + else sax.showSelectOrganisation(); + } + sax.getMemberOrganisations = function (fnCallback) + { + let handleMemberOrganisationsResponse = function (response) + { + //sax.debug('handleUserOrgsResponse', response); + if (!response.success) return; + sax.data.memberOrganisations = response.organisations; + if (fnCallback) fnCallback(); + } + sax.ajax.userOrgs = sax.ajax.userOrgs || { url: sax.url.base + 'Organisation/Ajax/GetMemberOrganisationList', formId: 'frmNavigate', callbackFn: handleMemberOrganisationsResponse }; + sax.doAjaxRequest(sax.ajax.userOrgs); + } + sax.showSelectOrganisation = function () + { + //sax.debug('sax.selectFilterOrganisation'); + sax.selectOrganisation.fn.selectOrganisation({ caption: sax.dlg.title.selectOrganisation, items: sax.data.memberOrganisations, fnCallback: sax.onOrganisationSelected }); + } + sax.onOrganisationSelected = function (id, name) + { + //sax.debug('sax.onOrganisationSelected', id, name); + $('#frmMemberFilter #ifFilterOrganisationId, #frmMember #ifMemberOrganisationId').val(id); + $('#frmMemberFilter #ifFilterOrganisation').val(name); + sax.refreshResults(50); + } + + //-------------------------------------------------------------- + sax.createItem = function () + { + if (!sax.data.userCanManageMembers) return; + sax.clearItem(); + $('.MemberOrganisationId', sax.$frm.member).val($('.FilterOrganisationId', sax.$frm.filter).val()); + sax.$dlg.member.dialog('option', 'title', sax.dlg.title.createMember); + sax.openDialog(sax.$dlg.member, 600, 600); + } + sax.clearItem = function () + { + sax.member = { roleCodes: [] }; + let $frm = sax.$frm.member; + $('.MemberId,.MemberExists,.MemberRoleCodes,input[type="text"],textarea,select', $frm).val(''); + $('.MemberEmailAddress', $frm).prop('readonly', false); + $('.MemberDisplayName,.MemberTitle,.MemberFirstName,.MemberLastName', $frm).hide(); + sax.clearControlErrors(); + } + sax.doCreateItem = function () + { + sax.storeItem(); + } + + //-------------------------------------------------------------- + sax.updateItem = function (id) + { + if (!sax.data.userCanManageMembers) return; + $('.MemberId', sax.$frm.memberId).val(id); + sax.ajax.updateItem = sax.ajax.updateItem || { url: sax.url.base + 'Organisation/Ajax/GetMember', formId: 'frmMemberId', callbackFn: sax.onUpdateItem }; + sax.doAjaxRequest(sax.ajax.updateItem, 50); + } + sax.onUpdateItem = function (response) + { + //sax.debug('onUpdateItem', response); + sax.clearItem(); + if (!response.success) return; + let $frm = sax.$frm.member; + let item = sax.member = response.member; + $('.MemberId', $frm).val(item.id); + $('.MemberExists', $frm).val(1); + $('.MemberOrganisationId', $frm).val(); + $('.MemberEmailAddress', $frm).val(item.emailAddress).prop('readonly', true); + $('.MemberDisplayName', $frm).toggle(!item.awaiting).val(item.displayName); + $('.MemberTitle', $frm).toggle(!!item.awaiting).val(item.title); + $('.MemberFirstName', $frm).toggle(!!item.awaiting).val(item.firstName); + $('.MemberLastName', $frm).toggle(!!item.awaiting).val(item.lastName); + $('.MemberRoleCodes', $frm).val(item.roleCodes); + $('.MemberRoles', $frm).val(sax.getSelectedMemberRoleDescs().join('\n')); + $('.MemberAdminComment', $frm).val(item.adminComment); + sax.clearControlErrors(); + sax.$dlg.member.dialog('option', 'title', sax.dlg.title.updateMember); + sax.openDialog(sax.$dlg.member, 600, 600); + } + sax.doUpdateItem = function () + { + sax.storeItem(); + } + + //-------------------------------------------------------------- + // Member Roles. + sax.selectMemberRoles = function () + { + //sax.debug('selectMemberRoles',sax.data); + sax.codeSelectCfg = sax.codeSelectCfg || { caption: sax.dlg.title.selectRoles, width: 520, fnCallback: sax.updateMemberRoles }; + sax.codeSelectCfg.codes = sax.data.memberRoles; + sax.codeSelectCfg.selectedCodes = sax.member.roleCodes || []; + sax.codeSelect.fn.openCodeSelect(sax.codeSelectCfg); + } + sax.updateMemberRoles = function (memberRoleCodes) + { + sax.debug('updateMemberRoles', memberRoleCodes); + let $frm = sax.$frm.member; + sax.member.roleCodes = memberRoleCodes; + $('.MemberRoleCodes',$frm).val(memberRoleCodes.join(sax.data.memberRoleCodeDelim)); + $('.MemberRoles', $frm).val(sax.getSelectedMemberRoleDescs().join('\n')); + } + sax.getSelectedMemberRoleDescs = function () + { + let roles = []; + sax.member.roleCodes.forEach(function (value) { roles.push(sax.getMemberRoleDesc(value)); }) + return roles.sortCI(); + } + sax.getMemberRoleDesc = function (roleCode) + { + let roleDesc = ''; + sax.data.memberRoles.some(function (value) { if (value.code == roleCode) { roleDesc = value.desc; return true; } return false; }) + return roleDesc || roleCode; + } + + //-------------------------------------------------------------- + sax.storeItem = function () + { + let $frm = sax.$frm.member; + $('.MemberNameSupplied', $frm).val($('.MemberLastName', $frm).is(":visible") ? 1 : 0); + sax.ajax.storeItem = sax.ajax.storeItem || { url: sax.url.base + 'Organisation/Ajax/StoreMember', formId: 'frmMember', callbackFn: sax.onStoreItem }; + sax.doAjaxRequest(sax.ajax.storeItem, 50); + } + sax.onStoreItem = function (response) + { + sax.debug('onStoreItem', response); + sax.refreshControlValues(response.controlValues); + if (!response.success) return; + let $frm = sax.$frm.member; + if (response.memberNameRequired) + { + $('.MemberEmailAddress', $frm).prop('readonly', true); + $('.MemberTitle,.MemberFirstName,.MemberLastName', $frm).show(); + return; + } + sax.$dlg.member.dialog('close'); + var item = response.member; + var $item = $('#' + sax.itemIdPrefix + item.id); + if (!$item.length) + { + $item = sax.getItemTemplate(); + $('.sax-panel-item-list', sax.$pnl.results).prepend($item).show(); + } + sax.populateItem($item, item) + } + + //-------------------------------------------------------------- + sax.removeItem = function (id) + { + let fn = function () + { + $('.MemberId', sax.$frm.memberId).val(id); + sax.ajax.removeItem = sax.ajax.removeItem || { url: sax.url.base + 'Organisation/Ajax/RemoveMember', formId: 'frmMemberId', callbackFn: sax.onItemRemoved }; + sax.doAjaxRequest(sax.ajax.removeItem, 50); + } + + sax.doConfirm(sax.dlg.title.remove, sax.msg.remove, fn); + } + sax.onItemRemoved = function (response) + { + if (!response.success) return; + var item = response.member; + var $item = $('#' + sax.itemIdPrefix + item.id); + //sax.debug(item, $item); + if ($item.length) + $item.remove(); + } + + //-------------------------------------------------------------- + sax.activateItem = function (id) + { + let fn = function () + { + $('.MemberId', sax.$frm.memberId).val(id); + sax.ajax.activateItem = sax.ajax.activateItem || { url: sax.url.base + 'Organisation/Ajax/ActivateMember', formId: 'frmMemberId', callbackFn: sax.onItemActivated }; + sax.doAjaxRequest(sax.ajax.activateItem, 50); + } + sax.doConfirm(sax.dlg.title.activate, sax.msg.activate, fn); + } + sax.onItemActivated = function (response) + { + if (!response.success) return; + var item = response.member; + var $item = $('#' + sax.itemIdPrefix + item.id); + //sax.debug(item, $item); + if ($item.length) + sax.populateItem($item, item) + } + + //-------------------------------------------------------------- + $(function () + { + sax.$pnl.members = $('#pnlMembers'); + sax.$pnl.filter = $('.sax-panel-filter', sax.$pnl.members); + sax.$pnl.results = $('.sax-panel-results', sax.$pnl.members); + sax.$frm.filter = $('#frmMemberFilter'); + $('.MemberFilter', sax.$frm.filter).on('input', function () { sax.refreshResults(250); }).on('keypress', function (event) + { + if (event.keyCode == 13) setTimeout(sax.refreshResults, 50); + }); + sax.$frm.memberId = $('#frmMemberId'); + sax.$frm.member = $('#frmMember'); + $('.sax-input', sax.$frm.member).on('input', function () { sax.clearControlError(this); }); + + var opt = sax.dlgOpts(); + opt.buttons[sax.dlg.btn.ok] = function () { sax.doCreateItem(); }; + opt.buttons[sax.dlg.btn.cancel] = function () { $(this).dialog('close'); }; + sax.$dlg.member = $('#dlgMember').dialog(opt); + + setTimeout(sax.refreshResults, 250); + }); + +}(window, window.document)); + +/* The End */ \ No newline at end of file diff --git a/SaxoniteCoreWebOnline/wwwroot/js/sax-org-req.js b/SaxoniteCoreWebOnline/wwwroot/js/sax-org-req.js index 3e1501c..1002d4d 100644 --- a/SaxoniteCoreWebOnline/wwwroot/js/sax-org-req.js +++ b/SaxoniteCoreWebOnline/wwwroot/js/sax-org-req.js @@ -21,7 +21,7 @@ or.fn.storeOrganisation = function () { //sax.debug('refreshResults'); - or.ajax.registerOrg = or.ajax.registerOrg || { url: sax.url.base + 'Organisation/Ajax/requestOrganisation', formId: 'frmOrganisationRequest', callbackFn: or.fn.handleRequestOrganisationResponse }; + or.ajax.registerOrg = or.ajax.registerOrg || { url: sax.url.base + 'Organisation/Ajax/Request', formId: 'frmOrganisationRequest', callbackFn: or.fn.handleRequestOrganisationResponse }; sax.doAjaxRequest(or.ajax.registerOrg, 50); } or.fn.handleRequestOrganisationResponse = function (response) diff --git a/SaxoniteStandardBase/Base/App/Sax-Version.cs b/SaxoniteStandardBase/Base/App/Sax-Version.cs index a0678f0..c1c74b6 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.1.2.0"; + private static String version { get; } = "1.1.3.0"; // Retrieve version. public static String GetFullVersion () { return version; } public static String GetAppVersion () @@ -40,7 +40,8 @@ namespace Saxonite.Net.Standard.Base.App //------------------------------------------------------------------- // Version history. //------------------------------------------------------------------- - // 1.1.3 ??-Jul-2021 Organisation Activation. + // 1.1.3 23-Jul-2021 Online Members. + // - Online Members. // - Admin organisation activation. // 1.1.2 10-Jul-2021 Organisation Request. // - Online Organisation Request. diff --git a/SaxoniteStandardBase/Base/App/SaxDbDfn_Core.cs b/SaxoniteStandardBase/Base/App/SaxDbDfn_Core.cs index b3a2b4c..e271b6d 100644 --- a/SaxoniteStandardBase/Base/App/SaxDbDfn_Core.cs +++ b/SaxoniteStandardBase/Base/App/SaxDbDfn_Core.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using System.Linq; //--------------------------------------------------------------------------- -// Code generated by Sarand Database Utilities Fri 2-Jul-2021 07:46:23 +// Code generated by Sarand Database Utilities Wed 14-Jul-2021 14:06:01 //--------------------------------------------------------------------------- namespace Saxonite.Base.App { @@ -257,6 +257,7 @@ namespace Saxonite.Base.App UserOrganisation_OrganisationId, UserOrganisation_OrganisationRoleCodes, UserOrganisation_OrganisationRoleCount, + UserOrganisation_AdminComment, UserOrganisation_Notes, UserOrganisation_Flags, UserOrganisation_Status, @@ -304,7 +305,7 @@ namespace Saxonite.Base.App //------------------------------------------------------------------- // Data members. private static readonly int tableCount = 15; - private static readonly int columnCount = 260; + private static readonly int columnCount = 261; private static ulong maxTextLen = 0; //------------------------------------------------------------------- @@ -709,6 +710,7 @@ namespace Saxonite.Base.App new ColumnDfn(12,"OrganisationId",4,4,false,10), new ColumnDfn(12,"OrganisationRoleCodes",0,12,false,256), new ColumnDfn(12,"OrganisationRoleCount",2,-6,false,3), + new ColumnDfn(12,"AdminComment",0,-9,false,2000), new ColumnDfn(12,"Notes",0,-9,false,2000), new ColumnDfn(12,"Flags",3,5,false,5), new ColumnDfn(12,"Status",0,12,false,3), diff --git a/SaxoniteStandardBase/Base/Bus/SaxUserAccount.cs b/SaxoniteStandardBase/Base/Bus/SaxUserAccount.cs index 5c6f982..d034338 100644 --- a/SaxoniteStandardBase/Base/Bus/SaxUserAccount.cs +++ b/SaxoniteStandardBase/Base/Bus/SaxUserAccount.cs @@ -422,6 +422,7 @@ namespace Saxonite.Net.Standard.Base.Bus Any, AwaitingOnly, ActiveOnly, + AwaitingOrActive, InactiveOnly, NotDeleted, DeletedOnly, diff --git a/SaxoniteStandardBase/Base/Bus/SaxUserOrganisation.cs b/SaxoniteStandardBase/Base/Bus/SaxUserOrganisation.cs index 5cadbc3..3557cb1 100644 --- a/SaxoniteStandardBase/Base/Bus/SaxUserOrganisation.cs +++ b/SaxoniteStandardBase/Base/Bus/SaxUserOrganisation.cs @@ -4,6 +4,7 @@ using Sarand.Net.Standard.Base.Utils; using Saxonite.Net.Standard.Base.App; using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Linq; using System.Text; @@ -31,12 +32,18 @@ namespace Saxonite.Net.Standard.Base.Bus User_Title, User_FirstName, User_LastName, - // Associated data. + User_Status, + User_Status_Desc, + // Organisation. Organisation_Name, Organisation_RegionId, Organisation_Region_Name, Organisation_SportId, Organisation_Sport_Name, + Organisation_Member_Count, + Organisation_Opportunity_Count, + // Properties. + AdminComment, // Associated data. // Common. Notes, @@ -54,7 +61,8 @@ namespace Saxonite.Net.Standard.Base.Bus { public SysId UserAccountId { get; set; } public SysId OrganisationId { get; set; } - public List OrganisationRoles { get; } = new List(); + public HashSet OrganisationRoles { get; } = new HashSet(); + public String AdminComment { get; set; } } //------------------------------------------------------------------- @@ -79,17 +87,64 @@ namespace Saxonite.Net.Standard.Base.Bus get { return data.OrganisationId; } set { CheckChanged(data.OrganisationId, value); data.OrganisationId = value; } } + // Organisation roles. + public byte OrganisationRoleCount + { + get { return (byte)data.OrganisationRoles.Count; } + } + public ReadOnlyCollection GetOrganisationRoles() + { + return data.OrganisationRoles.ToList().AsReadOnly(); + } + public void ClearOrganisationRoles() + { + if (!Changed) Changed = !data.OrganisationRoles.IsEmpty(); + data.OrganisationRoles.Clear(); + } + public bool HasOrganisationRole(Sax.OrganisationRole organisationRole) + { + return data.OrganisationRoles.Contains(organisationRole); + } + public void AddOrganisationRole(Sax.OrganisationRole organisationRole) + { + if (!data.OrganisationRoles.Contains(organisationRole)) + { + if (!Changed) Changed = true; + data.OrganisationRoles.Add(organisationRole); + } + } + /// Store organisation roles. public List OrganisationRoles { - get { return data.OrganisationRoles; } - set { CheckChanged(data.OrganisationRoles, value); data.OrganisationRoles.Clear(); data.OrganisationRoles.AddRange(value); } + set + { + if (Object.ReferenceEquals(data.OrganisationRoles, value)) + { + // Lists are the same object. + // Assume the list has been modified, sort it and immediately return. + // NB Shouldn't happen as HashSet isn't exposed. + Changed = true; + return; + } + CheckChanged(data.OrganisationRoles.ToList(), value); + data.OrganisationRoles.Clear(); + foreach (Sax.OrganisationRole organisationRole in value) + data.OrganisationRoles.Add(organisationRole); + } + } + // Admin comment. + public String AdminComment + { + get { return data.AdminComment; } + set { CheckChanged(data.AdminComment, value); data.AdminComment = value; } } // Population. internal void PopulateFrom(Data data) { UserAccountId = data.UserAccountId; OrganisationId = data.OrganisationId; - OrganisationRoles = data.OrganisationRoles; + OrganisationRoles = data.GetOrganisationRoles().ToList(); + AdminComment = data.AdminComment; base.PopulateFrom(data); } public Data Clone() @@ -108,6 +163,7 @@ namespace Saxonite.Net.Standard.Base.Bus public ValidValue UserAccountId { get; set; } = ValidValue.Valid; public ValidValue OrganisationId { get; set; } = ValidValue.Valid; public ValidValue OrganisationRoles { get; set; } = ValidValue.Valid; + public ValidValue AdminComment { get; set; } = ValidValue.Valid; } //------------------------------------------------------------------- @@ -127,10 +183,14 @@ namespace Saxonite.Net.Standard.Base.Bus //------------------------------------------------------------------- // Status. + /// + /// UserOrganisation is either active or deleted. + /// Other status are handled by the User Account itself. + /// As the User Organisation is simple a link between the user and Organisation and membership is defined by the presence of the 'Member' role. + /// private static readonly List validStatuses = new List() { SaStatus.Active, - SaStatus.Inactive, SaStatus.Deleted, }; public static SaStatus DefaultStatus { get { return SaStatus.Active; } } @@ -171,8 +231,9 @@ namespace Saxonite.Net.Standard.Base.Bus { List organisationRoleCodes = new List(); List roles = SaxSystem.GetCodesOfType(Sax.CodeType.OrganisationRole); - foreach (Sax.OrganisationRole organisationRole in OrganisationRoles) + foreach (Sax.OrganisationRole organisationRole in GetOrganisationRoles()) organisationRoleCodes.Add(roles.GetCodeForType(organisationRole)); + organisationRoleCodes.Sort(); return organisationRoleCodes; } public String GetOrganisationRoleCodesData() @@ -217,12 +278,41 @@ namespace Saxonite.Net.Standard.Base.Bus data.OrganisationId = value; } } - /// Prganisation Roles. + + /// Number of Organisation Roles. + public byte OrganisationRoleCount + { + get { return data.OrganisationRoleCount; } + } + /// Organisation Roles - use setter to populate. + public ReadOnlyCollection GetOrganisationRoles() + { + return data.GetOrganisationRoles(); + } + /// Store Organisation Roles. public List OrganisationRoles { - get { return data.OrganisationRoles; } set { data.OrganisationRoles = value; } } + public void ClearOrganisationRoles() + { + data.ClearOrganisationRoles(); + } + public bool HasOrganisationRole(Sax.OrganisationRole organisationRole) + { + return data.HasOrganisationRole(organisationRole); + } + public void AddOrganisationRole(Sax.OrganisationRole organisationRole) + { + data.AddOrganisationRole(organisationRole); + } + + /// Organisation admin comment. + public String AdminComment + { + get { return StringUtil.EmptyOnNull(data.AdminComment); } + set { data.AdminComment = value; } + } //------------------------------------------------------------------- // Flags - positions 1-16 available. @@ -239,7 +329,6 @@ namespace Saxonite.Net.Standard.Base.Bus { Any, ActiveOnly, - InactiveOnly, NotDeleted, DeletedOnly, } @@ -252,8 +341,9 @@ namespace Saxonite.Net.Standard.Base.Bus public List IdsIncluded { get; } = new List(); public List IdsExcluded { get; } = new List(); public List UserAccountIdsIncluded { get; } = new List(); - public SaxUserAccount.StatusCat UserAccountStatusCat { get; set; } = SaxUserAccount.StatusCat.Any; + public SaxUserAccount.StatusCat UserAccountStatusCat { get; set; } = SaxUserAccount.StatusCat.NotDeleted; public List OrganisationIdsIncluded { get; } = new List(); + public SaxOrganisation.StatusCat OrganisationStatusCat { get; set; } = SaxOrganisation.StatusCat.NotDeleted; /// Set true to restruct to User Organisations with at least one Organisation Role. public bool OrganisationMembersOnly { get; set; } public List OrganisationRoles { get; } = new List(); @@ -261,7 +351,7 @@ namespace Saxonite.Net.Standard.Base.Bus public String NotesPartial { get; set; } public StatusCat StatusCat { get; set; } = StatusCat.NotDeleted; // Table aliases - blank if not specified/linked. - public String Alias_UserOrganisation{ get; set; } + public String Alias_UserOrganisation { get; set; } public String Alias_UserAccount { get; set; } public String Alias_Organisation { get; set; } } diff --git a/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-Organisation.cs b/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-Organisation.cs index 1f7169b..2f6a295 100644 --- a/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-Organisation.cs +++ b/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-Organisation.cs @@ -24,6 +24,7 @@ namespace Saxonite.Net.Standard.Base.Mgr SaxUserOrganisation.Params prmUserOrganisation = new SaxUserOrganisation.Params(); prmUserOrganisation.OrganisationIdsIncluded.Add(organisationId); prmUserOrganisation.OrganisationMembersOnly = true; + prmUserOrganisation.UserAccountStatusCat = SaxUserAccount.StatusCat.ActiveOnly; prmUserOrganisation.StatusCat = SaxUserOrganisation.StatusCat.ActiveOnly; return GetUserOrganisationCount(prmUserOrganisation); } diff --git a/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserAccount.cs b/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserAccount.cs index 717655d..10d7a3b 100644 --- a/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserAccount.cs +++ b/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserAccount.cs @@ -104,7 +104,7 @@ namespace Saxonite.Net.Standard.Base.Mgr return false; } // Last name. - if (errField.LastName != SacsHierarchyItem.ValidValue.Ignore && !IsValidValue(data.LastName, "last name", (long)SaxDbDfn_Core.GetColTextLen(SaxDbDfn_Core.ColumnIdx.UserAccount_LastName))) + if (errField.LastName != SacsHierarchyItem.ValidValue.Ignore && !IsValidValue(data.LastName, "last name", (long)SaxDbDfn_Core.GetColTextLen(SaxDbDfn_Core.ColumnIdx.UserAccount_LastName), true)) { errField.LastName = SacsHierarchyItem.ValidValue.Invalid; return false; diff --git a/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserOrganisation.cs b/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserOrganisation.cs index 6e76307..6115754 100644 --- a/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserOrganisation.cs +++ b/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-UserOrganisation.cs @@ -53,7 +53,7 @@ namespace Saxonite.Net.Standard.Base.Mgr prmUserOrganisation.StatusCat = statusCat; using (SaxUserOrganisation userOrganisation = GetUserOrganisation(prmUserOrganisation)) if (userOrganisation != null) - organisationRoles = userOrganisation.OrganisationRoles; + organisationRoles = userOrganisation.GetOrganisationRoles().ToList(); return organisationRoles; } @@ -85,6 +85,16 @@ namespace Saxonite.Net.Standard.Base.Mgr prmUserOrganisation.StatusCat = SaxUserOrganisation.StatusCat.Any; return GetUserOrganisation(prmUserOrganisation); } + public SaxUserOrganisation GetUserOrganisationByUserOrganisation(SysId userAccountId, SysId organisationId) + { + ClearErrorState(); + if (!SysId.IsValid(userAccountId) || !SysId.IsValid(organisationId)) return null; + SaxUserOrganisation.Params prmUserOrganisation = new SaxUserOrganisation.Params(); + prmUserOrganisation.UserAccountIdsIncluded.Add(userAccountId); + prmUserOrganisation.OrganisationIdsIncluded.Add(organisationId); + prmUserOrganisation.StatusCat = SaxUserOrganisation.StatusCat.Any; + return GetUserOrganisation(prmUserOrganisation); + } //------------------------------------------------------------------- // Validate item data. @@ -112,6 +122,13 @@ namespace Saxonite.Net.Standard.Base.Mgr return SetErrorState("Organisation Id must be specified"); } + // Admin comment. + if (errField.AdminComment != SacsHierarchyItem.ValidValue.Ignore && !IsValidValue(data.AdminComment, "comment", (long)SaxDbDfn_Core.GetColTextLen(SaxDbDfn_Core.ColumnIdx.UserOrganisation_AdminComment))) + { + errField.AdminComment = SacsHierarchyItem.ValidValue.Invalid; + return false; + } + //--------------------------------------------------------------- // Common properties. if (errField.Notes != SacsHierarchyItem.ValidValue.Ignore && !IsValidValue(data.Notes, "notes", (long)SaxDbDfn_Core.GetColTextLen(SaxDbDfn_Core.ColumnIdx.UserOrganisation_Notes))) diff --git a/SaxoniteStandardBase/Resources/Email/Security/sax-email-account-activated.html b/SaxoniteStandardBase/Resources/Email/Security/sax-email-account-activated.html index f08421b..211404a 100644 --- a/SaxoniteStandardBase/Resources/Email/Security/sax-email-account-activated.html +++ b/SaxoniteStandardBase/Resources/Email/Security/sax-email-account-activated.html @@ -12,7 +12,7 @@
Account Activated
-
Congratulations - yoru account has been activated.
+
Congratulations - your account has been activated.
Follow this link to set a password against your account.
Once that's been done you'll be able to sign-in and participate on the platform.
The link will expire in [#Link.ExpiresMinutes] minutes.
diff --git a/SaxoniteStandardBase/Resources/Email/Shared/sax-email-footer.html b/SaxoniteStandardBase/Resources/Email/Shared/sax-email-footer.html index 1c5016a..37d3726 100644 --- a/SaxoniteStandardBase/Resources/Email/Shared/sax-email-footer.html +++ b/SaxoniteStandardBase/Resources/Email/Shared/sax-email-footer.html @@ -1,3 +1,3 @@ -
-
Email Footer
+ diff --git a/SaxoniteStandardBase/Resources/Email/Shared/sax-email-header.html b/SaxoniteStandardBase/Resources/Email/Shared/sax-email-header.html index 65ce379..039a91e 100644 --- a/SaxoniteStandardBase/Resources/Email/Shared/sax-email-header.html +++ b/SaxoniteStandardBase/Resources/Email/Shared/sax-email-header.html @@ -1,3 +1,3 @@ 
-
Email Header
+
Your Sport Information Platform
diff --git a/SaxoniteStandardBase/Resources/Email/Style/sax-email-main.css b/SaxoniteStandardBase/Resources/Email/Style/sax-email-main.css index 0b5b642..825ae1f 100644 --- a/SaxoniteStandardBase/Resources/Email/Style/sax-email-main.css +++ b/SaxoniteStandardBase/Resources/Email/Style/sax-email-main.css @@ -1,9 +1,14 @@ -body +html +{ + font-size: 10px; +} + +body { margin: 0; padding: 0; font-family: Verdana, Helvetica, Arial, sans-serif; - font-size: 16px; + font-size: 1.6rem; line-height: 1.5; background-color: #fff; } @@ -16,6 +21,7 @@ td .sax-email-header { margin: 0 0 12px 0; + font-size: 2.4rem; border-bottom: 1px solid rgba(0,0,0,.1); box-shadow: 0 1px 0 rgba(0,0,0,.1); -moz-box-shadow: 0 1px 0 rgba(0,0,0,.1); @@ -26,8 +32,8 @@ td { width: 90%; max-width: 1040px; - margin:0 auto; - padding:12px 24px; + margin: 0 auto; + padding: 12px 24px; } .sax-email-header .sax-content @@ -41,7 +47,7 @@ td .sax-email-panel-caption { margin: 12px 0; - font-size: 40px; + font-size: 3.2rem; font-weight: bold; color: #000; background-color: transparent; @@ -60,7 +66,7 @@ td td.sax-label { padding: 4px 12px; - font-size: 16px; + font-size: 1.6rem; text-align: right; color: #808080; white-space: nowrap; @@ -69,7 +75,7 @@ td.sax-label td.sax-value { padding-top: 4px; - font-size: 16px; + font-size: 1.6rem; } .sax-email-footer @@ -80,7 +86,7 @@ td.sax-value .sax-email-footer .sax-content { - padding:12px 24px; + padding: 12px 24px; } .sax-email-footer .sax-content .sax-footer-lower diff --git a/SaxoniteStandardDbas/Dbas/Internal/ActivityIndex_Db.cs b/SaxoniteStandardDbas/Dbas/Internal/ActivityIndex_Db.cs index 0616da3..4f43d56 100644 --- a/SaxoniteStandardDbas/Dbas/Internal/ActivityIndex_Db.cs +++ b/SaxoniteStandardDbas/Dbas/Internal/ActivityIndex_Db.cs @@ -9,7 +9,7 @@ using System.Data.Common; using System.Linq; //--------------------------------------------------------------------------- -// Code generated by Sarand Database Utilities Fri 2-Jul-2021 07:46:23 +// Code generated by Sarand Database Utilities Wed 14-Jul-2021 14:06:01 //--------------------------------------------------------------------------- namespace Saxonite.Net.Standard.Dbas.Internal { diff --git a/SaxoniteStandardDbas/Dbas/Internal/ItemAddress_Db.cs b/SaxoniteStandardDbas/Dbas/Internal/ItemAddress_Db.cs index d6db65b..2a18b46 100644 --- a/SaxoniteStandardDbas/Dbas/Internal/ItemAddress_Db.cs +++ b/SaxoniteStandardDbas/Dbas/Internal/ItemAddress_Db.cs @@ -9,7 +9,7 @@ using System.Data.Common; using System.Linq; //--------------------------------------------------------------------------- -// Code generated by Sarand Database Utilities Fri 2-Jul-2021 07:46:23 +// Code generated by Sarand Database Utilities Wed 14-Jul-2021 14:06:01 //--------------------------------------------------------------------------- namespace Saxonite.Net.Standard.Dbas.Internal { diff --git a/SaxoniteStandardDbas/Dbas/Internal/ItemImage_Db.cs b/SaxoniteStandardDbas/Dbas/Internal/ItemImage_Db.cs index a180d4e..0e9f72b 100644 --- a/SaxoniteStandardDbas/Dbas/Internal/ItemImage_Db.cs +++ b/SaxoniteStandardDbas/Dbas/Internal/ItemImage_Db.cs @@ -9,7 +9,7 @@ using System.Data.Common; using System.Linq; //--------------------------------------------------------------------------- -// Code generated by Sarand Database Utilities Fri 2-Jul-2021 07:46:23 +// Code generated by Sarand Database Utilities Wed 14-Jul-2021 14:06:01 //--------------------------------------------------------------------------- namespace Saxonite.Net.Standard.Dbas.Internal { diff --git a/SaxoniteStandardDbas/Dbas/Internal/ItemSport_Db.cs b/SaxoniteStandardDbas/Dbas/Internal/ItemSport_Db.cs index 86b86c7..227eee6 100644 --- a/SaxoniteStandardDbas/Dbas/Internal/ItemSport_Db.cs +++ b/SaxoniteStandardDbas/Dbas/Internal/ItemSport_Db.cs @@ -9,7 +9,7 @@ using System.Data.Common; using System.Linq; //--------------------------------------------------------------------------- -// Code generated by Sarand Database Utilities Fri 2-Jul-2021 07:46:23 +// Code generated by Sarand Database Utilities Wed 14-Jul-2021 14:06:01 //--------------------------------------------------------------------------- namespace Saxonite.Net.Standard.Dbas.Internal { diff --git a/SaxoniteStandardDbas/Dbas/Internal/JournalistArticle_Db.cs b/SaxoniteStandardDbas/Dbas/Internal/JournalistArticle_Db.cs index 8ea7ff7..4693303 100644 --- a/SaxoniteStandardDbas/Dbas/Internal/JournalistArticle_Db.cs +++ b/SaxoniteStandardDbas/Dbas/Internal/JournalistArticle_Db.cs @@ -9,7 +9,7 @@ using System.Data.Common; using System.Linq; //--------------------------------------------------------------------------- -// Code generated by Sarand Database Utilities Fri 2-Jul-2021 07:46:23 +// Code generated by Sarand Database Utilities Wed 14-Jul-2021 14:06:01 //--------------------------------------------------------------------------- namespace Saxonite.Net.Standard.Dbas.Internal { diff --git a/SaxoniteStandardDbas/Dbas/Internal/OrganisationOpportunity_Db.cs b/SaxoniteStandardDbas/Dbas/Internal/OrganisationOpportunity_Db.cs index 1b1b171..f00e1f1 100644 --- a/SaxoniteStandardDbas/Dbas/Internal/OrganisationOpportunity_Db.cs +++ b/SaxoniteStandardDbas/Dbas/Internal/OrganisationOpportunity_Db.cs @@ -9,7 +9,7 @@ using System.Data.Common; using System.Linq; //--------------------------------------------------------------------------- -// Code generated by Sarand Database Utilities Fri 2-Jul-2021 07:46:23 +// Code generated by Sarand Database Utilities Wed 14-Jul-2021 14:06:01 //--------------------------------------------------------------------------- namespace Saxonite.Net.Standard.Dbas.Internal { diff --git a/SaxoniteStandardDbas/Dbas/Internal/Organisation_Db.cs b/SaxoniteStandardDbas/Dbas/Internal/Organisation_Db.cs index 47e86ad..f9d63e7 100644 --- a/SaxoniteStandardDbas/Dbas/Internal/Organisation_Db.cs +++ b/SaxoniteStandardDbas/Dbas/Internal/Organisation_Db.cs @@ -9,7 +9,7 @@ using System.Data.Common; using System.Linq; //--------------------------------------------------------------------------- -// Code generated by Sarand Database Utilities Fri 2-Jul-2021 07:46:23 +// Code generated by Sarand Database Utilities Wed 14-Jul-2021 14:06:01 //--------------------------------------------------------------------------- namespace Saxonite.Net.Standard.Dbas.Internal { diff --git a/SaxoniteStandardDbas/Dbas/Internal/Region_Db.cs b/SaxoniteStandardDbas/Dbas/Internal/Region_Db.cs index 725fc0e..c6f4ddd 100644 --- a/SaxoniteStandardDbas/Dbas/Internal/Region_Db.cs +++ b/SaxoniteStandardDbas/Dbas/Internal/Region_Db.cs @@ -9,7 +9,7 @@ using System.Data.Common; using System.Linq; //--------------------------------------------------------------------------- -// Code generated by Sarand Database Utilities Fri 2-Jul-2021 07:46:23 +// Code generated by Sarand Database Utilities Wed 14-Jul-2021 14:06:01 //--------------------------------------------------------------------------- namespace Saxonite.Net.Standard.Dbas.Internal { diff --git a/SaxoniteStandardDbas/Dbas/Internal/Sport_Db.cs b/SaxoniteStandardDbas/Dbas/Internal/Sport_Db.cs index 2736a33..75678e6 100644 --- a/SaxoniteStandardDbas/Dbas/Internal/Sport_Db.cs +++ b/SaxoniteStandardDbas/Dbas/Internal/Sport_Db.cs @@ -9,7 +9,7 @@ using System.Data.Common; using System.Linq; //--------------------------------------------------------------------------- -// Code generated by Sarand Database Utilities Fri 2-Jul-2021 07:46:23 +// Code generated by Sarand Database Utilities Wed 14-Jul-2021 14:06:01 //--------------------------------------------------------------------------- namespace Saxonite.Net.Standard.Dbas.Internal { diff --git a/SaxoniteStandardDbas/Dbas/Internal/UserAccount_Db.cs b/SaxoniteStandardDbas/Dbas/Internal/UserAccount_Db.cs index 3e27bd9..46d64c8 100644 --- a/SaxoniteStandardDbas/Dbas/Internal/UserAccount_Db.cs +++ b/SaxoniteStandardDbas/Dbas/Internal/UserAccount_Db.cs @@ -9,7 +9,7 @@ using System.Data.Common; using System.Linq; //--------------------------------------------------------------------------- -// Code generated by Sarand Database Utilities Fri 2-Jul-2021 07:46:23 +// Code generated by Sarand Database Utilities Wed 14-Jul-2021 14:06:01 //--------------------------------------------------------------------------- namespace Saxonite.Net.Standard.Dbas.Internal { diff --git a/SaxoniteStandardDbas/Dbas/Internal/UserActivity_Db.cs b/SaxoniteStandardDbas/Dbas/Internal/UserActivity_Db.cs index d266aec..30bf60d 100644 --- a/SaxoniteStandardDbas/Dbas/Internal/UserActivity_Db.cs +++ b/SaxoniteStandardDbas/Dbas/Internal/UserActivity_Db.cs @@ -9,7 +9,7 @@ using System.Data.Common; using System.Linq; //--------------------------------------------------------------------------- -// Code generated by Sarand Database Utilities Fri 2-Jul-2021 07:46:23 +// Code generated by Sarand Database Utilities Wed 14-Jul-2021 14:06:01 //--------------------------------------------------------------------------- namespace Saxonite.Net.Standard.Dbas.Internal { diff --git a/SaxoniteStandardDbas/Dbas/Internal/UserOrganisation_Db.cs b/SaxoniteStandardDbas/Dbas/Internal/UserOrganisation_Db.cs index 47321ac..a37317a 100644 --- a/SaxoniteStandardDbas/Dbas/Internal/UserOrganisation_Db.cs +++ b/SaxoniteStandardDbas/Dbas/Internal/UserOrganisation_Db.cs @@ -9,7 +9,7 @@ using System.Data.Common; using System.Linq; //--------------------------------------------------------------------------- -// Code generated by Sarand Database Utilities Fri 2-Jul-2021 07:46:23 +// Code generated by Sarand Database Utilities Wed 14-Jul-2021 14:06:01 //--------------------------------------------------------------------------- namespace Saxonite.Net.Standard.Dbas.Internal { @@ -24,6 +24,7 @@ namespace Saxonite.Net.Standard.Dbas.Internal public ColumnDataInt32 _OrganisationId { get; } = new ColumnDataInt32("OrganisationId"); public ColumnDataString _OrganisationRoleCodes { get; } = new ColumnDataString("OrganisationRoleCodes"); public ColumnDataByte _OrganisationRoleCount { get; } = new ColumnDataByte("OrganisationRoleCount"); + public ColumnDataString _AdminComment { get; } = new ColumnDataString("AdminComment"); public ColumnDataString _Notes { get; } = new ColumnDataString("Notes"); public ColumnDataInt16 _Flags { get; } = new ColumnDataInt16("Flags"); public ColumnDataString _Status { get; } = new ColumnDataString("Status"); @@ -44,6 +45,7 @@ namespace Saxonite.Net.Standard.Dbas.Internal "OrganisationId", "OrganisationRoleCodes", "OrganisationRoleCount", + "AdminComment", "Notes", "Flags", "Status", @@ -90,6 +92,7 @@ namespace Saxonite.Net.Standard.Dbas.Internal public Int32 _OrganisationId { get { return Cols._OrganisationId.Value; } set { Cols._OrganisationId.Value = value; } } public String _OrganisationRoleCodes { get { return Cols._OrganisationRoleCodes.Value; } set { Cols._OrganisationRoleCodes.Value = value; } } public Byte _OrganisationRoleCount { get { return Cols._OrganisationRoleCount.Value; } set { Cols._OrganisationRoleCount.Value = value; } } + public String _AdminComment { get { return Cols._AdminComment.Value; } set { Cols._AdminComment.Value = value; } } public String _Notes { get { return Cols._Notes.Value; } set { Cols._Notes.Value = value; } } public Int16 _Flags { get { return Cols._Flags.Value; } set { Cols._Flags.Value = value; } } public String _Status { get { return Cols._Status.Value; } set { Cols._Status.Value = value; } } @@ -125,6 +128,7 @@ namespace Saxonite.Net.Standard.Dbas.Internal _OrganisationId = GetFieldInt32(dbDataReader, "OrganisationId", _OrganisationId); _OrganisationRoleCodes = GetFieldString(dbDataReader, "OrganisationRoleCodes", _OrganisationRoleCodes); _OrganisationRoleCount = GetFieldByte(dbDataReader, "OrganisationRoleCount", _OrganisationRoleCount); + _AdminComment = GetFieldString(dbDataReader, "AdminComment", _AdminComment); _Notes = GetFieldString(dbDataReader, "Notes", _Notes); _Flags = GetFieldInt16(dbDataReader, "Flags", _Flags); _Status = GetFieldString(dbDataReader, "Status", _Status); @@ -159,6 +163,7 @@ namespace Saxonite.Net.Standard.Dbas.Internal if (columnNames.ContainsCI("OrganisationId")) AddCommandParameter(dbCommand, "@OrganisationId", _OrganisationId); if (columnNames.ContainsCI("OrganisationRoleCodes")) AddCommandParameter(dbCommand, "@OrganisationRoleCodes", _OrganisationRoleCodes); if (columnNames.ContainsCI("OrganisationRoleCount")) AddCommandParameter(dbCommand, "@OrganisationRoleCount", _OrganisationRoleCount); + if (columnNames.ContainsCI("AdminComment")) AddCommandParameter(dbCommand, "@AdminComment", _AdminComment); if (columnNames.ContainsCI("Notes")) AddCommandParameter(dbCommand, "@Notes", _Notes); if (columnNames.ContainsCI("Flags")) AddCommandParameter(dbCommand, "@Flags", _Flags); if (columnNames.ContainsCI("Status")) AddCommandParameter(dbCommand, "@Status", _Status); @@ -208,6 +213,7 @@ namespace Saxonite.Net.Standard.Dbas.Internal if (columnNames.ContainsCI("OrganisationId")) AddCommandParameter(dbCommand, "@OrganisationId", _OrganisationId); if (columnNames.ContainsCI("OrganisationRoleCodes")) AddCommandParameter(dbCommand, "@OrganisationRoleCodes", _OrganisationRoleCodes); if (columnNames.ContainsCI("OrganisationRoleCount")) AddCommandParameter(dbCommand, "@OrganisationRoleCount", _OrganisationRoleCount); + if (columnNames.ContainsCI("AdminComment")) AddCommandParameter(dbCommand, "@AdminComment", _AdminComment); if (columnNames.ContainsCI("Notes")) AddCommandParameter(dbCommand, "@Notes", _Notes); if (columnNames.ContainsCI("Flags")) AddCommandParameter(dbCommand, "@Flags", _Flags); if (columnNames.ContainsCI("Status")) AddCommandParameter(dbCommand, "@Status", _Status); diff --git a/SaxoniteStandardDbas/Dbas/Internal/UserRole_Db.cs b/SaxoniteStandardDbas/Dbas/Internal/UserRole_Db.cs index 4365f5a..e8874b5 100644 --- a/SaxoniteStandardDbas/Dbas/Internal/UserRole_Db.cs +++ b/SaxoniteStandardDbas/Dbas/Internal/UserRole_Db.cs @@ -9,7 +9,7 @@ using System.Data.Common; using System.Linq; //--------------------------------------------------------------------------- -// Code generated by Sarand Database Utilities Fri 2-Jul-2021 07:46:23 +// Code generated by Sarand Database Utilities Wed 14-Jul-2021 14:06:01 //--------------------------------------------------------------------------- namespace Saxonite.Net.Standard.Dbas.Internal { diff --git a/SaxoniteStandardDbas/Dbas/Internal/UserSession_Db.cs b/SaxoniteStandardDbas/Dbas/Internal/UserSession_Db.cs index 7c5c541..7d0313f 100644 --- a/SaxoniteStandardDbas/Dbas/Internal/UserSession_Db.cs +++ b/SaxoniteStandardDbas/Dbas/Internal/UserSession_Db.cs @@ -9,7 +9,7 @@ using System.Data.Common; using System.Linq; //--------------------------------------------------------------------------- -// Code generated by Sarand Database Utilities Fri 2-Jul-2021 07:46:23 +// Code generated by Sarand Database Utilities Wed 14-Jul-2021 14:06:01 //--------------------------------------------------------------------------- namespace Saxonite.Net.Standard.Dbas.Internal { diff --git a/SaxoniteStandardDbas/Dbas/Internal/UserSport_Db.cs b/SaxoniteStandardDbas/Dbas/Internal/UserSport_Db.cs index 778d508..49d93e3 100644 --- a/SaxoniteStandardDbas/Dbas/Internal/UserSport_Db.cs +++ b/SaxoniteStandardDbas/Dbas/Internal/UserSport_Db.cs @@ -9,7 +9,7 @@ using System.Data.Common; using System.Linq; //--------------------------------------------------------------------------- -// Code generated by Sarand Database Utilities Fri 2-Jul-2021 07:46:23 +// Code generated by Sarand Database Utilities Wed 14-Jul-2021 14:06:01 //--------------------------------------------------------------------------- namespace Saxonite.Net.Standard.Dbas.Internal { diff --git a/SaxoniteStandardDbas/Dbas/Mgr/SaxAccountMgr_Dbas-UserAccount.cs b/SaxoniteStandardDbas/Dbas/Mgr/SaxAccountMgr_Dbas-UserAccount.cs index 23cf130..4251150 100644 --- a/SaxoniteStandardDbas/Dbas/Mgr/SaxAccountMgr_Dbas-UserAccount.cs +++ b/SaxoniteStandardDbas/Dbas/Mgr/SaxAccountMgr_Dbas-UserAccount.cs @@ -505,6 +505,8 @@ namespace Saxonite.Net.Standard.Dbas.Mgr return "='" + SacsSystem.GetStatusCode(SaStatus.Awaiting) + "'"; if (statusCat == SaxUserAccount.StatusCat.ActiveOnly) return "='" + SacsSystem.GetStatusCode(SaStatus.Active) + "'"; + if (statusCat == SaxUserAccount.StatusCat.AwaitingOrActive) + return " In ('" + SacsSystem.GetStatusCode(SaStatus.Awaiting) + "','" + SacsSystem.GetStatusCode(SaStatus.Active) + "')"; if (statusCat == SaxUserAccount.StatusCat.InactiveOnly) return "='" + SacsSystem.GetStatusCode(SaStatus.Inactive) + "'"; if (statusCat == SaxUserAccount.StatusCat.NotDeleted) diff --git a/SaxoniteStandardDbas/Dbas/Mgr/SaxAccountMgr_Dbas-UserOrganisation.cs b/SaxoniteStandardDbas/Dbas/Mgr/SaxAccountMgr_Dbas-UserOrganisation.cs index 3c61a68..f583d92 100644 --- a/SaxoniteStandardDbas/Dbas/Mgr/SaxAccountMgr_Dbas-UserOrganisation.cs +++ b/SaxoniteStandardDbas/Dbas/Mgr/SaxAccountMgr_Dbas-UserOrganisation.cs @@ -31,7 +31,7 @@ namespace Saxonite.Net.Standard.Dbas.Mgr new StringPair("Id",SysId.ToString(userOrganisation.Id)), new StringPair("UserAccountId",SysId.ToString(userOrganisation.UserAccountId)), new StringPair("OrganisationId",SysId.ToString(userOrganisation.OrganisationId)), - new StringPair("Roles",userOrganisation.OrganisationRoles.Select(item => item.ToString()).Concat(", ")), + new StringPair("Roles",userOrganisation.GetOrganisationRoles().ToList().Select(item => item.ToString()).Concat(", ")), new StringPair("Status",userOrganisation.StatusCode) }; } @@ -216,7 +216,8 @@ namespace Saxonite.Net.Standard.Dbas.Mgr dbUserOrganisation._UserAccountId = SysId.ToInt(userOrganisation.UserAccountId); dbUserOrganisation._OrganisationId = SysId.ToInt(userOrganisation.OrganisationId); dbUserOrganisation._OrganisationRoleCodes = userOrganisation.GetOrganisationRoleCodesData(); - dbUserOrganisation._OrganisationRoleCount = (byte)userOrganisation.OrganisationRoles.Count; + dbUserOrganisation._OrganisationRoleCount = (byte)userOrganisation.OrganisationRoleCount; + dbUserOrganisation._AdminComment = userOrganisation.AdminComment; // Common. dbUserOrganisation._Notes = userOrganisation.Notes; dbUserOrganisation._Status = SacsSystem.GetStatusCode(userOrganisation.Status); @@ -237,6 +238,7 @@ namespace Saxonite.Net.Standard.Dbas.Mgr //userOrganisation.UserAccountId = new SysId(dbUserOrganisation._UserAccountId); //userOrganisation.OrganisationId = new SysId(dbUserOrganisation._OrganisationId); userOrganisation.OrganisationRoles = SaxUserOrganisation.GetOrganisationRolesFromCodes(dbUserOrganisation._OrganisationRoleCodes); + userOrganisation.AdminComment = dbUserOrganisation._AdminComment; // Common. userOrganisation.Notes = dbUserOrganisation._Notes; userOrganisation.Status = SacsSystem.GetStatusForCode(dbUserOrganisation._Status); @@ -256,6 +258,8 @@ namespace Saxonite.Net.Standard.Dbas.Mgr private static readonly String prefixUserOrganisation_Organisation_Region = "UsrOrg_Org_Rgn_"; private static readonly String aliasUserOrganisation_Organisation_Sport = "UsrOrg_Org_Spt"; private static readonly String prefixUserOrganisation_Organisation_Sport = "UsrOrg_Org_Spt_"; + private static readonly String dataName_UserOrganisation_Organisation_Member_Count = "UsrOrg_Org_Mbr_Count"; + private static readonly String dataName_UserOrganisation_Organisation_Opportunity_Count = "UsrOrg_Org_Opp_Count"; private void SetUserOrganisationColDfn (ColumnDfn col, String caption, int width, SaxDbDfn_Core.ColumnIdx dbCol, ColumnAlignment align = ColumnAlignment.Default, DataValue.DataType dataType = DataValue.DataType.String, @@ -329,6 +333,14 @@ namespace Saxonite.Net.Standard.Dbas.Mgr SetUserOrganisationColDfn(columnDfn, "Last name", 120, SaxDbDfn_Core.ColumnIdx.UserAccount_LastName, ColumnAlignment.Left, DataValue.DataType.String, aliasUserOrganisation_UserAccount, prefixUserOrganisation_UserAccount); columnDfn.ReadOnly = true; break; + case SaxUserOrganisation.Column.User_Status: + SetUserOrganisationColDfn(columnDfn, "Status", 80, SaxDbDfn_Core.ColumnIdx.UserAccount_Status, ColumnAlignment.Center, DataValue.DataType.String, aliasUserOrganisation_UserAccount, prefixUserOrganisation_UserAccount); + columnDfn.ReadOnly = true; + break; + case SaxUserOrganisation.Column.User_Status_Desc: + SetUserOrganisationColDfn(columnDfn, "Status", 90, SaxDbDfn_Core.ColumnIdx.UserAccount_Status, ColumnAlignment.Center, DataValue.DataType.String, aliasUserOrganisation_UserAccount, prefixUserOrganisation_UserAccount); + columnDfn.ReadOnly = true; + break; //------------------------------------------------------- // Organisation. @@ -353,6 +365,24 @@ namespace Saxonite.Net.Standard.Dbas.Mgr columnDfn.ReadOnly = true; break; + // Derived properties. + case SaxUserOrganisation.Column.Organisation_Member_Count: + SetUserOrganisationColDfn(columnDfn, "Members", 76, SaxDbDfn_Core.ColumnIdx.Unknown, ColumnAlignment.Center, DataValue.DataType.Long); + columnDfn.DataName = dataName_UserOrganisation_Organisation_Member_Count; + columnDfn.ReadOnly = true; + break; + case SaxUserOrganisation.Column.Organisation_Opportunity_Count: + SetUserOrganisationColDfn(columnDfn, "Opportunities", 76, SaxDbDfn_Core.ColumnIdx.Unknown, ColumnAlignment.Center, DataValue.DataType.Long); + columnDfn.DataName = dataName_UserOrganisation_Organisation_Opportunity_Count; + columnDfn.ReadOnly = true; + break; + + //----------------------------------------------------------- + // Properties. + case SaxUserOrganisation.Column.AdminComment: + SetUserOrganisationColDfn(columnDfn, "Comment", 240, SaxDbDfn_Core.ColumnIdx.UserOrganisation_AdminComment); + break; + //----------------------------------------------------------- // Derived properties. @@ -436,6 +466,25 @@ namespace Saxonite.Net.Standard.Dbas.Mgr if (!organisationIdsIncluded.IsEmpty()) filter.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.UserOrganisation_OrganisationId, aliasUserOrganisation) + " IN (" + organisationIdsIncluded + ")"); + // Organisation status. + if (prm.OrganisationStatusCat != SaxOrganisation.StatusCat.Any) + { + String aliasUserOrganisation_Organisation = StringUtil.OnEmpty(prm.Alias_Organisation, SaxAccountMgr_Dbas.aliasUserOrganisation_Organisation); + if (StringUtil.IsEmpty(prm.Alias_Organisation)) + { + // User Account table isn't linked. + DbQuery qry = new DbQuery(CoreDatabase.DbmsType); + qry.Tables.Add(SaxDbDfn_Core.GetTableName(SaxDbDfn_Core.TableIdx.Organisation), aliasUserOrganisation_Organisation); + qry.Columns.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.Organisation_Id, aliasUserOrganisation_Organisation)); + qry.Filters.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.Organisation_Id, aliasUserOrganisation_Organisation) + "=" + SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.UserOrganisation_OrganisationId, aliasUserOrganisation)); + qry.Filters.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.Organisation_Status, aliasUserOrganisation_Organisation) + GetOrganisationStatusCatFilterCondition(prm.OrganisationStatusCat)); + filter.Add("Exists (" + qry.BuildQuery() + ")"); + } + else + // User Account table is linked - filter directly. + filter.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.Organisation_Status, aliasUserOrganisation_Organisation) + GetOrganisationStatusCatFilterCondition(prm.OrganisationStatusCat)); + } + // Organisation roles. if (prm.OrganisationMembersOnly) filter.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.UserOrganisation_OrganisationRoleCount, aliasUserOrganisation) + ">0"); @@ -465,8 +514,6 @@ namespace Saxonite.Net.Standard.Dbas.Mgr { if (statusCat == SaxUserOrganisation.StatusCat.ActiveOnly) return "='" + SacsSystem.GetStatusCode(SaStatus.Active) + "'"; - if (statusCat == SaxUserOrganisation.StatusCat.InactiveOnly) - return "='" + SacsSystem.GetStatusCode(SaStatus.Inactive) + "'"; if (statusCat == SaxUserOrganisation.StatusCat.NotDeleted) return "!='" + SacsSystem.GetStatusCode(SaStatus.Deleted) + "'"; if (statusCat == SaxUserOrganisation.StatusCat.DeletedOnly) @@ -572,7 +619,7 @@ namespace Saxonite.Net.Standard.Dbas.Mgr // Add columns as defined in query list. if (columns.HasId(SaxUserOrganisation.Column.Id)) query.Columns.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.UserOrganisation_Id, aliasUserOrganisation)); - bool hasUserAccountCols = (columns.HasId(SaxUserOrganisation.Column.User_EmailAddress) || columns.HasId(SaxUserOrganisation.Column.User_DisplayName) || columns.HasId(SaxUserOrganisation.Column.User_Title) || columns.HasId(SaxUserOrganisation.Column.User_FirstName) || columns.HasId(SaxUserOrganisation.Column.User_LastName)); + bool hasUserAccountCols = (columns.HasId(SaxUserOrganisation.Column.User_EmailAddress) || columns.HasId(SaxUserOrganisation.Column.User_DisplayName) || columns.HasId(SaxUserOrganisation.Column.User_Title) || columns.HasId(SaxUserOrganisation.Column.User_FirstName) || columns.HasId(SaxUserOrganisation.Column.User_LastName) || columns.HasId(SaxUserOrganisation.Column.User_Status) || columns.HasId(SaxUserOrganisation.Column.User_Status_Desc)); if (columns.HasId(SaxUserOrganisation.Column.UserAccountId) || hasUserAccountCols) query.Columns.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.UserOrganisation_UserAccountId, aliasUserOrganisation)); bool hasOrganisationCols = (columns.HasId(SaxUserOrganisation.Column.Organisation_Name) || columns.HasId(SaxUserOrganisation.Column.Organisation_RegionId) || columns.HasId(SaxUserOrganisation.Column.Organisation_SportId)); @@ -584,6 +631,8 @@ namespace Saxonite.Net.Standard.Dbas.Mgr query.Columns.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.UserOrganisation_OrganisationRoleCodes, aliasUserOrganisation)); if (columns.HasId(SaxUserOrganisation.Column.OrganisationRoleCount)) query.Columns.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.UserOrganisation_OrganisationRoleCount, aliasUserOrganisation)); + if (columns.HasId(SaxUserOrganisation.Column.AdminComment)) + query.Columns.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.UserOrganisation_AdminComment, aliasUserOrganisation)); // Common properties. if (columns.HasId(SaxUserOrganisation.Column.Notes)) query.Columns.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.UserOrganisation_Notes, aliasUserOrganisation)); @@ -608,10 +657,16 @@ namespace Saxonite.Net.Standard.Dbas.Mgr joinUserAccount.Filters.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.UserAccount_Id, aliasUserOrganisation_UserAccount) + "=" + SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.UserOrganisation_UserAccountId, aliasUserOrganisation)); query.Joins.Add(joinUserAccount); List cols = new List(); - cols.Add(SaxDbDfn_Core.ColumnIdx.UserAccount_EmailAddress); - cols.Add(SaxDbDfn_Core.ColumnIdx.UserAccount_Title); - cols.Add(SaxDbDfn_Core.ColumnIdx.UserAccount_FirstName); - cols.Add(SaxDbDfn_Core.ColumnIdx.UserAccount_LastName); + if (columns.HasId(SaxUserOrganisation.Column.User_EmailAddress)) + cols.Add(SaxDbDfn_Core.ColumnIdx.UserAccount_EmailAddress); + if (columns.HasId(SaxUserOrganisation.Column.User_Title) || columns.HasId(SaxUserOrganisation.Column.User_DisplayName)) + cols.Add(SaxDbDfn_Core.ColumnIdx.UserAccount_Title); + if (columns.HasId(SaxUserOrganisation.Column.User_FirstName) || columns.HasId(SaxUserOrganisation.Column.User_DisplayName)) + cols.Add(SaxDbDfn_Core.ColumnIdx.UserAccount_FirstName); + if (columns.HasId(SaxUserOrganisation.Column.User_LastName) || columns.HasId(SaxUserOrganisation.Column.User_DisplayName)) + cols.Add(SaxDbDfn_Core.ColumnIdx.UserAccount_LastName); + if (columns.HasId(SaxUserOrganisation.Column.User_Status) || columns.HasId(SaxUserOrganisation.Column.User_Status_Desc)) + cols.Add(SaxDbDfn_Core.ColumnIdx.UserAccount_Status); foreach (var col in cols) query.Columns.Add(SaxDbDfn_Core.GetColumnNameQual(col, aliasUserOrganisation_UserAccount), prefixUserOrganisation_UserAccount + SaxDbDfn_Core.GetColumnName(col, false)); } @@ -659,6 +714,34 @@ namespace Saxonite.Net.Standard.Dbas.Mgr } } + //--------------------------------------------------------------------- + // Organisation Member count. + if (columns.HasId(SaxUserOrganisation.Column.Organisation_Member_Count)) + { + // Count 'Member' entries on User Organisation for the Organisation. + String aliasOrgMbr = "OrgMbr"; + DbQuery qryOrgMbr = new DbQuery(CoreDatabase.DbmsType); + qryOrgMbr.Tables.Add(SaxDbDfn_Core.GetTableName(SaxDbDfn_Core.TableIdx.UserOrganisation), aliasOrgMbr); + qryOrgMbr.Columns.Add("Count(*)"); + qryOrgMbr.Filters.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.UserOrganisation_OrganisationId, aliasOrgMbr) + "=" + SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.UserOrganisation_OrganisationId, aliasUserOrganisation)); + qryOrgMbr.Filters.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.UserOrganisation_OrganisationRoleCount, aliasOrgMbr) + ">0"); + query.Columns.Add("(" + qryOrgMbr.BuildQuery() + ")", dataName_UserOrganisation_Organisation_Member_Count); + } + + //--------------------------------------------------------------------- + // Organisation Opportunity count. + if (columns.HasId(SaxUserOrganisation.Column.Organisation_Opportunity_Count)) + { + // Count Organisation Opportunities. + String aliasOrgOpp = "OrgOpp"; + DbQuery qryOrgOpp = new DbQuery(CoreDatabase.DbmsType); + qryOrgOpp.Tables.Add(SaxDbDfn_Core.GetTableName(SaxDbDfn_Core.TableIdx.OrganisationOpportunity), aliasOrgOpp); + qryOrgOpp.Columns.Add("Count(*)"); + qryOrgOpp.Filters.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.OrganisationOpportunity_OrganisationId, aliasOrgOpp) + "=" + SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.UserOrganisation_OrganisationId, aliasUserOrganisation)); + // ToDo Should be limited by expiry etc. + query.Columns.Add("(" + qryOrgOpp.BuildQuery() + ")", dataName_UserOrganisation_Organisation_Opportunity_Count); + } + //--------------------------------------------------------------------- // Populate filter. query.Filters.AddRange(GetUserOrganisationQueryFilter(prm)); diff --git a/SaxoniteStandardDbas/Resources/Database/SaxDbDfn_Core.xml b/SaxoniteStandardDbas/Resources/Database/SaxDbDfn_Core.xml index ba40bf4..ded9577 100644 --- a/SaxoniteStandardDbas/Resources/Database/SaxDbDfn_Core.xml +++ b/SaxoniteStandardDbas/Resources/Database/SaxDbDfn_Core.xml @@ -256,7 +256,7 @@ false false 1 - 6 + 7 false @@ -903,8 +903,8 @@ false false false - 0 - 0 + 1 + 2 false @@ -2432,7 +2432,7 @@ false false 1 - 4 + 7 false @@ -2849,7 +2849,7 @@ false false 1 - 3 + 4 false @@ -3210,7 +3210,7 @@ false false 5 - 269 + 270 false @@ -3486,7 +3486,7 @@ false false 1 - 5 + 14 false @@ -4011,7 +4011,7 @@ false false 1 - 4 + 7 false @@ -4474,7 +4474,7 @@ false false 1 - 9 + 10 false @@ -4680,7 +4680,7 @@ dbo UserOrganisation
6 - Notes + AdminComment VarChar -9 @@ -4697,6 +4697,23 @@ dbo UserOrganisation
7 + Notes + VarChar + + -9 + nvarchar + false + 2000 + 4000 + 0 + NoNulls + false + + + + dbo + UserOrganisation
+ 8 Flags SmallInt @@ -4713,7 +4730,7 @@ dbo UserOrganisation
- 8 + 9 Status VarChar @@ -4730,7 +4747,7 @@ dbo UserOrganisation
- 9 + 10 CreateOn DateTime @@ -4747,7 +4764,7 @@ dbo UserOrganisation
- 10 + 11 CreateBy Int @@ -4764,7 +4781,7 @@ dbo UserOrganisation
- 11 + 12 CreateHost VarChar @@ -4781,7 +4798,7 @@ dbo UserOrganisation
- 12 + 13 UpdateOn DateTime @@ -4798,7 +4815,7 @@ dbo UserOrganisation
- 13 + 14 UpdateBy Int @@ -4815,7 +4832,7 @@ dbo UserOrganisation
- 14 + 15 UpdateHost VarChar @@ -4841,7 +4858,7 @@ false false 1 - 6 + 8 false @@ -5624,7 +5641,7 @@ false false 1 - 4 + 5 false