From 7b0a710b13b8e9522c536c6c2c76b4a0ff2607e3 Mon Sep 17 00:00:00 2001 From: Andy Howard Date: Mon, 31 May 2021 20:27:40 +0100 Subject: [PATCH] v1.0.5.3 Activity index, Db schema validation. --- .../SaxoniteRegionPopulation_Tester.cs | 2 +- ...ster.cs => SaxSessionData_Admin_Tester.cs} | 4 +- ...ter.cs => SaxApplication_Online_Tester.cs} | 0 ...ter.cs => SaxSessionData_Online_Tester.cs} | 84 +++++++---- SaxoniteCoreWeb/Base/App/SaxLocaliser.cs | 23 +++ .../Base/Consoles/SaxActivityConsole_Web.cs | 132 +++++++++++++++++- .../Base/Controllers/SaxController_Base.cs | 8 +- .../SystemController-Ajax-Database.cs | 92 +++++++++++- .../Admin/Models/Model_Home.cs | 1 + .../Localisation/SaxAdminProperties.resx | 15 ++ SaxoniteCoreWebAdmin/Views/Home/Index.cshtml | 29 +++- .../Views/Security/ChangePassword.cshtml | 87 ++++++------ .../Views/Security/RequestPassword.cshtml | 38 ++--- .../Views/Security/Signin.cshtml | 74 +++++----- .../Views/Shared/_Layout.cshtml | 14 +- SaxoniteCoreWebAdmin/wwwroot/css/sax-data.css | 19 ++- SaxoniteCoreWebAdmin/wwwroot/css/sax-form.css | 89 +++++++++++- SaxoniteCoreWebAdmin/wwwroot/js/sax-home.js | 65 +++++++++ .../Localisation/SaxOnlineProperties.resx | 6 + .../Online/App/SaxSessionData_Online.cs | 71 ++++++++++ .../Controllers/ActivityController-Ajax.cs | 51 ++++--- .../Controllers/SaxController_Online.cs | 35 +++++ .../Controllers/SecurityController-Ajax.cs | 2 - .../Online/Models/Model_Home.cs | 2 + SaxoniteCoreWebOnline/Views/Home/Index.cshtml | 55 +++++++- .../wwwroot/css/sax-data.css | 15 +- .../wwwroot/css/sax-home.css | 6 + SaxoniteCoreWebOnline/wwwroot/js/sax-home.js | 70 ++++++++++ .../wwwroot/js/sax-org-opp.js | 4 +- SaxoniteStandardBase/Base/App/Sax-Version.cs | 8 +- .../Base/App/SaxSessionData.cs | 14 +- .../Base/Bus/SaxActivityIndex.cs | 6 + .../Base/Consoles/SaxActivityConsole.cs | 15 +- .../Mgr/SaxActivityMgr_Dbas-ActivityIndex.cs | 72 +++++++++- .../SaxActivityMgr_Dbas-JournalistArticle.cs | 4 +- ...ctivityMgr_Dbas-OrganisationOpportunity.cs | 4 +- 36 files changed, 1008 insertions(+), 208 deletions(-) rename SaxoniteCoreUnitTests/UnitTests/Web/Admin/App/{SaxSessionData_Tester.cs => SaxSessionData_Admin_Tester.cs} (97%) rename SaxoniteCoreUnitTestsOnline/UnitTests/Online/App/{SaxApplication_Admin_Tester.cs => SaxApplication_Online_Tester.cs} (100%) rename SaxoniteCoreUnitTestsOnline/UnitTests/Online/App/{SaxSessionData_Tester.cs => SaxSessionData_Online_Tester.cs} (52%) create mode 100644 SaxoniteCoreWebAdmin/wwwroot/js/sax-home.js create mode 100644 SaxoniteCoreWebOnline/wwwroot/css/sax-home.css create mode 100644 SaxoniteCoreWebOnline/wwwroot/js/sax-home.js diff --git a/SaxoniteCoreUnitTests/UnitTests/Data/Population/SaxoniteRegionPopulation_Tester.cs b/SaxoniteCoreUnitTests/UnitTests/Data/Population/SaxoniteRegionPopulation_Tester.cs index b9e1740..b28b107 100644 --- a/SaxoniteCoreUnitTests/UnitTests/Data/Population/SaxoniteRegionPopulation_Tester.cs +++ b/SaxoniteCoreUnitTests/UnitTests/Data/Population/SaxoniteRegionPopulation_Tester.cs @@ -7,7 +7,7 @@ namespace Saxonite.Net.Core.UnitTests.Data.Population { //======================================================================= [TestClass] - class SaxoniteRegionPopulation_Tester + public class SaxoniteRegionPopulation_Tester { } //======================================================================= diff --git a/SaxoniteCoreUnitTests/UnitTests/Web/Admin/App/SaxSessionData_Tester.cs b/SaxoniteCoreUnitTests/UnitTests/Web/Admin/App/SaxSessionData_Admin_Tester.cs similarity index 97% rename from SaxoniteCoreUnitTests/UnitTests/Web/Admin/App/SaxSessionData_Tester.cs rename to SaxoniteCoreUnitTests/UnitTests/Web/Admin/App/SaxSessionData_Admin_Tester.cs index aedf81a..f179440 100644 --- a/SaxoniteCoreUnitTests/UnitTests/Web/Admin/App/SaxSessionData_Tester.cs +++ b/SaxoniteCoreUnitTests/UnitTests/Web/Admin/App/SaxSessionData_Admin_Tester.cs @@ -14,7 +14,7 @@ namespace Saxonite.Net.Core.UnitTests.Web.Admin.App { //======================================================================= [TestClass] - public class SaxSessionData_Tester + public class SaxSessionData_Admin_Tester { //------------------------------------------------------------------- // Properties. @@ -23,7 +23,7 @@ namespace Saxonite.Net.Core.UnitTests.Web.Admin.App //------------------------------------------------------------------- [TestMethod] - public void Test_SmmSessionData_Serialization() + public void Test_SmmSessionData_Admin_Serialization() { // User account. SaxAccountMgr accountMgr = Internals.AccountMgr; diff --git a/SaxoniteCoreUnitTestsOnline/UnitTests/Online/App/SaxApplication_Admin_Tester.cs b/SaxoniteCoreUnitTestsOnline/UnitTests/Online/App/SaxApplication_Online_Tester.cs similarity index 100% rename from SaxoniteCoreUnitTestsOnline/UnitTests/Online/App/SaxApplication_Admin_Tester.cs rename to SaxoniteCoreUnitTestsOnline/UnitTests/Online/App/SaxApplication_Online_Tester.cs diff --git a/SaxoniteCoreUnitTestsOnline/UnitTests/Online/App/SaxSessionData_Tester.cs b/SaxoniteCoreUnitTestsOnline/UnitTests/Online/App/SaxSessionData_Online_Tester.cs similarity index 52% rename from SaxoniteCoreUnitTestsOnline/UnitTests/Online/App/SaxSessionData_Tester.cs rename to SaxoniteCoreUnitTestsOnline/UnitTests/Online/App/SaxSessionData_Online_Tester.cs index 041bb56..c18acd0 100644 --- a/SaxoniteCoreUnitTestsOnline/UnitTests/Online/App/SaxSessionData_Tester.cs +++ b/SaxoniteCoreUnitTestsOnline/UnitTests/Online/App/SaxSessionData_Online_Tester.cs @@ -15,7 +15,7 @@ namespace Saxonite.Net.Core.UnitTests.Web.Online.App { //======================================================================= [TestClass] - public class SaxSessionData_Tester + public class SaxSessionData_Online_Tester { //------------------------------------------------------------------- // Properties. @@ -24,7 +24,7 @@ namespace Saxonite.Net.Core.UnitTests.Web.Online.App //------------------------------------------------------------------- [TestMethod] - public void Test_SmmSessionData_Serialization() + public void Test_SmmSessionData_Online_Serialization() { // User account. SaxAccountMgr accountMgr = Internals.AccountMgr; @@ -35,23 +35,41 @@ namespace Saxonite.Net.Core.UnitTests.Web.Online.App userAccount.EmailAddress = "john.smith@acme.co.uk"; userAccount.FirstName = "John"; userAccount.LastName = "Smith"; + userAccount.RegionIdCountry = new SysId(SacsRandomHelper.GetInt32(Int32.MaxValue)); + userAccount.RegionIdProvince = new SysId(SacsRandomHelper.GetInt32(Int32.MaxValue)); // User Roles. List userRoles = new List(); SaxUserRole userRole = accountMgr.CreateUserRole(userAccount.Id, Sax.UserRole.Journalist); userRoles.Add(userRole); + // User Sports. + SaxSessionData_Online.UserProfileData.SportData sport1 = new SaxSessionData_Online.UserProfileData.SportData(new SysId(SacsRandomHelper.GetInt32(Int32.MaxValue)), Sarand.Net.Standard.Base.Core.SaStatus.Active); + SaxSessionData_Online.UserProfileData.SportData sport2 = new SaxSessionData_Online.UserProfileData.SportData(new SysId(SacsRandomHelper.GetInt32(Int32.MaxValue)), Sarand.Net.Standard.Base.Core.SaStatus.Active); + SaxSessionData_Online.UserProfileData.SportData sport3 = new SaxSessionData_Online.UserProfileData.SportData(new SysId(SacsRandomHelper.GetInt32(Int32.MaxValue)), Sarand.Net.Standard.Base.Core.SaStatus.Inactive); + List sports = new List() + { + sport1, sport2, sport3 + }; + // Create user session and verify user id. SaxSessionData_Online sessionData = new SaxSessionData_Online(App.SessionContext, userAccount, userRoles); Assert.AreEqual(SysId.ToInt(sessionData.User.UserAccountId), SysId.ToInt(sessionData.User.UserAccountId)); Assert.AreEqual(userAccount.FirstName, sessionData.User.FirstName); Assert.AreEqual(userAccount.LastName, sessionData.User.LastName); + Assert.AreEqual(userAccount.RegionIdCountry, sessionData.User.RegionIdCountry); + Assert.AreEqual(userAccount.RegionIdProvince, sessionData.User.RegionIdProvince); Assert.IsTrue(sessionData.ValidUserSession); Assert.AreNotEqual("", sessionData.User.DisplayName); + // Profile sports. + foreach (SaxSessionData_Online.UserProfileData.SportData sport in sports) + sessionData.Profile.AddSport(sport.SportId, sport.Status); + Assert.AreEqual(sports.Count, sessionData.Profile.Sports.Count); + // Filter properties. SysId filterOrganisationId = new SysId(SacsRandomHelper.GetInt32(Int32.MaxValue)); - String filterOrganisationCaption= "Filter organisation caption"; + String filterOrganisationCaption = "Filter organisation caption"; sessionData.Filter.OrganisationId = filterOrganisationId; sessionData.Filter.OrganisationCaption = filterOrganisationCaption; SysId filterOrganisationIdMember = new SysId(SacsRandomHelper.GetInt32(Int32.MaxValue)); @@ -64,34 +82,52 @@ namespace Saxonite.Net.Core.UnitTests.Web.Online.App Assert.AreNotEqual("", sessionDataJson); // Deserialise session data using same method as base controller. - SaxSessionData_Online sessionDataDeserialized = + SaxSessionData_Online sessionDataDeserialised = JsonConvert.DeserializeObject( sessionDataJson, - new JsonSerializerSettings { ContractResolver = new SaxController_Base.SessionStorage.SaxSessionDataContractResolver() } + SaxController_Base.SessionStorage.JsonDeserialisationSettings ); - Assert.IsNotNull(sessionDataDeserialized); + Assert.IsNotNull(sessionDataDeserialised); // Validate deserialised values. - Assert.AreEqual(App.SessionContext.Index, sessionDataDeserialized.SessionContext.Index); - Assert.AreEqual(Sax.Application.Online, sessionDataDeserialized.SessionContext.Application); - Assert.AreEqual(SysId.ToInt(sessionDataDeserialized.User.UserAccountId), SysId.ToInt(sessionDataDeserialized.User.UserAccountId)); - Assert.AreEqual(SysId.ToInt(sessionData.User.UserAccountId), SysId.ToInt(sessionDataDeserialized.User.UserAccountId)); - Assert.AreEqual(sessionData.User.EmailAddress, sessionDataDeserialized.User.EmailAddress); - Assert.AreEqual(sessionData.User.FirstName, sessionDataDeserialized.User.FirstName); - Assert.AreEqual(sessionData.User.LastName, sessionDataDeserialized.User.LastName); - Assert.AreEqual(sessionData.User.DisplayName, sessionDataDeserialized.User.DisplayName); + Assert.AreEqual(App.SessionContext.Index, sessionDataDeserialised.SessionContext.Index); + Assert.AreEqual(Sax.Application.Online, sessionDataDeserialised.SessionContext.Application); + Assert.AreEqual(SysId.ToInt(sessionDataDeserialised.User.UserAccountId), SysId.ToInt(sessionDataDeserialised.User.UserAccountId)); + Assert.AreEqual(SysId.ToInt(sessionData.User.UserAccountId), SysId.ToInt(sessionDataDeserialised.User.UserAccountId)); + Assert.AreEqual(sessionData.User.EmailAddress, sessionDataDeserialised.User.EmailAddress); + Assert.AreEqual(sessionData.User.FirstName, sessionDataDeserialised.User.FirstName); + Assert.AreEqual(sessionData.User.LastName, sessionDataDeserialised.User.LastName); + Assert.AreEqual(sessionData.User.DisplayName, sessionDataDeserialised.User.DisplayName); + Assert.AreEqual(sessionData.User.RegionIdCountry, sessionDataDeserialised.User.RegionIdCountry); + Assert.AreEqual(sessionData.User.RegionIdProvince, sessionDataDeserialised.User.RegionIdProvince); + // User roles. - Assert.AreEqual(userRoles.Count, sessionDataDeserialized.User.Roles.Count); + Assert.AreEqual(userRoles.Count, sessionDataDeserialised.User.Roles.Count); + foreach (var userRoleDeserialised in sessionDataDeserialised.User.Roles) + { + Assert.AreNotEqual(0, userRoleDeserialised.Role); + } + + // Profile sports. + Assert.AreEqual(sports.Count, sessionDataDeserialised.Profile.Sports.Count); + foreach (SaxSessionData_Online.UserProfileData.SportData sport in sports) + { + SaxSessionData_Online.UserProfileData.SportData sportDeserialised = sessionDataDeserialised.Profile.Sports.Find(item => item.SportId.Equals(sport.SportId)); + Assert.IsNotNull(sportDeserialised); + Assert.AreEqual(sport.SportId, sportDeserialised.SportId); + Assert.AreEqual(sport.Status, sportDeserialised.Status); + } + // Filter properties. - Assert.IsNotNull(sessionDataDeserialized.Filter); - Assert.IsNotNull(sessionDataDeserialized.Filter.OrganisationId); - Assert.IsTrue(SysId.IsValid(sessionDataDeserialized.Filter.OrganisationId)); - Assert.AreEqual(filterOrganisationId, sessionDataDeserialized.Filter.OrganisationId); - Assert.AreEqual(filterOrganisationCaption, sessionDataDeserialized.Filter.OrganisationCaption); - Assert.IsNotNull(sessionDataDeserialized.Filter.OrganisationIdMember); - Assert.IsTrue(SysId.IsValid(sessionDataDeserialized.Filter.OrganisationIdMember)); - Assert.AreEqual(filterOrganisationIdMember, sessionDataDeserialized.Filter.OrganisationIdMember); - Assert.AreEqual(filterOrganisationCaptionMember, sessionDataDeserialized.Filter.OrganisationCaptionMember); + Assert.IsNotNull(sessionDataDeserialised.Filter); + Assert.IsNotNull(sessionDataDeserialised.Filter.OrganisationId); + Assert.IsTrue(SysId.IsValid(sessionDataDeserialised.Filter.OrganisationId)); + Assert.AreEqual(filterOrganisationId, sessionDataDeserialised.Filter.OrganisationId); + Assert.AreEqual(filterOrganisationCaption, sessionDataDeserialised.Filter.OrganisationCaption); + Assert.IsNotNull(sessionDataDeserialised.Filter.OrganisationIdMember); + Assert.IsTrue(SysId.IsValid(sessionDataDeserialised.Filter.OrganisationIdMember)); + Assert.AreEqual(filterOrganisationIdMember, sessionDataDeserialised.Filter.OrganisationIdMember); + Assert.AreEqual(filterOrganisationCaptionMember, sessionDataDeserialised.Filter.OrganisationCaptionMember); } //------------------------------------------------------------------- diff --git a/SaxoniteCoreWeb/Base/App/SaxLocaliser.cs b/SaxoniteCoreWeb/Base/App/SaxLocaliser.cs index eea16a9..706110c 100644 --- a/SaxoniteCoreWeb/Base/App/SaxLocaliser.cs +++ b/SaxoniteCoreWeb/Base/App/SaxLocaliser.cs @@ -3,7 +3,9 @@ using Microsoft.AspNetCore.Mvc.Localization; using Microsoft.Extensions.Localization; using Microsoft.Extensions.Logging; 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.Standard.Base.App; using Saxonite.Net.Standard.Base.Bus; using System; @@ -119,6 +121,17 @@ namespace Saxonite.Net.Core.Web.Base.App return value; } + //------------------------------------------------------------------- + // Numeric and datetime formatting + //------------------------------------------------------------------- + public static String DateFormat_Default { get; } = "d-MMM-yyyy"; + public static String DateWithWeekdayFormat_Default { get; } = "ddd d-MMM-yyyy"; + public String FormatDate(DateTime value, Boolean includeWeekday = true) + { + String dateFormat = includeWeekday ? DateWithWeekdayFormat_Default : DateFormat_Default; + return DateUtil.Format(value, dateFormat); + } + //------------------------------------------------------------------- // Specific phrase translation. //------------------------------------------------------------------- @@ -208,6 +221,11 @@ namespace Saxonite.Net.Core.Web.Base.App // ToDo Region name localisation. return region.Name; } + public String GetRegionName(SysId regionId, String regionName) + { + // ToDo Region name localisation. + return regionName; + } //------------------------------------------------------------------- // Sport. @@ -217,6 +235,11 @@ namespace Saxonite.Net.Core.Web.Base.App // ToDo Sport name localisation. return sport.Name; } + public String GetSportName(SysId sportId, String sportName) + { + // ToDo Sport name localisation. + return sportName; + } //------------------------------------------------------------------- } diff --git a/SaxoniteCoreWeb/Base/Consoles/SaxActivityConsole_Web.cs b/SaxoniteCoreWeb/Base/Consoles/SaxActivityConsole_Web.cs index 03f991d..3a59d8c 100644 --- a/SaxoniteCoreWeb/Base/Consoles/SaxActivityConsole_Web.cs +++ b/SaxoniteCoreWeb/Base/Consoles/SaxActivityConsole_Web.cs @@ -10,6 +10,7 @@ using Saxonite.Net.Standard.Base.Bus; using Saxonite.Net.Standard.Base.Consoles; using System; using System.Collections.Generic; +using System.Linq; namespace Saxonite.Net.Core.Web.Base.Consoles { @@ -46,6 +47,20 @@ namespace Saxonite.Net.Core.Web.Base.Consoles //------------------------------------------------------------------- // Query Activity Index Entries for User Context. //------------------------------------------------------------------- + private class ActivityData_Web : IndexResults.ActivityData + { + public ActivityData_Web(ItemId itemId, DateTime effectiveOn) : base(itemId, effectiveOn) { } + public void SetCaption(String caption) { Caption = caption; } + public void SetBlurb(String blurb) { Blurb = blurb; } + public void SetSport(SysId sportId, String sportName) { SportId = sportId; SportName = sportName; } + public void SetRegion(SysId regionId, String regionName) { RegionId = regionId; RegionName = regionName; } + public static int CompareActivityIndices(ActivityData_Web item1, ActivityData_Web item2) + { + int effectiveCompare = item1.EffectiveOn.CompareTo(item2.EffectiveOn); + if (effectiveCompare != null) return effectiveCompare * -1; + return 1; + } + } public override IndexResults QueryIndex(IndexContext context) { String fn = GetType().Name + ".QueryIndex"; @@ -55,6 +70,7 @@ namespace Saxonite.Net.Core.Web.Base.Consoles // Columns to query. ColumnDfn colItemType = internals.ActivityMgr.GetActivityIndexColumnDfn(SaxActivityIndex.Column.ItemType); ColumnDfn colItemId = internals.ActivityMgr.GetActivityIndexColumnDfn(SaxActivityIndex.Column.ItemId); + String colDataNameEffectiveOn = "EffectiveOn"; // Query the index. ISacsDatabase db = SaxGlobals.App.GetCoreDatabase(SessionContext); @@ -62,9 +78,10 @@ namespace Saxonite.Net.Core.Web.Base.Consoles query.Tables.Add(SaxDbDfn_Core.GetTableName(SaxDbDfn_Core.TableIdx.ActivityIndex, false), alias_ActivityIndex); query.Columns.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.ActivityIndex_ItemType, alias_ActivityIndex), colItemType.DataName); query.Columns.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.ActivityIndex_ItemId, alias_ActivityIndex), colItemId.DataName); - query.Columns.Add("Max(" + SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.ActivityIndex_EffectiveOn) + ")", alias_ActivityIndex); + query.Columns.Add("Max(" + SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.ActivityIndex_EffectiveOn, alias_ActivityIndex) + ")", colDataNameEffectiveOn); query.GroupBy.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.ActivityIndex_ItemType, alias_ActivityIndex)); query.GroupBy.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.ActivityIndex_ItemId, alias_ActivityIndex)); + query.OrderBy.Add("Max(" + SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.ActivityIndex_EffectiveOn, alias_ActivityIndex) + ") DESC"); // Filter. SaxActivityIndex.Params prmActivityIndex = new SaxActivityIndex.Params(); @@ -75,6 +92,8 @@ namespace Saxonite.Net.Core.Web.Base.Consoles prmActivityIndex.Alias_ActivityIndex = alias_ActivityIndex; query.Filters.AddRange(internals.ActivityMgr.GetActivityIndexQueryFilter(prmActivityIndex)); + // First pass, note the activity Item and effective date. + List activityDataList = new List(); using (DbResultSet dbResultSet = DbResultSet_DataReader.ExecuteQuery(db, query.BuildQuery(context.Paging))) { while (dbResultSet != null && dbResultSet.NextRecord()) @@ -82,14 +101,121 @@ namespace Saxonite.Net.Core.Web.Base.Consoles NamedValue.Map values = dbResultSet.GetRecordValues(); Sax.DataItemType itemType = (Sax.DataItemType)values.GetLong(colItemType.DataName); ItemId itemId = new ItemId((short)itemType, new SysId(values.GetLong(colItemId.DataName))); - - SaLogger.Info(fn + " Activity found - " + itemType.ToString() + ":" + SysId.ToString(itemId.Id)); + DateTime effectiveOn = values.GetDate(colDataNameEffectiveOn); + activityDataList.Add(new ActivityData_Web(itemId, effectiveOn)); + SaLogger.Info(fn + " Activity found - " + itemType.ToString() + ":" + SysId.ToString(itemId.Id) + ". Effective: " + DateUtil.Format(effectiveOn, "d-MMM-yyyy")); } } + activityDataList.Sort(ActivityData_Web.CompareActivityIndices); + // Populate activity details. + PopulateActivityDetails(activityDataList); + + // ToDo Remove activities with blank caption. + + results.Items.AddRange(activityDataList); return results; } + //------------------------------------------------------------------- + // Populate Activity Details. + //------------------------------------------------------------------- + private void PopulateActivityDetails(List activities) + { + PopulateJournalistArticleDetails(activities); + PopulateOrganisationOpportunitiesDetails(activities); + } + private void PopulateJournalistArticleDetails(List activities) + { + // Extract article Ids. + List articleIds = activities.FindAll(item => item.ItemId.Type == (int)Sax.DataItemType.JournalistArticle).Select(ItemId => ItemId.ItemId.Id).ToList(); + if (articleIds.IsEmpty()) return; + + // Article columns - limit to essential information. + ColumnDfn colId = Internals.ActivityMgr.GetJournalistArticleColumnDfn(SaxJournalistArticle.Column.Id); + ColumnDfn colHeadline = Internals.ActivityMgr.GetJournalistArticleColumnDfn(SaxJournalistArticle.Column.Headline); + ColumnDfn colSummary = Internals.ActivityMgr.GetJournalistArticleColumnDfn(SaxJournalistArticle.Column.Summary); + ColumnDfn colSportId = Internals.ActivityMgr.GetJournalistArticleColumnDfn(SaxJournalistArticle.Column.SportIdPrimary); + ColumnDfn colSportName = Internals.ActivityMgr.GetJournalistArticleColumnDfn(SaxJournalistArticle.Column.Sport_Name); + ColumnDfn colRegionId = Internals.ActivityMgr.GetJournalistArticleColumnDfn(SaxJournalistArticle.Column.RegionIdPrimary); + ColumnDfn colRegionName = Internals.ActivityMgr.GetJournalistArticleColumnDfn(SaxJournalistArticle.Column.Region_Name); + List columns = new List() { colId, colHeadline, colSummary, colSportId, colSportName, colRegionId, colRegionName }; + + // Article filter. + SaxJournalistArticle.Params prmArticle = new SaxJournalistArticle.Params(); + prmArticle.IdsIncluded.AddRange(articleIds); + prmArticle.StatusCat = SaxJournalistArticle.StatusCat.ActiveOnly; + + using (DbResultSet dbResultSet = Internals.ActivityMgr.ExecuteJournalistArticleQuery(columns, prmArticle)) + { + while (dbResultSet != null && dbResultSet.NextRecord()) + { + NamedValue.Map values = dbResultSet.GetRecordValues(); + ItemId itemId = new ItemId((short)Sax.DataItemType.JournalistArticle, new SysId(values.GetLong(colId.DataName))); + String headline = values.GetString(colHeadline.DataName); + String summary = values.GetString(colSummary.DataName); + SysId sportId = new SysId(values.GetLong(colSportId.DataName)); + String sportName = values.GetString(colSportName.DataName); + SysId regionId = new SysId(values.GetLong(colRegionId.DataName)); + String regionName = values.GetString(colRegionName.DataName); + + // Populate to corresponding activities. + foreach (ActivityData_Web activity in activities.FindAll(item => item.ItemId.Equals(itemId))) + { + activity.SetCaption(headline); + activity.SetBlurb(summary); + activity.SetSport(sportId, sportName); + activity.SetRegion(regionId, regionName); + } + } + } + } + private void PopulateOrganisationOpportunitiesDetails(List activities) + { + // Extract opportunity Ids. + List opportunityIds = activities.FindAll(item => item.ItemId.Type == (int)Sax.DataItemType.OrganisationOpportunity).Select(ItemId => ItemId.ItemId.Id).ToList(); + if (opportunityIds.IsEmpty()) return; + + // Opportunity columns - limit to essential information. + ColumnDfn colId = Internals.ActivityMgr.GetOrganisationOpportunityColumnDfn(SaxOrganisationOpportunity.Column.Id); + ColumnDfn colPosition = Internals.ActivityMgr.GetOrganisationOpportunityColumnDfn(SaxOrganisationOpportunity.Column.Position); + ColumnDfn colOutline = Internals.ActivityMgr.GetOrganisationOpportunityColumnDfn(SaxOrganisationOpportunity.Column.Outline); + ColumnDfn colSportId = Internals.ActivityMgr.GetOrganisationOpportunityColumnDfn(SaxOrganisationOpportunity.Column.SportIdPrimary); + ColumnDfn colSportName = Internals.ActivityMgr.GetOrganisationOpportunityColumnDfn(SaxOrganisationOpportunity.Column.Sport_Name); + ColumnDfn colRegionId = Internals.ActivityMgr.GetOrganisationOpportunityColumnDfn(SaxOrganisationOpportunity.Column.RegionIdPrimary); + ColumnDfn colRegionName = Internals.ActivityMgr.GetOrganisationOpportunityColumnDfn(SaxOrganisationOpportunity.Column.Region_Name); + List columns = new List() { colId, colPosition, colOutline, colSportId, colSportName, colRegionId, colRegionName }; + + // Opportunity filter. + SaxOrganisationOpportunity.Params prmOpportunity = new SaxOrganisationOpportunity.Params(); + prmOpportunity.IdsIncluded.AddRange(opportunityIds); + prmOpportunity.StatusCat = SaxOrganisationOpportunity.StatusCat.ActiveOnly; + + using (DbResultSet dbResultSet = Internals.ActivityMgr.ExecuteOrganisationOpportunityQuery(columns, prmOpportunity)) + { + while (dbResultSet != null && dbResultSet.NextRecord()) + { + NamedValue.Map values = dbResultSet.GetRecordValues(); + ItemId itemId = new ItemId((short)Sax.DataItemType.OrganisationOpportunity, new SysId(values.GetLong(colId.DataName))); + String headline = values.GetString(colPosition.DataName); + String summary = values.GetString(colOutline.DataName); + SysId sportId = new SysId(values.GetLong(colSportId.DataName)); + String sportName = values.GetString(colSportName.DataName); + SysId regionId = new SysId(values.GetLong(colRegionId.DataName)); + String regionName = values.GetString(colRegionName.DataName); + + // Populate to corresponding activities. + foreach (ActivityData_Web activity in activities.FindAll(item => item.ItemId.Equals(itemId))) + { + activity.SetCaption(headline); + activity.SetBlurb(summary); + activity.SetSport(sportId, sportName); + activity.SetRegion(regionId, regionName); + } + } + } + } + //------------------------------------------------------------------- // Store Activity Index. //------------------------------------------------------------------- diff --git a/SaxoniteCoreWeb/Base/Controllers/SaxController_Base.cs b/SaxoniteCoreWeb/Base/Controllers/SaxController_Base.cs index 5c12094..1c43730 100644 --- a/SaxoniteCoreWeb/Base/Controllers/SaxController_Base.cs +++ b/SaxoniteCoreWeb/Base/Controllers/SaxController_Base.cs @@ -309,13 +309,15 @@ namespace Saxonite.Net.Core.Web.Base.Controllers return base.CreateProperties(type, MemberSerialization.Fields); } }; - public JsonSerializerSettings SaxSessionDataJsonSerializerSettings = new JsonSerializerSettings + public static JsonSerializerSettings JsonDeserialisationSettings = new JsonSerializerSettings { - ContractResolver = new SaxSessionDataContractResolver() + ContractResolver = new SaxSessionDataContractResolver(), + // Force all deserialised objects to be replaced - to avoid proxy lists not populating. + ObjectCreationHandling = ObjectCreationHandling.Replace }; } private readonly SessionStorage sessionStorage = new SessionStorage(); - protected JsonSerializerSettings SaxSessionDataJsonSerializerSettings { get { return sessionStorage.SaxSessionDataJsonSerializerSettings; } } + protected JsonSerializerSettings SaxSessionDataJsonSerializerSettings { get { return SessionStorage.JsonDeserialisationSettings; } } //------------------------------------------------------------------- public SaxSessionData SessionData diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/SystemController-Ajax-Database.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/SystemController-Ajax-Database.cs index b1593d4..682b6e8 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/SystemController-Ajax-Database.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/SystemController-Ajax-Database.cs @@ -3,13 +3,19 @@ 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.Helpers; +using Sarand.Net.Standard.Base.Procs; +using Sarand.Net.Standard.Base.Utils; using Sarand.Net.Standard.Dbas.Data; +using Sarand.Net.Standard.Dbas.Proc; using Saxonite.Net.Core.Web.Admin.Models; using Saxonite.Net.Standard.Base.App; using Saxonite.Net.Standard.Base.Bus; +using Saxonite.Net.Standard.Dbas.App; using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Threading.Tasks; namespace Saxonite.Net.Core.Web.Admin.Controllers @@ -17,7 +23,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers //======================================================================= // System - Ajax - Database controller //======================================================================= - partial class SystemController + partial class SystemController : SaDbProcess_CompareDatabaseSchemas.IProcessNotifier { //------------------------------------------------------------------- // Database data. @@ -92,6 +98,90 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers return Json(responseData); } + //------------------------------------------------------------------- + // Validate Database Schema ajax request. + //------------------------------------------------------------------- + public class DbSchemaItemResponseData + { + public int Id { get; set; } + public String TableName { get; set; } + public String ColumnName { get; set; } + public int ColumnPosition { get; set; } + public String IndexName { get; set; } + public String Comment { get; set; } + } + public class AjaxResponse_ValidateDatabaseSchema : AjaxResponse_Data + { + public List Items { get; } = new List(); + } + //------------------------------------------------------------------- + [HttpPost] + [Route("System/Ajax/ValidateDatabaseSchema")] + public IActionResult Ajax_ValidateDatabaseSchema(Model_Sport model) + { + String fn = GetType().Name + ".Ajax_GetDatabaseTables"; + String messageTitle = Localiser["system.caption.database"]; + AjaxResponse_ValidateDatabaseSchema responseData = new AjaxResponse_ValidateDatabaseSchema(); + + // Validate session. + if (!ValidUserSession) + return GetAjaxResult_InvalidSession(responseData); + + // Core Database connection. + ISacsDatabase db = SaxGlobals.App.GetCoreDatabase(SessionContext); + + // Retrieve XML database schema definition. + var assembly = typeof(SaxSystem_Dbas).GetTypeInfo().Assembly; + String xmlSchema = SacsResourceHelper.GetManifestResourceAsText(assembly, "", "SaxDbDfn_Core.xml"); + if (StringUtil.IsEmpty(xmlSchema)) + return Json(responseData.SetMessage(messageTitle, "Failed to extract XML schema definition.")); + + // Extract schema from XML definition. + SaDbProcess_ConvertDbSchema.Config cfgSchemaConvert = new SaDbProcess_ConvertDbSchema.Config(); + SaDbProcess_ConvertDbSchema procSchemaConvert = new SaDbProcess_ConvertDbSchema(cfgSchemaConvert); + List tables = procSchemaConvert.ConvertXmlToTables(xmlSchema); + if (procSchemaConvert.HasProcessError()) + return Json(responseData.SetMessage(messageTitle, "Failed to convert XML schema: " + procSchemaConvert.GetProcessError())); + + SaDbProcess_CompareDatabaseSchemas.Config cfgSchemaCompare = new SaDbProcess_CompareDatabaseSchemas.Config(tables, db); + cfgSchemaCompare.HideIdenticalObjects = true; + SaDbProcess_CompareDatabaseSchemas procSchemaCompare = new SaDbProcess_CompareDatabaseSchemas(cfgSchemaCompare); + procSchemaCompare.ProcessNotifier = this; + procSchemaCompare.RunProcess(); + + List schemaComparisonItems = procSchemaCompare.GetSchemaComparisonItems(); + foreach (SaDbProcess_CompareDatabaseSchemas.SchemaComparisonItem schemaComparisonItem in schemaComparisonItems) + { + DbSchemaItemResponseData item = new DbSchemaItemResponseData() + { + Id = schemaComparisonItem.Id, + TableName = schemaComparisonItem.TableName, + ColumnName = schemaComparisonItem.ColumnName, + ColumnPosition = schemaComparisonItem.ColumnPosition, + IndexName = schemaComparisonItem.IndexName, + Comment = schemaComparisonItem.Comment + }; + responseData.Items.Add(item); + } + + // Complete response. + responseData.Success = true; + return Json(responseData); + } + + public void OnStartProcess() + { + } + public void SetProgressInfo(List progressInfo) + { + } + public void OnEndProcess() + { + } + public void OnSchemaComparisonItemAdded(SaDbProcess_CompareDatabaseSchemas proc) + { + } + //------------------------------------------------------------------- } //======================================================================= diff --git a/SaxoniteCoreWebAdmin/Admin/Models/Model_Home.cs b/SaxoniteCoreWebAdmin/Admin/Models/Model_Home.cs index e9bd8a7..fd14dcb 100644 --- a/SaxoniteCoreWebAdmin/Admin/Models/Model_Home.cs +++ b/SaxoniteCoreWebAdmin/Admin/Models/Model_Home.cs @@ -15,6 +15,7 @@ namespace Saxonite.Net.Core.Web.Admin.Models public Model_Home(SaxLocaliser localiser) : base(localiser) { + AdditionalFiles_JS.Add("js/sax-home.js"); } //------------------------------------------------------------------- diff --git a/SaxoniteCoreWebAdmin/Localisation/SaxAdminProperties.resx b/SaxoniteCoreWebAdmin/Localisation/SaxAdminProperties.resx index b23439e..668d1de 100644 --- a/SaxoniteCoreWebAdmin/Localisation/SaxAdminProperties.resx +++ b/SaxoniteCoreWebAdmin/Localisation/SaxAdminProperties.resx @@ -159,6 +159,9 @@ Close + + Column + Columns @@ -186,6 +189,9 @@ Forgotten password? + + Index + Indices @@ -273,6 +279,9 @@ System + + Table + Title @@ -441,9 +450,15 @@ System - Database + + Database Schema + System - Logs + + Database schema is valid. + Select User Roles diff --git a/SaxoniteCoreWebAdmin/Views/Home/Index.cshtml b/SaxoniteCoreWebAdmin/Views/Home/Index.cshtml index 7fa4961..cff73a6 100644 --- a/SaxoniteCoreWebAdmin/Views/Home/Index.cshtml +++ b/SaxoniteCoreWebAdmin/Views/Home/Index.cshtml @@ -3,6 +3,31 @@ ViewData["Title"] = "Home Page"; } -
-

Welcome

+ + +
+ +
+
@Localiser["system.caption.database.schema"]
+
+
+
+
+
+
+
+
@(Localiser["label.table"]):
+
+
+
@(Localiser["label.index"]):
+
@(Localiser["label.column"]):
+
+
+
+
+
+
+ diff --git a/SaxoniteCoreWebAdmin/Views/Security/ChangePassword.cshtml b/SaxoniteCoreWebAdmin/Views/Security/ChangePassword.cshtml index c762bde..7551b20 100644 --- a/SaxoniteCoreWebAdmin/Views/Security/ChangePassword.cshtml +++ b/SaxoniteCoreWebAdmin/Views/Security/ChangePassword.cshtml @@ -6,57 +6,60 @@
- @using (@Html.BeginForm(FormMethod.Post, new { @id = "frmChangePassword", @class = "sax-form" })) - { -
@Localiser["security.caption.change.password"]
+
@Localiser["security.caption.change.password"]
+
- @if (!Model.RequestPasswordMode) + @using (@Html.BeginForm(FormMethod.Post, new { @id = "frmChangePassword", @class = "sax-form" })) { - /* Current password - required when setting password within user session. */ + + @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 }) +
+
+
+ } + else + { + /* 'Request Password' encrypted parameters from email link. */ + @Html.HiddenFor(model => model.rpe, new { @id = "ifRpe" }); + } + +
+
-
+
- @Html.PasswordFor(model => model.PasswordCurrent, new { @id = "ifPasswordCurrent", @class = "sax-input", @maxlength = Model.PasswordMaxLength }) + @Html.PasswordFor(model => model.Password, new { @id = "ifPassword", @class = "sax-input", @maxlength = Model.PasswordMaxLength }) +
+
+
@Localiser["label.strength"] 
+
+ +
+
+
+ @Html.PasswordFor(model => model.PasswordConfirm, new { @id = "ifPasswordConfirm", @class = "sax-input", @maxlength = Model.PasswordMaxLength }) +
+
+
+ +
+ +
+
@Localiser["security.caption.change.password"]
+
+ } - else - { - /* 'Request Password' encrypted parameters from email link. */ - @Html.HiddenFor(model => model.rpe, new { @id = "ifRpe" }); - } - -
- -
-
-
- @Html.PasswordFor(model => model.Password, new { @id = "ifPassword", @class = "sax-input", @maxlength = Model.PasswordMaxLength }) -
-
-
@Localiser["label.strength"] 
-
-
-
-
- -
-
-
- @Html.PasswordFor(model => model.PasswordConfirm, new { @id = "ifPasswordConfirm", @class = "sax-input", @maxlength = Model.PasswordMaxLength }) -
-
-
- -
- -
-
@Localiser["security.caption.change.password"]
-
- - } +
diff --git a/SaxoniteCoreWebAdmin/Views/Security/RequestPassword.cshtml b/SaxoniteCoreWebAdmin/Views/Security/RequestPassword.cshtml index e991502..e10b335 100644 --- a/SaxoniteCoreWebAdmin/Views/Security/RequestPassword.cshtml +++ b/SaxoniteCoreWebAdmin/Views/Security/RequestPassword.cshtml @@ -6,27 +6,31 @@
- @using (@Html.BeginForm(FormMethod.Post, new { @id = "frmRequestPassword", @class = "sax-form" })) - { -
@Localiser["security.caption.request.password"]
+
@Localiser["security.caption.request.password"]
-
-
- +
+ + @using (@Html.BeginForm(FormMethod.Post, new { @id = "frmRequestPassword", @class = "sax-form" })) + { + +
+
+ +
+
+ @Html.TextBoxFor(model => model.EmailAddress, new { @id = "ifEmailAddress", @class = "sax-input lower", @maxlength = "128" }) +
+
-
- @Html.TextBoxFor(model => model.EmailAddress, new { @id = "ifEmailAddress", @class = "sax-input lower", @maxlength = "128" }) + +
+ +
+
@Localiser["security.caption.request.password"]
-
-
-
- -
-
@Localiser["security.caption.request.password"]
-
- - } + } +
diff --git a/SaxoniteCoreWebAdmin/Views/Security/Signin.cshtml b/SaxoniteCoreWebAdmin/Views/Security/Signin.cshtml index 5b302c5..b66da66 100644 --- a/SaxoniteCoreWebAdmin/Views/Security/Signin.cshtml +++ b/SaxoniteCoreWebAdmin/Views/Security/Signin.cshtml @@ -8,53 +8,55 @@
- @using (@Html.BeginForm(FormMethod.Post, new { @id = "frmSignIn", @class = "sax-form" })) - { +
@Localiser["security.caption.signin"]
-
@Localiser["security.caption.signin"]
+
-
-
- + @using (@Html.BeginForm(FormMethod.Post, new { @id = "frmSignIn", @class = "sax-form" })) + { + +
+
+ +
+
+ @Html.TextBoxFor(model => model.EmailAddress, new { @id = "ifEmailAddress", @class = "sax-input lower", @maxlength = @Model.GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.UserAccount_EmailAddress) }) +
+
-
- @Html.TextBoxFor(model => model.EmailAddress, new { @id = "ifEmailAddress", @class = "sax-input lower", @maxlength = @Model.GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.UserAccount_EmailAddress) }) -
-
-
-
-
-
-
- +
+
+
+
+ +
+
+
+
-
- +
+ @Html.PasswordFor(model => model.Password, new { @id = "ifPassword", @class = "sax-input", @maxlength = @Model.PasswordMaxLength }) +
+
+
+ +
+ +
+
+ @Html.CheckBoxFor(model => model.KeepMeSignedIn, new { @id = "chkKeepMeSignedIn" }) +   + @Localiser["label.keep.signed.in"]
-
- @Html.PasswordFor(model => model.Password, new { @id = "ifPassword", @class = "sax-input", @maxlength = @Model.PasswordMaxLength }) -
-
-
-
- -
- @Html.CheckBoxFor(model => model.KeepMeSignedIn, new { @id = "chkKeepMeSignedIn" }) -   - @Localiser["label.keep.signed.in"] +
@Localiser["security.caption.signin"]
-
-
-
@Localiser["security.caption.signin"]
-
- - } + } +
-
diff --git a/SaxoniteCoreWebAdmin/Views/Shared/_Layout.cshtml b/SaxoniteCoreWebAdmin/Views/Shared/_Layout.cshtml index 44f26a4..b5895d5 100644 --- a/SaxoniteCoreWebAdmin/Views/Shared/_Layout.cshtml +++ b/SaxoniteCoreWebAdmin/Views/Shared/_Layout.cshtml @@ -54,7 +54,7 @@ } @* Scripts *@ - + @@ -102,9 +102,11 @@