diff --git a/SaBusMgrCoreWeb/Web/App/SbmAppConfig.cs b/SaBusMgrCoreWeb/Web/App/SbmAppConfig.cs index c138b07..9419190 100644 --- a/SaBusMgrCoreWeb/Web/App/SbmAppConfig.cs +++ b/SaBusMgrCoreWeb/Web/App/SbmAppConfig.cs @@ -99,7 +99,6 @@ namespace Sarand.BusinessMgr.Net.Core.Web.App } public DatabaseConfig ControlDatabase { get; private set; } public DatabaseConfig TemplateDatabase { get; private set; } - public List BusMgrDatabases { get; } = new List(); // Documents. public class DocumentsConfig { @@ -211,18 +210,6 @@ namespace Sarand.BusinessMgr.Net.Core.Web.App ExtractDatabaseConfig(TemplateDatabase, templateDatabaseSection); } - // BusinessMgr databases. - IConfigurationSection busMgrDatabasesSection = configSection.GetSection("BusMgrDatabases"); - if (busMgrDatabasesSection.Exists()) - { - foreach (IConfigurationSection busMgrDbSection in busMgrDatabasesSection.GetChildren()) - { - SbmAppConfig.DatabaseConfig dbConfig = new SbmAppConfig.DatabaseConfig(); - ExtractDatabaseConfig(dbConfig, busMgrDbSection); - BusMgrDatabases.Add(dbConfig); - } - } - // Variants. foreach (ConfigurationSection variantSection in configSection.GetSection("Variants").GetChildren()) { diff --git a/SaBusMgrCoreWeb/Web/App/SbmApplication_Web.cs b/SaBusMgrCoreWeb/Web/App/SbmApplication_Web.cs index e29cadf..e07c56d 100644 --- a/SaBusMgrCoreWeb/Web/App/SbmApplication_Web.cs +++ b/SaBusMgrCoreWeb/Web/App/SbmApplication_Web.cs @@ -71,8 +71,6 @@ namespace Sarand.BusinessMgr.Net.Core.Web.App applicationParams.Add("Configuration", SbmSystem.DebugMode ? "Debug" : "Release"); applicationParams.Add("Environment", EnvironmentName); applicationParams.Add("Control database", AppConfig.ControlDatabase.Location); - foreach (SbmAppConfig.DatabaseConfig dbConfig in AppConfig.BusMgrDatabases) - applicationParams.Add("BusMgr database", (dbConfig.Identifiers.Any() ? $"[{String.Join(",", dbConfig.Identifiers)}] " : "") + dbConfig.Location); applicationParams.Add("Application root", SbmSystem.ApplicationFolder); if (!StringUtil.IsEmpty(AppConfig.Documents.RootFolder)) applicationParams.Add("Documents root", AppConfig.Documents.RootFolder); diff --git a/SaBusMgrCoreWeb/Web/Controllers/SbmController_Web.cs b/SaBusMgrCoreWeb/Web/Controllers/SbmController_Web.cs index 519f0d5..6e1e57a 100644 --- a/SaBusMgrCoreWeb/Web/Controllers/SbmController_Web.cs +++ b/SaBusMgrCoreWeb/Web/Controllers/SbmController_Web.cs @@ -117,8 +117,7 @@ namespace Sarand.BusinessMgr.Net.Core.Web.Controllers //------------------------------------------------------------------- // Model. - //------------------------------------------------------------------- - protected void RefreshModel(SbmModel_Base model) + protected virtual void RefreshModel(SbmModel_Base model) { if (model == null) return; diff --git a/SaBusMgrStandardBase/Base/Bus/SbmAffiliate.cs b/SaBusMgrStandardBase/Base/Bus/SbmAffiliate.cs index 46ff3a5..9e0103a 100644 --- a/SaBusMgrStandardBase/Base/Bus/SbmAffiliate.cs +++ b/SaBusMgrStandardBase/Base/Bus/SbmAffiliate.cs @@ -53,7 +53,8 @@ namespace Sarand.BusinessMgr.Net.Standard.Base.Bus ProspectGroup, // Associated data. GroupMembers, - Association_Count, + AssociationOwner_Count, + AssociationTarget_Count, // Common properties. Notes, Flag, diff --git a/SaBusMgrStandardDbas/Dbas/Mgr/SbmAccountMgr_Dbas-Affiliate.cs b/SaBusMgrStandardDbas/Dbas/Mgr/SbmAccountMgr_Dbas-Affiliate.cs index 2e9e14b..81b3ec8 100644 --- a/SaBusMgrStandardDbas/Dbas/Mgr/SbmAccountMgr_Dbas-Affiliate.cs +++ b/SaBusMgrStandardDbas/Dbas/Mgr/SbmAccountMgr_Dbas-Affiliate.cs @@ -366,7 +366,8 @@ namespace Sarand.BusinessMgr.Net.Standard.Dbas.Mgr private static readonly String aliasAffiliate_AddPrim = "Aff_AddPrim"; private static readonly String prefixAffiliate_AddPrim = aliasAffiliate_AddPrim + "_"; private static readonly String colNameAffiliate_GroupMembers = "GroupMembers"; - private static readonly String colNameAffiliate_AssociationCount = "AssocCount"; + private static readonly String colNameAffiliate_AssociationOwnerCount = "AssocOwnerCount"; + private static readonly String colNameAffiliate_AssociationTargetCount = "AssocTargetCount"; // Column definition population. private void SetAffiliateColDfn @@ -499,9 +500,13 @@ namespace Sarand.BusinessMgr.Net.Standard.Dbas.Mgr SetAffiliateColDfn(columnDfn, "Members", 90, SbmDbDfn.ColumnIdx.Unknown, ColumnAlignment.Right); columnDfn.DataName = colNameAffiliate_GroupMembers; break; - case SbmAffiliate.Column.Association_Count: - SetAffiliateColDfn(columnDfn, "Associations", 90, SbmDbDfn.ColumnIdx.Unknown, ColumnAlignment.Right); - columnDfn.DataName = colNameAffiliate_AssociationCount; + case SbmAffiliate.Column.AssociationOwner_Count: + SetAffiliateColDfn(columnDfn, "Owner Assocs", 90, SbmDbDfn.ColumnIdx.Unknown, ColumnAlignment.Right); + columnDfn.DataName = colNameAffiliate_AssociationOwnerCount; + break; + case SbmAffiliate.Column.AssociationTarget_Count: + SetAffiliateColDfn(columnDfn, "Target Assocs", 90, SbmDbDfn.ColumnIdx.Unknown, ColumnAlignment.Right); + columnDfn.DataName = colNameAffiliate_AssociationTargetCount; break; //----------------------------------------------------------------- @@ -1042,16 +1047,32 @@ namespace Sarand.BusinessMgr.Net.Standard.Dbas.Mgr } //--------------------------------------------------------------- - if (columns.HasId(SbmAffiliate.Column.Association_Count)) + // Associations. + + if (columns.HasId(SbmAffiliate.Column.AssociationOwner_Count)) { // Count active associations. - String aliasAffiliate_Associations = "AffAssocs"; + String aliasAffiliate_Associations_Owner = "AffAssOwner"; DbQuery qryAssocs = new DbQuery(db.DbmsType); - qryAssocs.Tables.Add((SbmDbDfn.TableIdx.Association).ToString(), aliasAffiliate_Associations); + qryAssocs.Tables.Add((SbmDbDfn.TableIdx.Association).ToString(), aliasAffiliate_Associations_Owner); qryAssocs.Columns.Add("Count(*)"); - qryAssocs.Filters.Add($"{SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Association_AffIdOwner, aliasAffiliate_Associations)}={SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Affiliate_Id, aliasAffiliate)}"); - qryAssocs.Filters.Add($"{SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Association_Status, aliasAffiliate_Associations)}='{StatusCurrent}'"); - query.Columns.Add("(" + qryAssocs.BuildQuery() + ")", colNameAffiliate_AssociationCount); + qryAssocs.Filters.Add($"{SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Association_AffIdOwner, aliasAffiliate_Associations_Owner)}={SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Affiliate_Id, aliasAffiliate)}"); + qryAssocs.Filters.Add($"{SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Association_Type, aliasAffiliate_Associations_Owner)}!={(int)Sbm.AssociationType.Internal}"); + qryAssocs.Filters.Add($"{SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Association_Status, aliasAffiliate_Associations_Owner)}='{StatusCurrent}'"); + query.Columns.Add("(" + qryAssocs.BuildQuery() + ")", colNameAffiliate_AssociationOwnerCount); + } + + if (columns.HasId(SbmAffiliate.Column.AssociationTarget_Count)) + { + // Count active associations. + String aliasAffiliate_Associations_Target = "AffAssTarget"; + DbQuery qryAssocs = new DbQuery(db.DbmsType); + qryAssocs.Tables.Add((SbmDbDfn.TableIdx.Association).ToString(), aliasAffiliate_Associations_Target); + qryAssocs.Columns.Add("Count(*)"); + qryAssocs.Filters.Add($"{SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Association_AffIdTarget, aliasAffiliate_Associations_Target)}={SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Affiliate_Id, aliasAffiliate)}"); + qryAssocs.Filters.Add($"{SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Association_Type, aliasAffiliate_Associations_Target)}!={(int)Sbm.AssociationType.Internal}"); + qryAssocs.Filters.Add($"{SbmDbDfn.GetColumnNameQual(SbmDbDfn.ColumnIdx.Association_Status, aliasAffiliate_Associations_Target)}='{StatusCurrent}'"); + query.Columns.Add("(" + qryAssocs.BuildQuery() + ")", colNameAffiliate_AssociationTargetCount); } //--------------------------------------------------------------- diff --git a/SaBusMgrTest_Portal/Web/Portal/App/SbmApplication_Database_Tester.cs b/SaBusMgrTest_Portal/Web/Portal/App/SbmApplication_Database_Tester.cs index c3fa010..09d3fad 100644 --- a/SaBusMgrTest_Portal/Web/Portal/App/SbmApplication_Database_Tester.cs +++ b/SaBusMgrTest_Portal/Web/Portal/App/SbmApplication_Database_Tester.cs @@ -1,7 +1,9 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; +using Sarand.BusinessMgr.Net.Core.Web.Consoles; using Sarand.Net.Standard.Base.App; using Sarand.Net.Standard.Base.Dbas; using Sarand.Net.Standard.Base.Helpers; +using Sarand.Net.Standard.Base.Security; using System.Collections.Generic; using System.IO; using System.Text.RegularExpressions; @@ -19,12 +21,16 @@ namespace Sarand.BusinessMgr.Test.Web.Portal.App [TestMethod] public void Test_PasswordEncoding() { - Assert.IsNotNull(BusMgrDatabase); - StringAssert.Matches(BusMgrDatabase.EncodePassword_Hex(""), reHex); + using (SbmSecurityConsole_Web securityConsole = new SbmSecurityConsole_Web(UserContext)) + { - // Iterate using random filename (8.3). - for (int i = 0; i < 1000; i++) - StringAssert.Matches(BusMgrDatabase.EncodePassword_Hex(Path.GetRandomFileName()), reHex); + Assert.IsNotNull(BusMgrDatabase); + StringAssert.Matches(securityConsole.EncodePassword_Hex(""), reHex); + + // Iterate using random filename (8.3). + for (int i = 0; i < 1000; i++) + StringAssert.Matches(securityConsole.EncodePassword_Hex(Path.GetRandomFileName()), reHex); + } } //------------------------------------------------------------------- @@ -34,19 +40,23 @@ namespace Sarand.BusinessMgr.Test.Web.Portal.App Assert.IsNotNull(BusMgrDatabase); Dictionary passwordHashes = new Dictionary(); - // Iterate using random filename (8.3). - for (int i = 0; i < 200; i++) + using (SbmSecurityConsole_Web securityConsole = new SbmSecurityConsole_Web(UserContext)) { - string password = Path.GetRandomFileName(); - string passwordHash = BusMgrDatabase.EncodePassword_Hex(password); - StringAssert.Matches(passwordHash, reHex); - passwordHashes.Add(password, passwordHash); - } - foreach (string password in passwordHashes.Keys) - { - string passwordHash = passwordHashes.GetValueOrDefault(password); - StringAssert.Matches(passwordHash, reHex); - Assert.IsTrue(BusMgrDatabase.ComparePassword_Hex(password, passwordHash)); + + // Iterate using random filename (8.3). + for (int i = 0; i < 200; i++) + { + string password = Path.GetRandomFileName(); + string passwordHash = securityConsole.EncodePassword_Hex(password); + StringAssert.Matches(passwordHash, reHex); + passwordHashes.Add(password, passwordHash); + } + foreach (string password in passwordHashes.Keys) + { + string passwordHash = passwordHashes.GetValueOrDefault(password); + StringAssert.Matches(passwordHash, reHex); + Assert.IsTrue(securityConsole.ComparePassword_Hex(password, passwordHash)); + } } } diff --git a/SaBusinessMgrCoreWebPortal/Portal/App/SbmApplication_Portal.cs b/SaBusinessMgrCoreWebPortal/Portal/App/SbmApplication_Portal.cs index ab14459..7af67c4 100644 --- a/SaBusinessMgrCoreWebPortal/Portal/App/SbmApplication_Portal.cs +++ b/SaBusinessMgrCoreWebPortal/Portal/App/SbmApplication_Portal.cs @@ -55,23 +55,6 @@ namespace Sarand.BusinessMgr.Web.Portal.App public SysId ControlOwnerId { get; protected set; } public SysId ControlRepositoryId { get; protected set; } } - private class SessionContextData_Core : SessionContextData - { - // Instance configuration for core database instance without Control configuration. - public SessionContextData_Core(SbmSessionContext_Web sessionContext) : base(sessionContext) - { - } - private SbmInstanceConfig instanceConfig = null; - public override SbmInstanceConfig InstanceConfig - { - get - { - if (instanceConfig == null) - instanceConfig = new SbmInstanceConfig_Core(SessionContext); - return instanceConfig; - } - } - } private class SessionContextData_Control : SessionContextData { public SessionContextData_Control(SbmSessionContext_Web sessionContext, SysId controlOwnerId, SysId controlRepositoryId) : base(sessionContext) @@ -233,26 +216,6 @@ namespace Sarand.BusinessMgr.Web.Portal.App controlDatabase = SacsDatabaseConsole.OpenOdbcDatabase(odbcConfig); if (controlDatabase.HasErrorState()) SaLogger.Warn($"{SysUtil.Fn} Control Database initialisation error: " + controlDatabase.ErrorStateMessage); - - // Configure BusMgr database connections. - foreach (Net.Core.Web.App.SbmAppConfig.DatabaseConfig dbConfig in AppConfig.BusMgrDatabases) - { - SacsDatabaseConsole.OdbcConfig config = new SacsDatabaseConsole.OdbcConfig(); - config.Location = dbConfig.Location; - config.User = dbConfig.User; - config.Password = dbConfig.Password; - SacsDatabase coreDatabase = SacsDatabaseConsole.OpenOdbcDatabase(config); - if (!coreDatabase.HasErrorState()) - { - // Session context. - ushort sessionContextIdx = sessionContextIdx_Next++; - SessionContextData sessionContextData = new SessionContextData_Core(new SbmSessionContext_Web(sessionContextIdx, Sbm.Application.WebPortal)); - sessionContextData.BusMgrDatabase = coreDatabase; - sessionContextData.RepositoryContexts.UnionWith(dbConfig.Identifiers); - sessionContexts.Add(sessionContextData.SessionContext.Index, sessionContextData); - } - else SaLogger.Warn($"{SysUtil.Fn} Database: " + coreDatabase.DatabaseName + ". Initialisation error: " + coreDatabase.ErrorStateMessage); - } } catch (Exception exc) { diff --git a/SaBusinessMgrCoreWebPortal/Portal/Consoles/SbmReferenceDataConsole_Portal.cs b/SaBusinessMgrCoreWebPortal/Portal/Consoles/SbmReferenceDataConsole_Portal.cs index cf15243..0e9f929 100644 --- a/SaBusinessMgrCoreWebPortal/Portal/Consoles/SbmReferenceDataConsole_Portal.cs +++ b/SaBusinessMgrCoreWebPortal/Portal/Consoles/SbmReferenceDataConsole_Portal.cs @@ -10,7 +10,7 @@ namespace Sarand.BusinessMgr.Web.Portal.Consoles { //------------------------------------------------------------------- // Data members. - private SaUpdateLogConsole updateLogConsole; + //private SaUpdateLogConsole updateLogConsole; //------------------------------------------------------------------- // Construction. diff --git a/SaBusinessMgrCoreWebPortal/Portal/Controllers/Commodities/CommodityController.cs b/SaBusinessMgrCoreWebPortal/Portal/Controllers/Commodities/CommodityController.cs index 089365d..250a8ca 100644 --- a/SaBusinessMgrCoreWebPortal/Portal/Controllers/Commodities/CommodityController.cs +++ b/SaBusinessMgrCoreWebPortal/Portal/Controllers/Commodities/CommodityController.cs @@ -33,6 +33,8 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Commodities //------------------------------------------------------------------- [HttpGet] + [Route("Commodity")] + [Route("Commodities")] public IActionResult Index(Model_Commodity model) { if (!ValidUserSession) diff --git a/SaBusinessMgrCoreWebPortal/Portal/Controllers/Contacts/ContactController-Ajax.cs b/SaBusinessMgrCoreWebPortal/Portal/Controllers/Contacts/ContactController-Ajax.cs index 44d824a..54cd499 100644 --- a/SaBusinessMgrCoreWebPortal/Portal/Controllers/Contacts/ContactController-Ajax.cs +++ b/SaBusinessMgrCoreWebPortal/Portal/Controllers/Contacts/ContactController-Ajax.cs @@ -234,7 +234,8 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Contacts ColumnDfn colPhoneMobile = AccountMgr.GetAffiliateColumnDfn(SbmAffiliate.Column.PhoneMobile); ColumnDfn colPhoneDirect = AccountMgr.GetAffiliateColumnDfn(SbmAffiliate.Column.PhoneDirect); ColumnDfn colEmail = AccountMgr.GetAffiliateColumnDfn(SbmAffiliate.Column.Email); - ColumnDfn colAssocCount = AccountMgr.GetAffiliateColumnDfn(SbmAffiliate.Column.Association_Count); + ColumnDfn colAssocOwnerCount = AccountMgr.GetAffiliateColumnDfn(SbmAffiliate.Column.AssociationOwner_Count); + ColumnDfn colAssocTargetCount = AccountMgr.GetAffiliateColumnDfn(SbmAffiliate.Column.AssociationTarget_Count); ColumnDfn colNotes = AccountMgr.GetAffiliateColumnDfn(SbmAffiliate.Column.Notes); ColumnDfn colStatus = AccountMgr.GetAffiliateColumnDfn(SbmAffiliate.Column.Status); ColumnDfn colCreatedOn = AccountMgr.GetAffiliateColumnDfn(SbmAffiliate.Column.CreateOn_Date); @@ -242,7 +243,7 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Contacts List columns = new List() { colId, colName, colAffiliateType, - colAddress, colContact, colPhoneMain, colPhoneAlt, colPhoneMobile, colPhoneDirect, colEmail, colAssocCount, + colAddress, colContact, colPhoneMain, colPhoneAlt, colPhoneMobile, colPhoneDirect, colEmail, colAssocOwnerCount, colAssocTargetCount, colNotes, colStatus, colCreatedOn, colUpdatedOn }; if (responseData.Config.Column.Hide.Contains($"{Model_Contact.ItemField.Address}")) @@ -250,7 +251,7 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Contacts if (responseData.Config.Column.Hide.Contains($"{Model_Contact.ItemField.Email}")) columns.Remove(colEmail); if (responseData.Config.Column.Hide.Contains($"{Model_Contact.ItemField.Associations}")) - columns.Remove(colAssocCount); + columns.Remove(colAssocOwnerCount); // Sort Columns. if (!prmAffiliate.SortColumns.Any()) @@ -279,7 +280,9 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Contacts affiliateData.Field.Value.Add($"{Model_Contact.ItemField.Status}", SacsSystem.GetStatusDescForCode(values.GetString(colStatus.DataName))); affiliateData.Field.Value.Add($"{Model_Contact.ItemField.CreatedOn}", Localiser.FormatDate(values.GetDate(colCreatedOn.DataName))); affiliateData.Field.Value.Add($"{Model_Contact.ItemField.UpdatedOn}", Localiser.FormatDate(values.GetDate(colUpdatedOn.DataName))); - long assocCount = values.GetLong(colAssocCount.DataName); + long assocOwnerCount = values.GetLong(colAssocOwnerCount.DataName); + long assocTargetCount = values.GetLong(colAssocTargetCount.DataName); + long assocCount = assocOwnerCount + assocTargetCount; if (assocCount > 0) affiliateData.AssociationCount = assocCount; // Cleanse item values. diff --git a/SaBusinessMgrCoreWebPortal/Portal/Controllers/Contacts/ContactController.cs b/SaBusinessMgrCoreWebPortal/Portal/Controllers/Contacts/ContactController.cs index f2f1f7f..9c24a65 100644 --- a/SaBusinessMgrCoreWebPortal/Portal/Controllers/Contacts/ContactController.cs +++ b/SaBusinessMgrCoreWebPortal/Portal/Controllers/Contacts/ContactController.cs @@ -33,6 +33,8 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Contacts //------------------------------------------------------------------- [HttpGet] + [Route("Contact")] + [Route("Contacts")] public IActionResult Index(Model_Contact model) { if (!ValidUserSession) diff --git a/SaBusinessMgrCoreWebPortal/Portal/Controllers/SbmController_Portal.cs b/SaBusinessMgrCoreWebPortal/Portal/Controllers/SbmController_Portal.cs index 1087d71..01a8ce9 100644 --- a/SaBusinessMgrCoreWebPortal/Portal/Controllers/SbmController_Portal.cs +++ b/SaBusinessMgrCoreWebPortal/Portal/Controllers/SbmController_Portal.cs @@ -24,6 +24,10 @@ using static Sarand.BusinessMgr.Net.Standard.Base.App.Sbm.Config.Database; using Sarand.BusinessMgr.Net.Standard.Base.Bus.Control; using Sarand.BusinessMgr.Net.Core.Web.Helpers; using Sarand.BusinessMgr.Web.Portal.Helpers; +using DocumentFormat.OpenXml.EMMA; +using DocumentFormat.OpenXml.Wordprocessing; +using Sarand.BusinessMgr.Net.Core.Web.Models; +using Sarand.BusinessMgr.Web.Portal.Models; namespace Sarand.BusinessMgr.Web.Portal.Controllers { @@ -218,6 +222,26 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers base.OnActionExecuted(filterContext); } + //------------------------------------------------------------------- + // Model. + /// + /// Populate model wioth session data etc. + /// - Invoked from SbmController_Web.OnActionExecuting. + /// + /// + protected override void RefreshModel(SbmModel_Base model) + { + if (model == null) return; + base.RefreshModel(model); + + SbmModel_Portal portalModel = model as SbmModel_Portal; + if (portalModel != null) + { + // Populate menu options. + portalModel.PopulateMenu(); + } + } + //------------------------------------------------------------------- // Keep Logged In. //------------------------------------------------------------------- @@ -238,47 +262,41 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers String logSuffix = keepLoggedInParams.GetValidParams().Concat("", " ", "."); if (keepLoggedInParams.IsValid()) { - // User may be defined on any one of the BusinessMgr databases - need to find out which. - // Identify session contexts with a BusMgr database that hosts the specified email address. - List sessionContexts = SbmSessionContextHelper_Portal.GetSessionContextForRepositoryContextUserEmail(keepLoggedInParams.RepositoryContext, keepLoggedInParams.EmailAddress); - if (sessionContexts.Any()) + // Instance Repository configured on Control database identified by Context. + SbmSessionContext sessionContext = SbmSessionContextHelper_Portal.GetControlSessionContextByRepositoryContext(keepLoggedInParams.RepositoryContext); + if (sessionContext != null) { - foreach (SbmSessionContext sessionContext in sessionContexts) + // Consoles and Managers must be associated with the specific context. + SbmUserContext_Web userContext = new SbmUserContext_Web(sessionContext); + using (SaSecurityConsole securityConsoleForUser = App.CreateSecurityConsole(userContext)) + using (SbmAccountMgr accountMgrForUser = App.System.CreateAccountMgr(userContext)) { - // Consoles and Managers must be associated with the specific context. - SbmUserContext_Web userContext = new SbmUserContext_Web(sessionContext); - using (SaSecurityConsole securityConsoleForUser = App.CreateSecurityConsole(userContext)) - using (SbmAccountMgr accountMgrForUser = App.System.CreateAccountMgr(userContext)) + using (SaSecurityItem securityItemUser = securityConsoleForUser.GetUserByEmail(keepLoggedInParams.EmailAddress)) + using (SbmAffiliate affiliateUser = accountMgrForUser.GetAffiliateById(keepLoggedInParams.AffiliateIdUser)) + using (SbmAssociation associationOrganisation = accountMgrForUser.GetAssociationById(keepLoggedInParams.AssociationIdOrganisation)) + using (SbmAffiliate affiliateOrganisation = accountMgrForUser.GetAffiliateById(associationOrganisation?.AffiliateIdOwner ?? null)) { - using (SaSecurityItem securityItemUser = securityConsoleForUser.GetUserByEmail(keepLoggedInParams.EmailAddress)) - using (SbmAffiliate affiliateUser = accountMgrForUser.GetAffiliateById(keepLoggedInParams.AffiliateIdUser)) - using (SbmAssociation associationOrganisation = accountMgrForUser.GetAssociationById(keepLoggedInParams.AssociationIdOrganisation)) - using (SbmAffiliate affiliateOrganisation = accountMgrForUser.GetAffiliateById(associationOrganisation?.AffiliateIdOwner ?? null)) + if (securityItemUser != null && affiliateUser != null && associationOrganisation != null && affiliateOrganisation != null) { - if (securityItemUser != null && affiliateUser != null && associationOrganisation != null && affiliateOrganisation != null) + if (securityItemUser.Id.Equals(keepLoggedInParams.SecurityItemId) && affiliateUser.UserId.Equals(securityItemUser.Id)) { - if (securityItemUser.Id.Equals(keepLoggedInParams.SecurityItemId) && affiliateUser.UserId.Equals(securityItemUser.Id)) + if (affiliateUser.IsStatusCurrent) { - if (affiliateUser.IsStatusCurrent) + List supervisedOrganisations = accountMgrForUser.GetAffiliatesSupervisedBy(affiliateUser.Id); + String errorMessage = ""; + if (SignUserIn(userContext, keepLoggedInParams.RepositoryContext, securityConsoleForUser, accountMgrForUser, securityItemUser, affiliateUser, supervisedOrganisations, associationOrganisation, affiliateOrganisation, true, ref errorMessage)) { - List supervisedOrganisations = accountMgrForUser.GetAffiliatesSupervisedBy(affiliateUser.Id); - String errorMessage = ""; - if (SignUserIn(userContext, keepLoggedInParams.RepositoryContext, securityConsoleForUser, accountMgrForUser, securityItemUser, affiliateUser, supervisedOrganisations, associationOrganisation, affiliateOrganisation, true, ref errorMessage)) - { - SaLogger.Info(logPrefix + " 'Keep Logged In' cookie found." + logSuffix); - return true; - } - else SaLogger.Warn(logPrefix + "failed to sign user in: " + errorMessage + "." + logSuffix); + SaLogger.Info(logPrefix + " 'Keep Logged In' cookie found." + logSuffix); + return true; } - else SaLogger.Warn(logPrefix + "Affiliate is not active." + logSuffix); + else SaLogger.Warn(logPrefix + "failed to sign user in: " + errorMessage + "." + logSuffix); } - else SaLogger.Warn(logPrefix + "Security Item and Affiliate are inconsistent. " + logSuffix); + else SaLogger.Warn(logPrefix + "Affiliate is not active." + logSuffix); } - else SaLogger.Warn(logPrefix + "Security Item or Affiliate not found. " + logSuffix); + else SaLogger.Warn(logPrefix + "Security Item and Affiliate are inconsistent. " + logSuffix); } + else SaLogger.Warn(logPrefix + "Security Item or Affiliate not found. " + logSuffix); } - if (ValidUserSession) - break; } } else SaLogger.Warn($"{SysUtil.Fn} Keep Logged In cookie found but no matching session contexts found." + logSuffix); diff --git a/SaBusinessMgrCoreWebPortal/Portal/Controllers/Security/SecurityController-Ajax.cs b/SaBusinessMgrCoreWebPortal/Portal/Controllers/Security/SecurityController-Ajax.cs index 3d21749..dfef7d6 100644 --- a/SaBusinessMgrCoreWebPortal/Portal/Controllers/Security/SecurityController-Ajax.cs +++ b/SaBusinessMgrCoreWebPortal/Portal/Controllers/Security/SecurityController-Ajax.cs @@ -69,30 +69,12 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Security // Instance Repository configured on Control database. SbmSessionContext sessionContext = SbmSessionContextHelper_Portal.GetControlSessionContextByRepositoryContext(model.RepositoryContext); String errorMessage = ""; - if (!OpenUserSession(model, sessionContext, ref errorMessage)) { if (!StringUtil.IsBlank(errorMessage)) return Json(responseData.SetControlError(nameof(model.EmailAddress), errorMessage)); } } - - if (!(SessionData_Portal?.ValidUser ?? false)) - { - // Session contexts with a BusMgr database that hosts the specified email address. - List sessionContexts = SbmSessionContextHelper_Portal.GetSessionContextForRepositoryContextUserEmail(model.RepositoryContext, model.EmailAddress); - if (sessionContexts.IsEmpty()) - return Json(responseData.SetControlError(nameof(model.EmailAddress), model.Localiser["security.msg.signin.invalid"])); - foreach (SbmSessionContext sessionContext in sessionContexts) - { - String errorMessage = ""; - if (OpenUserSession(model, sessionContext, ref errorMessage)) break; - - if (!StringUtil.IsBlank(errorMessage)) - return Json(responseData.SetControlError(nameof(model.EmailAddress), errorMessage)); - } - } - } if (!(SessionData_Portal?.ValidUser ?? false)) @@ -125,14 +107,14 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Security disposables.Add(accountMgrForUser); // User's security item. - SaSecurityItem securityItemUser = GetSecurityItemForSignin(securityConsoleForUser, model); + SaSecurityItem securityItemUser = GetSecurityItemForSignin(userContext, securityConsoleForUser, model); disposables.Add(securityItemUser); if (securityItemUser == null) { SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve Security Item. Database: {busMgrDatabase.DatabaseName}. Email: {model.EmailAddress}."); return false; } - if (!securityConsoleForUser.PasswordMatches(securityItemUser, model.Password)) + if (!securityConsoleForUser.PasswordMatches(securityItemUser, model.Password, userContext.DbUpdateInfo)) { SaLogger.Warn($"{SysUtil.Fn} Supplied password doesn't match Security Item. Database: {busMgrDatabase.DatabaseName}. Email: {model.EmailAddress}."); return false; @@ -182,7 +164,7 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Security } //------------------------------------------------------------------- - private SaSecurityItem GetSecurityItemForSignin(SaSecurityConsole securityConsoleForUser, Model_Security model) + private SaSecurityItem GetSecurityItemForSignin(SbmUserContext_Web userContext, SaSecurityConsole securityConsoleForUser, Model_Security model) { // Retrieve security item by email address. SaSecurityItem securityItemUser = securityConsoleForUser.GetUserByEmail(model.EmailAddress); @@ -198,7 +180,7 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Security // Record invalid login attempt. securityItemUser.InvalidLoginCount++; securityItemUser.LastInvalidLoginAt = DateTime.Now; - if (!securityConsoleForUser.UpdateSecurityItem(securityItemUser, DbUpdateInfo)) + if (!securityConsoleForUser.UpdateSecurityItem(securityItemUser, userContext.DbUpdateInfo)) SaLogger.Warn($"{SysUtil.Fn} Failed to store invalid sign-in attempt against User: {model.EmailAddress}. {AccountMgr.ErrorStateMessage}", true); SaLogger.Warn($"{SysUtil.Fn} Attempt to sign-in to User {model.EmailAddress} with invalid password. Attempt: {securityItemUser.InvalidLoginCount}.", true); securityItemUser.Dispose(); securityItemUser = null; @@ -410,15 +392,14 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Security RequestPasswordParams storePasswordParams = ExtractRequestPasswordLinkParams(model.rpe); if (storePasswordParams.IsValid()) { - // User may be defined on any one of the BusinessMgr databases - need to find out which. - // Identify session contexts for databases with specified Id that hosts the specified email address. - List sessionContexts = SbmSessionContextHelper_Portal.GetSessionContextForRepositoryContextUserEmail(storePasswordParams.RepositoryContext, storePasswordParams.EmailAddress); - foreach (SbmSessionContext sessionContext in sessionContexts) + // Instance Repository configured on Control database identified by Context. + SbmSessionContext sessionContext = SbmSessionContextHelper_Portal.GetControlSessionContextByRepositoryContext(storePasswordParams.RepositoryContext); + if (sessionContext != null) { // Consoles and Managers must be associated with the specific context. SbmUserContext_Web userContext = new SbmUserContext_Web(sessionContext); - using (SaSecurityConsole securityConsoleForUser = App.CreateSecurityConsole(userContextForUser)) - using (SbmAccountMgr accountMgrForUser = App.System.CreateAccountMgr(userContextForUser)) + using (SaSecurityConsole securityConsoleForUser = App.CreateSecurityConsole(userContext)) + using (SbmAccountMgr accountMgrForUser = App.System.CreateAccountMgr(userContext)) { // Retrieve user details and validate email address. securityItemUser = securityConsoleForUser.GetSecurityItemById(SaSecurity.ItemType.User, storePasswordParams.SecurityItemId); @@ -429,7 +410,6 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Security userContextForUser = userContext; validRequestPasswordParams = true; validSessionRequired = false; - break; } else { @@ -471,7 +451,8 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Security String oldPasswordHashed = securityItemUser.PasswordHash; // Store password. - securityItemUser.PasswordHash = SbmPasswordHelper.GetPasswordHash(securityItemUser, model.Password); + // NB Do not use Microsoft.AspNetCore.Identity.PasswordHasher.HashPassword() while legacy (C++) desktop application still in use. + securityConsole.SetPassword(securityItemUser, model.Password); securityItemUser.PasswordChangedAt = DateTime.Now; // Remove new password from history if present. for (int passwordHistoryIdx = 0; passwordHistoryIdx < securityItemUser.PasswordHistory.Count; passwordHistoryIdx++) @@ -489,7 +470,7 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Security securityItemUser.PasswordHistory.Add(oldPasswordHashed); while (securityItemUser.PasswordHistory.Count > 10) securityItemUser.PasswordHistory.RemoveAt(securityItemUser.PasswordHistory.Count - 1); - if (!securityConsole.UpdateSecurityItem(securityItemUser, DbUpdateInfo)) + if (!securityConsole.UpdateSecurityItem(securityItemUser, userContextForUser?.DbUpdateInfo ?? DbUpdateInfo)) { SaLogger.Warn($"{SysUtil.Fn} Failed to change password for {securityItemUser.EmailAddress}: {securityConsole.ErrorStateMessage}"); return Json(responseData.SetControlError(nameof(model.Password), Localiser["security.msg.change.password.failed"])); @@ -537,9 +518,9 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Security model.ValidateRequestPasswordData(responseData); if (responseData.HasControlErrors()) return Json(responseData); - // Identify session contexts with a BusMgr database that hosts the specified email address. - List sessionContexts = SbmSessionContextHelper_Portal.GetSessionContextForRepositoryContextUserEmail(model.RepositoryContext, model.EmailAddress); - foreach (SbmSessionContext sessionContext in sessionContexts) + // Instance Repository configured on Control database identified by Context. + SbmSessionContext sessionContext = SbmSessionContextHelper_Portal.GetControlSessionContextByRepositoryContext(model.RepositoryContext); + if (sessionContext != null) { SbmUserContext_Web userContext = new SbmUserContext_Web(sessionContext); diff --git a/SaBusinessMgrCoreWebPortal/Portal/Controllers/Security/SecurityController.cs b/SaBusinessMgrCoreWebPortal/Portal/Controllers/Security/SecurityController.cs index acff24a..3a3e0e0 100644 --- a/SaBusinessMgrCoreWebPortal/Portal/Controllers/Security/SecurityController.cs +++ b/SaBusinessMgrCoreWebPortal/Portal/Controllers/Security/SecurityController.cs @@ -76,10 +76,9 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Security { String logPrefix = $"{SysUtil.Fn} 'Request Password' parameters supplied but "; String logSuffix = changePasswordParams.GetValidParams().Concat("", " ", "."); - // User may be defined on any one of the BusinessMgr databases - need to find out which. - // Identify session contexts with a BusMgr database that hosts the specified email address. - List sessionContexts = SbmSessionContextHelper_Portal.GetSessionContextForRepositoryContextUserEmail(changePasswordParams.RepositoryContext, changePasswordParams.EmailAddress); - foreach (SbmSessionContext sessionContext in sessionContexts) + // Instance Repository configured on Control database identified by Context. + SbmSessionContext sessionContext = SbmSessionContextHelper_Portal.GetControlSessionContextByRepositoryContext(changePasswordParams.RepositoryContext); + if (sessionContext != null) { // Consoles and Managers must be associated with the specific context. SbmUserContext_Web userContext = new SbmUserContext_Web(sessionContext); @@ -99,7 +98,6 @@ namespace Sarand.BusinessMgr.Web.Portal.Controllers.Security { model.rpe = encryptedParams; validSessionRequired = false; - break; } else SaLogger.Warn(logPrefix + "Security Item and Affiliate are incompatible with parameters. " + logSuffix); } diff --git a/SaBusinessMgrCoreWebPortal/Portal/Helpers/SbmSessionContextHelper_Portal.cs b/SaBusinessMgrCoreWebPortal/Portal/Helpers/SbmSessionContextHelper_Portal.cs index 2a28862..1e86276 100644 --- a/SaBusinessMgrCoreWebPortal/Portal/Helpers/SbmSessionContextHelper_Portal.cs +++ b/SaBusinessMgrCoreWebPortal/Portal/Helpers/SbmSessionContextHelper_Portal.cs @@ -63,6 +63,7 @@ namespace Sarand.BusinessMgr.Web.Portal.Helpers //------------------------------------------------------------------- // Session Contexts for Repository Context and User Email Address. //------------------------------------------------------------------- + [Obsolete("Deprecated - BusMgr databases only referenced via Control database rather than in application config.")] /// /// Identify the session context(s) that corresponds with the BusinessMgr /// repository by context for the specified user by email address. diff --git a/SaBusinessMgrCoreWebPortal/Portal/Models/SbmModel_Portal.cs b/SaBusinessMgrCoreWebPortal/Portal/Models/SbmModel_Portal.cs index 6f87ad0..7a067d8 100644 --- a/SaBusinessMgrCoreWebPortal/Portal/Models/SbmModel_Portal.cs +++ b/SaBusinessMgrCoreWebPortal/Portal/Models/SbmModel_Portal.cs @@ -40,6 +40,60 @@ namespace Sarand.BusinessMgr.Web.Portal.Models public bool UserCanCreateItem(Sbm.ItemType itemType) { return SessionData_Portal.SecurityProfile_Core.UserCanCreateItemType(itemType); } public bool UserCanUpdateItem(Sbm.ItemType itemType) { return SessionData_Portal.SecurityProfile_Core.UserCanUpdateItemType(itemType); } + //------------------------------------------------------------------- + // Page menu options. + + public class MenuOptionData + { + public String Caption { get; set; } + public String Page { get; set; } + } + public class MenuItemData + { + public String Caption { get; set; } + public List Options { get; } = new List(); + } + public class MenuData + { + public List Items { get; } = new List(); + } + public MenuData Menu { get; set; } + + /// + /// Populate available menu options for the page. + /// - Invoked from SbmController_Portal.OnActionExecuting. + /// + public void PopulateMenu() + { + Menu = new MenuData(); + + // Contacts. + MenuItemData contactsItem = new MenuItemData() { Caption = @Localiser["label.contacts"] }; + contactsItem.Options.Add(new MenuOptionData() { Caption = @Localiser["label.contacts"], Page = "Contacts" }); + contactsItem.Options.Add(new MenuOptionData() { Caption = @Localiser["label.customers"], Page = "Customers" }); + contactsItem.Options.Add(new MenuOptionData() { Caption = @Localiser["label.suppliers"], Page = "Suppliers" }); + contactsItem.Options.Add(new MenuOptionData() { Caption = @Localiser["label.associations"], Page = "Associations" }); + if (contactsItem.Options.Any()) + Menu.Items.Add(contactsItem); + + // Commodities. + MenuItemData commoditiesItem = new MenuItemData() { Caption = @Localiser["label.commodities"] }; + commoditiesItem.Options.Add(new MenuOptionData() { Caption = @Localiser["label.commodities"], Page = "Commodity" }); + commoditiesItem.Options.Add(new MenuOptionData() { Caption = @Localiser["caption.asset.categories"], Page = "Assets/AssetCategory" }); + commoditiesItem.Options.Add(new MenuOptionData() { Caption = @Localiser["label.assets"], Page = "Assets/Asset" }); + commoditiesItem.Options.Add(new MenuOptionData() { Caption = @Localiser["caption.vehicle.classes"], Page = "Assets/VehicleClass" }); + commoditiesItem.Options.Add(new MenuOptionData() { Caption = @Localiser["label.vehicles"], Page = "Assets/Vehicle" }); + if (commoditiesItem.Options.Any()) + Menu.Items.Add(commoditiesItem); + + // Reference. + MenuItemData referenceItem = new MenuItemData() { Caption = @Localiser["label.reference"] }; + referenceItem.Options.Add(new MenuOptionData() { Caption = @Localiser["caption.code.types"], Page = "Codes/CodeType" }); + referenceItem.Options.Add(new MenuOptionData() { Caption = @Localiser["label.codes"], Page = "Codes/Code" }); + if (referenceItem.Options.Any()) + Menu.Items.Add(referenceItem); + } + //------------------------------------------------------------------- // View reference data. diff --git a/SaBusinessMgrCoreWebPortal/Views/Commodity/Panel-CommodityMaintain.cshtml b/SaBusinessMgrCoreWebPortal/Views/Commodity/Panel-CommodityMaintain.cshtml index 9468bf8..a901584 100644 --- a/SaBusinessMgrCoreWebPortal/Views/Commodity/Panel-CommodityMaintain.cshtml +++ b/SaBusinessMgrCoreWebPortal/Views/Commodity/Panel-CommodityMaintain.cshtml @@ -40,13 +40,6 @@ @* Filter Panel *@
-
- @* Commodity filter - code, description etc *@ -
@Localiser["label.search"]
-
- @Html.TextBox("Filter.Search", "", new { @id = "", @class = "FilterCommodity sbm-input", @maxlength = "32" }) -
-
@* Commodity Category *@
@Localiser["label.type"]
@@ -54,6 +47,13 @@ @Html.DropDownList("Filter.CommodityType", Model.GetCommodityTypeCodesForSelect(), Localiser["label.any"], new { @id = "", @class = "FilterCommodityType sbm-select" })
+
+ @* Commodity filter - code, description etc *@ +
@Localiser["label.search"]
+
+ @Html.TextBox("Filter.Search", "", new { @id = "", @class = "FilterSearch sbm-input", @maxlength = "32" }) +
+
diff --git a/SaBusinessMgrCoreWebPortal/Views/Shared/_Layout.cshtml b/SaBusinessMgrCoreWebPortal/Views/Shared/_Layout.cshtml index 239ea31..9ebcc00 100644 --- a/SaBusinessMgrCoreWebPortal/Views/Shared/_Layout.cshtml +++ b/SaBusinessMgrCoreWebPortal/Views/Shared/_Layout.cshtml @@ -119,38 +119,20 @@ } else { - - - + @foreach (var menuItem in Model.Menu.Items) + { + + }