Activity index, Db schema validation.
This commit is contained in:
2021-05-31 20:27:40 +01:00
parent 63473c725b
commit 7b0a710b13
36 changed files with 1008 additions and 208 deletions
@@ -7,7 +7,7 @@ namespace Saxonite.Net.Core.UnitTests.Data.Population
{
//=======================================================================
[TestClass]
class SaxoniteRegionPopulation_Tester
public class SaxoniteRegionPopulation_Tester
{
}
//=======================================================================
@@ -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;
@@ -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,20 +35,38 @@ 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<SaxUserRole> userRoles = new List<SaxUserRole>();
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<SaxSessionData_Online.UserProfileData.SportData> sports = new List<SaxSessionData_Online.UserProfileData.SportData>()
{
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";
@@ -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<SaxSessionData_Online>(
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);
}
//-------------------------------------------------------------------
+23
View File
@@ -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;
}
//-------------------------------------------------------------------
}
@@ -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<ActivityData_Web> activityDataList = new List<ActivityData_Web>();
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<ActivityData_Web> activities)
{
PopulateJournalistArticleDetails(activities);
PopulateOrganisationOpportunitiesDetails(activities);
}
private void PopulateJournalistArticleDetails(List<ActivityData_Web> activities)
{
// Extract article Ids.
List<SysId> 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<ColumnDfn> columns = new List<ColumnDfn>() { 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<ActivityData_Web> activities)
{
// Extract opportunity Ids.
List<SysId> 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<ColumnDfn> columns = new List<ColumnDfn>() { 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.
//-------------------------------------------------------------------
@@ -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
@@ -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<DbSchemaItemResponseData> Items { get; } = new List<DbSchemaItemResponseData>();
}
//-------------------------------------------------------------------
[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<DbDfnTable> 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<SaDbProcess_CompareDatabaseSchemas.SchemaComparisonItem> 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<string> progressInfo)
{
}
public void OnEndProcess()
{
}
public void OnSchemaComparisonItemAdded(SaDbProcess_CompareDatabaseSchemas proc)
{
}
//-------------------------------------------------------------------
}
//=======================================================================
@@ -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");
}
//-------------------------------------------------------------------
@@ -159,6 +159,9 @@
<data name="label.close" xml:space="preserve">
<value>Close</value>
</data>
<data name="label.column" xml:space="preserve">
<value>Column</value>
</data>
<data name="label.columns" xml:space="preserve">
<value>Columns</value>
</data>
@@ -186,6 +189,9 @@
<data name="label.forgotten.password" xml:space="preserve">
<value>Forgotten password?</value>
</data>
<data name="label.index" xml:space="preserve">
<value>Index</value>
</data>
<data name="label.indices" xml:space="preserve">
<value>Indices</value>
</data>
@@ -273,6 +279,9 @@
<data name="label.system" xml:space="preserve">
<value>System</value>
</data>
<data name="label.table" xml:space="preserve">
<value>Table</value>
</data>
<data name="label.title" xml:space="preserve">
<value>Title</value>
</data>
@@ -441,9 +450,15 @@
<data name="system.caption.database" xml:space="preserve">
<value>System - Database</value>
</data>
<data name="system.caption.database.schema" xml:space="preserve">
<value>Database Schema</value>
</data>
<data name="system.caption.logs" xml:space="preserve">
<value>System - Logs</value>
</data>
<data name="system.msg.database.schema.valid" xml:space="preserve">
<value>Database schema is valid.</value>
</data>
<data name="user.caption.select.user.roles" xml:space="preserve">
<value>Select User Roles</value>
</data>
+27 -2
View File
@@ -3,6 +3,31 @@
ViewData["Title"] = "Home Page";
}
<div class="text-center">
<h1 class="display-4">Welcome</h1>
<script>
sax.msg.dbSchemaValid = '@Localiser.GetJavascriptText("system.msg.database.schema.valid")';
</script>
<div class="sax-panel-info sax-panel-results DbSchema">
<div class="sax-header">
<div class="sax-caption">@Localiser["system.caption.database.schema"]</div>
</div>
<div class="sax-content">
<div class="sax-panel-messages"></div>
<div class="sax-panel-item-list"></div>
<div class="sax-panel-item-template">
<div class="sax-panel-item DbSchemaItem">
<div class="sax-header">
<div class="sax-caption SchemaTable">@(Localiser["label.table"]): <span class="TableName"></span></div>
</div>
<div class="sax-content">
<div class="SchemaIndex">@(Localiser["label.index"]): <span class="IndexName"></span></div>
<div class="SChemColumn">@(Localiser["label.column"]): <span class="ColumnName"></span></div>
<div class=""><span class="Comment"></span></div>
</div>
</div>
</div>
</div>
</div>
@@ -6,12 +6,14 @@
<div class="">
<div class="sax-panel-form security">
<div class="sax-header"><div class="sax-caption">@Localiser["security.caption.change.password"]</div></div>
<div class="sax-content">
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmChangePassword", @class = "sax-form" }))
{
<div class="sax-heading"><div class="sax-caption">@Localiser["security.caption.change.password"]</div></div>
@if (!Model.RequestPasswordMode)
{
/* Current password - required when setting password within user session. */
@@ -60,3 +62,4 @@
}
</div>
</div>
</div>
@@ -6,11 +6,14 @@
<div class="">
<div class="sax-panel-form security">
<div class="sax-header"><div class="sax-caption">@Localiser["security.caption.request.password"]</div></div>
<div class="sax-content">
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmRequestPassword", @class = "sax-form" }))
{
<div class="sax-heading"><div class="sax-caption">@Localiser["security.caption.request.password"]</div></div>
<div class="sax-form-control">
<div class="sax-label">
<label for="ifEmailAddress"><span><span class="required">*</span> @Localiser["label.email.address"]</span></label>
@@ -30,3 +33,4 @@
}
</div>
</div>
</div>
@@ -8,11 +8,13 @@
<div class="sax-panel-form security">
<div class="sax-header"><div class="sax-caption">@Localiser["security.caption.signin"]</div></div>
<div class="sax-content">
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmSignIn", @class = "sax-form" }))
{
<div class="sax-heading"><div class="sax-caption">@Localiser["security.caption.signin"]</div></div>
<div class="sax-form-control">
<div class="sax-label">
<label for="ifEmailAddress"><span><span class="required">*</span> @Localiser["label.email.address"]</span></label>
@@ -56,5 +58,5 @@
}
</div>
</div>
</div>
@@ -102,9 +102,11 @@
</button>
<div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
<ul class="navbar-nav flex-grow-1">
@*
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">@Localiser["home.caption.home"]</a>
</li>
*@
@if (Model.AwaitingLogin)
{
<li class="nav-item">
@@ -179,7 +181,7 @@
@* Footer *@
<footer class="border-top footer text-muted">
<div class="container">
@(Model.CopyrightNotice) - @Localiser["company.name"] @(Model.InstanceVersion) - <a asp-area="" asp-controller="Home" asp-action="">Home</a> - <a asp-area="" asp-controller="Policies" asp-action="Privacy">Privacy</a>
@(Model.CopyrightNotice) - @Localiser["company.name"] @(Model.InstanceVersion) - <a asp-area="" asp-controller="Home" asp-action="">Home</a>
</div>
</footer>
+12 -7
View File
@@ -108,7 +108,7 @@
overflow: hidden;
}
.sax-panel-item .sax-header
.sax-panel-item > .sax-header
{
padding: 2px 8px;
min-height: 24px;
@@ -116,40 +116,45 @@
border-bottom: solid 2px #979797;
}
.sax-panel-item .sax-header .sax-caption
.sax-panel-item > .sax-header .sax-caption
{
margin-top: 4px;
font-size: 1.2rem;
font-weight: bold;
}
.sax-panel-item .sax-header .sax-action
.sax-panel-item > .sax-header .sax-action
{
border-radius: 4px;
}
.sax-panel-item .sax-header .sax-caption .sax-action:hover
.sax-panel-item >.sax-header .sax-caption .sax-action:hover
{
color: var(--color-light);
}
.sax-panel-item .sax-header .sax-actions .sax-action
.sax-panel-item > .sax-header .sax-actions .sax-action
{
margin: 2px 0 0 8px;
}
.sax-panel-item .sax-header .sax-actions .sax-action:hover
.sax-panel-item > .sax-header .sax-actions .sax-action:hover
{
background-color: #c0c0c0;
box-shadow: 0 0 4px 2px #c0c0c080;
}
.sax-panel-item .sax-content
.sax-panel-item > .sax-content
{
margin: 4px 8px;
padding: 4px 4px;
}
.sax-panel-item .sax-note
{
font-size: 0.8rem;
}
@media (min-width: 576px)
{
.sax-panel-filter
+81 -6
View File
@@ -8,12 +8,14 @@
{
max-width: 720px;
margin: 24px auto;
padding: 24px 24px;
padding: 0;
background-color: #fff;
border: solid 2px #979797;
border-color: var(--color-primary);
border-radius: 4px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
overflow: hidden;
}
.sax-panel-form.security
@@ -28,22 +30,95 @@
}
}
.sax-panel-info > .sax-header,
.sax-panel-form > .sax-header
{
padding: 12px 12px;
text-align: center;
background-color: #e8e8e8;
border-bottom: solid 1px #606060;
border-color: var(--color-primary);
}
.sax-panel-info > .sax-header .sax-caption,
.sax-panel-form > .sax-header .sax-caption
{
font-size: 1.5rem;
font-weight: bold;
color: var(--color-primary-dark);
}
.sax-panel-info > .sax-header .sax-action img:hover,
.sax-panel-form > .sax-header .sax-action img:hover
{
background-color: #f0f0f0;
border-radius: 4px;
box-shadow: 2px 2px 4px 0 #80808080;
}
.sax-panel-info > .sax-content,
.sax-panel-form > .sax-content
{
max-height: 400px;
padding: 12px 12px;
background-color: #f8f8f8;
overflow-y: auto;
}
.sax-panel-section.sax-edit .sax-panel-info,
.sax-panel-section.sax-populating .sax-button-action.edit,
.sax-panel-section .sax-panel-form
{
display: none;
}
.sax-panel-section.sax-edit .sax-panel-form
{
display: block;
}
/* Form */
.sax-form
{
}
.sax-form .sax-heading
/* Labels, values, notes */
.sax-panel-info .sax-label, .sax-panel-info .sax-value
{
margin-bottom: 20px;
margin-bottom: 6px;
font-size: 1rem;
}
.sax-form .sax-heading .sax-caption
.sax-panel-info .sax-label
{
font-size: 1.5rem;
padding-right: 16px;
font-weight: bold;
color: var(--color-primary);
color: #808080;
}
.sax-panel-info .sax-value
{
}
.sax-form .sax-form-control .sax-label
{
margin: 0 0 8px 0;
font-size: 1rem;
line-height: 1.25;
}
.sax-form .sax-form-control .sax-label-text
{
font-size: 1rem;
line-height: 1.25;
}
.sax-form .sax-form-control .sax-note
{
font-size: 0.9rem;
line-height: 1.2;
}
/* Controls */
@@ -0,0 +1,65 @@
/*
* Admin - Home page scripts.
*/
(function (win, doc, undef)
{
"use strict";
sax.itemIdPrefix = 'Itm_';
//--------------------------------------------------------------
sax.refreshDbSchema= function (delay)
{
var $results = $('.sax-panel-results.DbSchema');
if (!sax.awaitingRefreshResponse)
{
var $items = $('.sax-panel-item-list', $results).fadeOut(200).empty();
var $msgs = $('.sax-panel-messages', $results).hide().empty().append($('<div class="sax-message">' + sax.msg.dataQuerying + '</div>')).fadeIn(200);
}
sax.ajaxDbSchema = sax.ajaxDbSchema || { url: sax.url.base + 'System/Ajax/ValidateDatabaseSchema', formId: 'frmNavigate', callbackFn: sax.handleDbSchemaResponse, noWaitIcon: true };
sax.doAjaxRequest(sax.ajaxDbSchema, delay || 50);
sax.awaitingRefreshResponse = true;
}
sax.handleDbSchemaResponse = function (response)
{
sax.debug('handleDbSchemaResponse', response);
delete sax.awaitingRefreshResponse;
var $results = $('.sax-panel-results.DbSchema');
var $msgs = $('.sax-panel-messages', $results).hide().empty();
var $items = $('.sax-panel-item-list', $results).hide().empty();
var $tmpl = $('.sax-panel-item-template .sax-panel-item.DbSchemaItem', $results);
if (!response.success) return;
// Items.
var items = response.items;
if (!items.length)
return $msgs.append($('<div class="sax-message">' + sax.msg.dbSchemaValid + '</div>')).fadeIn(500);
for (let itemIdx = 0; itemIdx < items.length; itemIdx++)
{
let item = items[itemIdx];
let $item = $tmpl.clone();
sax.populateItem($item, item);
$items.append($item);
}
$items.fadeIn(50);
}
sax.populateItem = function ($item, item)
{
if (!item) return;
$item.prop('id', sax.itemIdPrefix + item.id);
$('.TableName', $item).text(item.tableName);
$('.IndexName', $item).text(item.indexName);
$('.SchemaIndex', $item).toggle(!!item.indexName);
$('.ColumnName', $item).text(item.columnName);
$('.SchemaColumn', $item).toggle(!!item.columnName);
$('.Comment', $item).text(item.comment);
}
//--------------------------------------------------------------
$(function ()
{
setTimeout(sax.refreshDbSchema, 250);
});
}(window, window.document));
/* The End */
@@ -159,6 +159,9 @@
<data name="dialog.title.signout" xml:space="preserve">
<value>Sign-out</value>
</data>
<data name="home.caption.activities" xml:space="preserve">
<value>Activities</value>
</data>
<data name="home.caption.application" xml:space="preserve">
<value>Online</value>
</data>
@@ -216,6 +219,9 @@
<data name="label.delete" xml:space="preserve">
<value>Delete</value>
</data>
<data name="label.effective" xml:space="preserve">
<value>Effective</value>
</data>
<data name="label.email.address" xml:space="preserve">
<value>Email address</value>
</data>
@@ -1,11 +1,15 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Core;
using Saxonite.Net.Core.Web.Base.App;
using Saxonite.Net.Standard.Base.App;
using Saxonite.Net.Standard.Base.Bus;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Online.App
@@ -32,6 +36,54 @@ namespace Saxonite.Net.Core.Web.Online.App
/// <summary>User - JSON deserialisation route.</summary>
public new UserData User { get { return base.User; } set { SetUser(value); } }
//-------------------------------------------------------------------
// User Profile.
// Protected constructors & setters for JSON deserialisation.
public class UserProfileData
{
public UserProfileData() { }
// Sports.
public class SportData
{
public SportData(SysId sportId, SaStatus status)
{
SportId = sportId;
Status = status;
}
public SysId SportId { get; protected set; }
public SaStatus Status { get; protected set; }
}
private class SportData_Online : SportData
{
// Internal class to allow values to be updated.
public SportData_Online(SysId sportId, SaStatus status) : base(sportId, status) { }
public SportData_Online(SportData sportData) : base(sportData.SportId, sportData.Status) { }
public void SetSportId(SysId sportId) { SportId = sportId; }
public void SetStatus(SaStatus status) { Status = status; }
}
[JsonIgnore]
private readonly List<SportData_Online> sports = new List<SportData_Online>();
[JsonIgnore]
public int SportCount { get { return sports.Count; } }
public List<SportData> Sports { get { return new List<SportData>(sports); } protected set { sports.Clear(); sports.AddRange(value.Select(item => new SportData_Online(item))); } }
public void AddSport(SysId sportId, SaStatus status)
{
if (!SysId.IsValid(sportId)) throw new NotSupportedException("Session Profile Sport must have a valid Id");
SportData_Online sport = this.sports.Find(item => item.SportId == sportId);
if (sport != null)
{
sport.SetStatus(status);
}
else
this.sports.Add(new SportData_Online(sportId, status));
}
public void ClearSports()
{
this.sports.Clear();
}
}
public UserProfileData Profile { get; protected set; } = new UserProfileData();
//-------------------------------------------------------------------
// Filter properties.
public class FilterData
@@ -69,6 +121,25 @@ namespace Saxonite.Net.Core.Web.Online.App
}
public FilterData Filter { get; private set; } = new FilterData();
//-------------------------------------------------------------------
// JSON Deserialisation.
[JsonExtensionData]
#pragma warning disable IDE0044 // Add readonly modifier
private IDictionary<string, JToken> _additionalData;
#pragma warning restore IDE0044 // Add readonly modifier
[OnDeserialized]
private void OnDeserialized(StreamingContext context)
{
if (_additionalData != null)
{
foreach (String name in _additionalData.Keys)
{
JToken value = _additionalData[name];
SaLogger.Info("OnDeserialized Name: " + name + ". Value: " + value.ToString());
}
}
}
//-------------------------------------------------------------------
}
//=======================================================================
@@ -5,6 +5,7 @@ using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Dbas;
using Saxonite.Net.Core.Web.Base.App;
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;
@@ -32,19 +33,12 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
public class ActivityResponseData
{
public short ItemType { get; set; }
// NB Need to mask the Item Id by storing with XRef to alternative generated value in User's session.
public int ItemIdx { get; set; }
public long ItemIdx { get; set; }
public String Caption { get; set; }
}
public class ArticleActivityResponseData : ActivityResponseData
{
public String Headline { get; set; }
public String Summary { get; set; }
}
public class OpportunityActivityResponseData : ActivityResponseData
{
public String Position { get; set; }
public String Outline { get; set; }
public String Blurb { get; set; }
public String Sport { get; set; }
public String Region { get; set; }
public String PublishedOnFmt{ get; set; }
}
//-------------------------------------------------------------------
@@ -52,7 +46,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
//-------------------------------------------------------------------
public class AjaxResponse_ActivityList : AjaxResponse_Base
{
public List<SaxModel_Online.RegionData> Regions { get; } = new List<SaxModel_Online.RegionData>();
public List<ActivityResponseData> Activities { get; } = new List<ActivityResponseData>();
}
//-------------------------------------------------------------------
[HttpPost]
@@ -70,13 +64,34 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
return Forbid();
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
SaxSessionData_Online sessionData = SessionData_Online;
// User context.
SaxActivityConsole.IndexContext indexContext = new SaxActivityConsole.IndexContext();
// Regions.
if (SysId.IsValid(sessionData.User.RegionIdProvince))
indexContext.RegionIds.Add(sessionData.User.RegionIdProvince);
else if (SysId.IsValid(sessionData.User.RegionIdCountry))
indexContext.RegionIds.Add(sessionData.User.RegionIdCountry);
// Sports.
indexContext.SportIds.AddRange(sessionData.Profile.Sports.Select(item => item.SportId));
// Populate activities.
SaxActivityConsole.IndexContext indexContext = new SaxActivityConsole.IndexContext();
SaxActivityConsole.IndexResults indexResults = ActivityConsole.QueryIndex(indexContext);
foreach (SaxActivityConsole.IndexResults.ItemData itemData in indexResults.Items)
foreach (SaxActivityConsole.IndexResults.ActivityData itemData in indexResults.Items)
{
ActivityResponseData activityData = new ActivityResponseData();
activityData.ItemType = itemData.ItemId.Type;
// ToDo Mask activity index Id with cross-reference stored in session data.
activityData.ItemIdx = SysId.ToLong(itemData.ItemId.Id);
activityData.Caption = itemData.Caption;
activityData.Blurb = itemData.Blurb;
activityData.Sport = Localiser.GetSportName(itemData.SportId, itemData.SportName);
activityData.Region = Localiser.GetRegionName(itemData.RegionId, itemData.RegionName);
activityData.PublishedOnFmt = Localiser.FormatDate(itemData.EffectiveOn);
responseData.Activities.Add(activityData);
}
// Complete response.
@@ -2,6 +2,7 @@
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.Utils;
using Saxonite.Net.Core.Web.Base.App;
@@ -86,6 +87,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
// Create session.
OpenUserSession(SessionContext, userAccount, userRoles);
RefreshSessionProfileSports();
// Send the 'Keep Logged In' cookie if requested.
if (userAccount.KeepLoggedIn_Online)
@@ -94,6 +96,39 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
return true;
}
//-------------------------------------------------------------------
/// <summary>Populate the user's profile sports to the session.</summary>
protected bool RefreshSessionProfileSports()
{
String fn = GetType().Name + ".RefreshSessionProfileSports";
SaxSessionData_Online sessionData = SessionData_Online;
SysId userAccountId = sessionData?.User.UserAccountId ?? null;
if (!SysId.IsValid(userAccountId)) return false;
sessionData.Profile.ClearSports();
// Read discrete values to avoid loading the entire UserSport records which can be substantial.
ColumnDfn colSportId = AccountMgr.GetUserSportColumnDfn(SaxUserSport.Column.SportId);
ColumnDfn colSportName = AccountMgr.GetUserSportColumnDfn(SaxUserSport.Column.Sport_Name);
ColumnDfn colStatus = AccountMgr.GetUserSportColumnDfn(SaxUserSport.Column.Status);
SaxUserSport.Params prmUserSport = new SaxUserSport.Params();
prmUserSport.UserAccountIdsIncluded.Add(userAccountId);
prmUserSport.StatusCat = SaxUserSport.StatusCat.ActiveOnly;
using (DbResultSet resultSet = AccountMgr.ExecuteUserSportQuery(new List<ColumnDfn>() { colSportId, colSportName, colStatus }, prmUserSport))
{
while (resultSet != null && resultSet.NextRecord())
{
NamedValue.Map values = resultSet.GetRecordValues();
SysId sportId = new SysId(values.GetLong(colSportId.DataName));
String sportName = values.GetString(colSportName.DataName);
SaStatus status = SacsSystem.GetStatusForCode(values.GetString(colStatus.DataName));
sessionData.Profile.AddSport(sportId, status);
}
}
SaLogger.Debug(fn+" Profile Sports: " + sessionData.Profile.SportCount);
return true;
}
//-------------------------------------------------------------------
/// <summary>Start a new user session on sign-in.</summary>
protected bool OpenUserSession(SaxSessionContext sessionContext, SaxUserAccount userAccount, List<SaxUserRole> userRoles)
@@ -56,9 +56,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
String errorMessage = "";
if (!SignUserIn(userAccount, AccountMgr.GetRolesForUser(userAccount.Id), model.KeepMeSignedIn, ref errorMessage))
{
return Json(responseData.SetControlError(nameof(model.EmailAddress), errorMessage));
}
// Construct response.
responseData.Success = SessionData_Online?.ValidUserSession ?? false;
@@ -15,6 +15,8 @@ namespace Saxonite.Net.Core.Web.Online.Models
public Model_Home(SaxLocaliser localiser) :
base(localiser)
{
AdditionalFiles_CSS.Add("css/sax-home.css");
AdditionalFiles_JS.Add("js/sax-home.js");
}
//-------------------------------------------------------------------
@@ -4,6 +4,55 @@
ViewData["Title"] = "Home";
}
@if (Model.AwaitingLogin)
{
<div class="text-center">
<h1 class="display-4">@Localiser["home.caption.welcome"]</h1>
</div>
}
else
{
<div class="sax-panel-filter">
<div class="sax-header row">
<div class="col"><div class="sax-caption">@Localiser["home.caption.activities"]</div></div>
<div class="fixed row">
</div>
</div>
<div class="sax-content">
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmActivityFilter", @class = "sax-form", @action = "#", @method = "POST", onsubmit = "return false;" }))
{
<div class="row">
<div class="col-sm-4"><div class="sax-label text-left text-sm-right">@Localiser["label.search"]</div></div>
<div class="col-sm-8"><input type="search" name="Filter.Activity" id="ifActivityFilter" class="ActivityFilter sax-input" maxlength="64" /></div>
</div>
}
</div>
</div>
<div class="sax-panel-results">
<div class="sax-panel-messages"></div>
<div class="sax-panel-item-list"></div>
<div class="sax-panel-item-template">
<div class="sax-panel-item Activity">
<div class="sax-header row">
<div class="col">
<div class="sax-caption"><div class="sax-action ActivityCaption"></div></div>
</div>
<div class="fixed sax-actions">
</div>
</div>
<div class="sax-content">
<div class="row">
<div class="col-sm-12"><div class="ActivityBlurb"></div></div>
<div class="col-sm-6">
<div class="sax-note">
<span class="ActivitySport"></span> - <span class="ActivityRegion"></span>
</div>
</div>
<div class="col-sm-6 right"><div class="sax-note ActivityPublished">@Localiser["label.published"]:&nbsp;<span class="ActivityPublishedOn"></span></div></div>
</div>
</div>
</div>
</div>
</div>
}
+10 -5
View File
@@ -122,29 +122,29 @@
border-bottom: solid 1px #979797;
}
.sax-panel-item .sax-caption
.sax-panel-item > .sax-header .sax-caption
{
margin-top: 4px;
font-size: 1.2rem;
font-weight: bold;
}
.sax-panel-item .sax-header .sax-action
.sax-panel-item > .sax-header .sax-action
{
border-radius: 4px;
}
.sax-panel-item .sax-header .sax-caption .sax-action:hover
.sax-panel-item > .sax-header .sax-caption .sax-action:hover
{
color: var(--color-primary);
}
.sax-panel-item .sax-header .sax-actions .sax-action
.sax-panel-item > .sax-header .sax-actions .sax-action
{
margin: 2px 0 0 8px;
}
.sax-panel-item .sax-header .sax-actions .sax-action:hover
.sax-panel-item > .sax-header .sax-actions .sax-action:hover
{
background-color: #c0c0c0;
box-shadow: 0 0 4px 2px #c0c0c080;
@@ -156,6 +156,11 @@
padding: 4px 4px;
}
.sax-panel-item .sax-note
{
font-size: 0.8rem;
}
/* Profile Sports */
.sax-panel-item.ProfileSport,
@@ -0,0 +1,6 @@
/*
Home stylesheet.
*/
/* The End */
@@ -0,0 +1,70 @@
/*
* Home scripts.
*/
(function (win, doc, undef)
{
"use strict";
//--------------------------------------------------------------
sax.refreshResults = function (delay)
{
var $results = $('.sax-panel-results');
if (!sax.awaitingRefreshResponse)
{
var $items = $('.sax-panel-item-list', $results).fadeOut(200).empty();
var $msgs = $('.sax-panel-messages', $results).hide().empty().append($('<div class="sax-message">' + sax.msg.dataQuerying + '</div>')).fadeIn(200);
}
sax.ajaxRefreshResults = sax.ajaxRefreshResults || { url: sax.url.base + 'Activity/Ajax/GetList', formId: 'frmActivityFilter', callbackFn: sax.doRefreshResults };
sax.doAjaxRequest(sax.ajaxRefreshResults, delay || 50);
sax.awaitingRefreshResponse = true;
}
sax.doRefreshResults = function (response)
{
sax.debug('doRefreshResults', response);
delete sax.awaitingRefreshResponse;
var $results = $('.sax-panel-results');
var $msgs = $('.sax-panel-messages', $results).hide().empty();
var $items = $('.sax-panel-item-list', $results).hide().empty();
var $tmpl = $('.sax-panel-item-template .sax-panel-item', $results);
if (!response.success) return;
var items = response.activities;
if (!items.length)
return $msgs.append($('<div class="sax-message">' + sax.msg.dataNoneFound + '</div>')).fadeIn(500);
for (let itemIdx = 0; itemIdx < items.length; itemIdx++)
{
let item = items[itemIdx];
let $item = $tmpl.clone();
sax.populateItem($item, item);
$items.append($item);
}
$items.fadeIn(50);
}
sax.populateItem = function ($item, item)
{
if (!item) return;
$item.prop('id', sax.itemIdPrefix + item.id);
$('.ActivityCaption', $item).text(item.caption);
$('.ActivityBlurb', $item).text(item.blurb);
$('.ActivitySport', $item).text(item.sport);
$('.ActivityRegion', $item).text(item.region);
$('.ActivityPublishedOn', $item).text(item.publishedOnFmt);
$item.off('click').on('click', function () { sax.expandItem(item.id) })
$('.sax-panel-results .sax-panel-messages').hide().empty();
}
//--------------------------------------------------------------
sax.expandItem = function (id)
{
sax.debug('expandItem(' + id + ')');
}
//--------------------------------------------------------------
$(function ()
{
setTimeout(sax.refreshResults, 250);
});
}(window, window.document));
/* The End */
@@ -49,7 +49,6 @@
sax.populateItem($item, item);
$items.append($item);
}
$('.sax-action.sax-item-update', $items).toggle(sax.data.userCanManageOpportunities);
$items.fadeIn(50);
}
sax.populateItem = function ($item, item)
@@ -67,7 +66,8 @@
$('.OpportunityExpiresOn', $item).text(item.expiresOnFmt);
$('.OpportunityExpires', $item).toggle(!!item.expiresOnFmt);
$('.OpportunityStatusDesc', $item).text(item.statusDesc);
$('.sax-caption .sax-action, .sax-actions .sax-action.sax-item-update', $item).off('click').on('click', function () { sax.updateItem(item.id) });
if (sax.data.userCanManageOpportunities)
$('.sax-caption .sax-action, .sax-actions .sax-action.sax-item-update', $item).off('click').on('click', function () { sax.updateItem(item.id) }).removeClass('hidden');
$item.off('dblclick').on('dblclick', function () { sax.updateItem(item.id) });
$('.sax-panel-results .sax-panel-messages').hide().empty();
}
+4 -4
View File
@@ -18,7 +18,7 @@ namespace Saxonite.Net.Standard.Base.App
//-------------------------------------------------------------------
public static class Version
{
private static String version { get; } = "1.0.5.0";
private static String version { get; } = "1.0.5.3";
// Retrieve version.
public static String GetFullVersion () { return version; }
public static String GetAppVersion ()
@@ -39,9 +39,9 @@ namespace Saxonite.Net.Standard.Base.App
//-------------------------------------------------------------------
// Version history.
// 1.0.5 ?-May-2021 Activity Index
// - Admin Database page.
// - Online Account country.
// 1.0.5 31-May-2021 Activity Index
// - Admin Database Schema (Home) and Tables page.
// - Online Account country, Home activities.
// 1.0.4 20-May-2021 Online profile sports & opportunities
// - Admin Organisation Region & Sport.
// - Online Profile Sports - simple list with name.
@@ -69,6 +69,8 @@ namespace Saxonite.Net.Standard.Base.App
LastName = userAccount.LastName;
DisplayName = userAccount.GetDisplayName();
EmailAddress = userAccount.EmailAddress;
RegionIdCountry = userAccount.RegionIdCountry;
RegionIdProvince = userAccount.RegionIdProvince;
if (sessionContext.Application == Sax.Application.Admin)
KeepLoggedIn = userAccount.KeepLoggedIn_Admin;
else if (sessionContext.Application == Sax.Application.Online)
@@ -87,12 +89,13 @@ namespace Saxonite.Net.Standard.Base.App
public String LastName { get; protected set; }
public String DisplayName { get; protected set; }
public String EmailAddress { get; protected set; }
public SysId RegionIdCountry { get; protected set; }
public SysId RegionIdProvince { get; protected set; }
public bool KeepLoggedIn { get; protected set; }
public bool UserAccountSupplied { get; protected set; }
// Roles.
public class RoleData
{
private RoleData() { }
public RoleData(SaxUserRole userRole)
{
if (userRole != null)
@@ -107,13 +110,6 @@ namespace Saxonite.Net.Standard.Base.App
[JsonIgnore]
private readonly List<RoleData> roles = new List<RoleData>();
public List<RoleData> Roles { get { return new List<RoleData>(roles); } protected set { roles.Clear(); roles.AddRange(value); } }
/// <summary>User - JSON deserialisation route.</summary>
protected void SetRoles(List<RoleData> roles)
{
if (this.roles.Count > 0)
throw new NotSupportedException("Session Data Roles cannot be replaced.");
this.roles.AddRange(roles);
}
[JsonIgnore]
public bool IsJournalist { get { return roles?.Exists(item => item.Role == Sax.UserRole.Journalist) ?? false; } }
}
@@ -122,7 +118,7 @@ namespace Saxonite.Net.Standard.Base.App
protected void SetUser(UserData user)
{
if (User != null && User.UserAccountSupplied)
throw new NotSupportedException("Session Data User cannot be replaced.");
throw new NotSupportedException("Session User cannot be replaced.");
User = user;
}
@@ -23,6 +23,12 @@ namespace Saxonite.Net.Standard.Base.Bus
ItemType_Code,
ItemType_Desc,
ItemId,
// Journalist Article.
Article_Headline,
Article_Summary,
// Organisation opportunity.
Opportunity_Position,
Opportunity_Outline,
// Sport.
SportId,
Sport_Name,
@@ -44,21 +44,28 @@ namespace Saxonite.Net.Standard.Base.Consoles
/// </summary>
public class IndexResults
{
public class ItemData
public class ActivityData
{
public ItemData(ItemId itemId, DateTime effectiveOn)
public ActivityData(ItemId itemId, DateTime effectiveOn)
{
ItemId = itemId;
EffectiveOn = effectiveOn;
}
public ItemId ItemId { get; }
public DateTime EffectiveOn { get; }
public String Caption { get; protected set; }
public String Blurb { get; protected set; }
public SysId SportId{ get; protected set; }
public String SportName{ get; protected set; }
public SysId RegionId { get; protected set; }
public String RegionName{ get; protected set; }
}
public List<ItemData> Items { get; } = new List<ItemData>();
public List<ActivityData> Items { get; } = new List<ActivityData>();
}
//-------------------------------------------------------------------
// Data members.
private SaxInternals internals = null;
private readonly List<Sax.DataItemType> validActivityItemTypes = new List<Sax.DataItemType>()
{
Sax.DataItemType.JournalistArticle,
@@ -75,12 +82,14 @@ namespace Saxonite.Net.Standard.Base.Consoles
{
if (disposing)
{
if (internals != null) internals.Dispose(); internals = null;
}
}
//-------------------------------------------------------------------
// Properties.
protected SaxSessionContext SessionContext { get; }
protected SaxInternals Internals { get { if (internals == null) internals = new SaxInternals(SessionContext); return internals; } }
protected List<Sax.DataItemType> ValidActivityItemTypes { get { return validActivityItemTypes.ToList(); } }
//-------------------------------------------------------------------
@@ -239,8 +239,12 @@ namespace Saxonite.Net.Standard.Dbas.Mgr
//-------------------------------------------------------------------
// Item column definitions.
private static readonly String aliasActivityIndex = "ActIdx";
private static readonly String aliasActivityIndex_Region = "ActIdx_Reg";
private static readonly String prefixActivityIndex_Region = "ActIdx_Reg_";
private static readonly String aliasActivityIndex_Article = "ActIdx_JrnArt";
private static readonly String prefixActivityIndex_Article = "ActIdx_JrnArt_";
private static readonly String aliasActivityIndex_Opportunity = "ActIdx_OrgOpp";
private static readonly String prefixActivityIndex_Opportunity = "ActIdx_OrgOpp_";
private static readonly String aliasActivityIndex_Region = "ActIdx_Rgn";
private static readonly String prefixActivityIndex_Region = "ActIdx_Rgn_";
private static readonly String aliasActivityIndex_Sport = "ActIdx_Spt";
private static readonly String prefixActivityIndex_Sport = "ActIdx_Spt_";
private void SetActivityIndexColDfn
@@ -287,13 +291,24 @@ namespace Saxonite.Net.Standard.Dbas.Mgr
break;
//-----------------------------------------------------------
// Region.
case SaxActivityIndex.Column.RegionId:
SetActivityIndexColDfn(columnDfn, "Region Id", 80, SaxDbDfn_Core.ColumnIdx.ActivityIndex_RegionId, ColumnAlignment.Right, DataValue.DataType.Long);
// Article.
case SaxActivityIndex.Column.Article_Headline:
SetActivityIndexColDfn(columnDfn, "Headline", 160, SaxDbDfn_Core.ColumnIdx.JournalistArticle_Headline, ColumnAlignment.Left, DataValue.DataType.String, aliasActivityIndex_Article, prefixActivityIndex_Article);
columnDfn.ReadOnly = true;
break;
case SaxActivityIndex.Column.Region_Name:
SetActivityIndexColDfn(columnDfn, "Region", 100, SaxDbDfn_Core.ColumnIdx.Region_Name, ColumnAlignment.Left, DataValue.DataType.String, aliasActivityIndex_Region, prefixActivityIndex_Region);
case SaxActivityIndex.Column.Article_Summary:
SetActivityIndexColDfn(columnDfn, "Summary", 240, SaxDbDfn_Core.ColumnIdx.JournalistArticle_Summary, ColumnAlignment.Left, DataValue.DataType.String, aliasActivityIndex_Article, prefixActivityIndex_Article);
columnDfn.ReadOnly = true;
break;
//-----------------------------------------------------------
// Opportunity.
case SaxActivityIndex.Column.Opportunity_Position:
SetActivityIndexColDfn(columnDfn, "Position", 160, SaxDbDfn_Core.ColumnIdx.OrganisationOpportunity_Position, ColumnAlignment.Left, DataValue.DataType.String, aliasActivityIndex_Opportunity, prefixActivityIndex_Opportunity);
columnDfn.ReadOnly = true;
break;
case SaxActivityIndex.Column.Opportunity_Outline:
SetActivityIndexColDfn(columnDfn, "Outliner", 240, SaxDbDfn_Core.ColumnIdx.OrganisationOpportunity_Outline, ColumnAlignment.Left, DataValue.DataType.String, aliasActivityIndex_Opportunity, prefixActivityIndex_Opportunity);
columnDfn.ReadOnly = true;
break;
@@ -308,6 +323,17 @@ namespace Saxonite.Net.Standard.Dbas.Mgr
columnDfn.ReadOnly = true;
break;
//-----------------------------------------------------------
// Region.
case SaxActivityIndex.Column.RegionId:
SetActivityIndexColDfn(columnDfn, "Region Id", 80, SaxDbDfn_Core.ColumnIdx.ActivityIndex_RegionId, ColumnAlignment.Right, DataValue.DataType.Long);
columnDfn.ReadOnly = true;
break;
case SaxActivityIndex.Column.Region_Name:
SetActivityIndexColDfn(columnDfn, "Region", 100, SaxDbDfn_Core.ColumnIdx.Region_Name, ColumnAlignment.Left, DataValue.DataType.String, aliasActivityIndex_Region, prefixActivityIndex_Region);
columnDfn.ReadOnly = true;
break;
//-----------------------------------------------------------
// Properties.
case SaxActivityIndex.Column.EffectiveOn:
@@ -520,6 +546,38 @@ namespace Saxonite.Net.Standard.Dbas.Mgr
if (columns.HasId(SaxActivityIndex.Column.UpdateOn_Date))
query.Columns.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.ActivityIndex_UpdateOn, aliasActivityIndex));
//---------------------------------------------------------------------
// Article.
if (columns.HasId(SaxActivityIndex.Column.Article_Headline) || columns.HasId(SaxActivityIndex.Column.Article_Summary))
{
DbQueryJoin joinArticle = new DbQueryJoin(DbJoinType.LeftOuter);
joinArticle.Table = new StringPair(SaxDbDfn_Core.GetTableName(SaxDbDfn_Core.TableIdx.JournalistArticle), aliasActivityIndex_Article);
joinArticle.Filters.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.ActivityIndex_ItemType, aliasActivityIndex) + "=" + ((int)Sax.DataItemType.JournalistArticle).ToString());
joinArticle.Filters.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.JournalistArticle_Id, aliasActivityIndex_Article) + "=" + SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.ActivityIndex_ItemId, aliasActivityIndex));
query.Joins.Add(joinArticle);
List<SaxDbDfn_Core.ColumnIdx> cols = new List<SaxDbDfn_Core.ColumnIdx>();
cols.Add(SaxDbDfn_Core.ColumnIdx.JournalistArticle_Headline);
cols.Add(SaxDbDfn_Core.ColumnIdx.JournalistArticle_Summary);
foreach (var col in cols)
query.Columns.Add(SaxDbDfn_Core.GetColumnNameQual(col, aliasActivityIndex_Article), prefixActivityIndex_Article + SaxDbDfn_Core.GetColumnName(col, false));
}
//---------------------------------------------------------------------
// Opportunity.
if (columns.HasId(SaxActivityIndex.Column.Opportunity_Position) || columns.HasId(SaxActivityIndex.Column.Opportunity_Outline))
{
DbQueryJoin joinOpportunity = new DbQueryJoin(DbJoinType.LeftOuter);
joinOpportunity.Table = new StringPair(SaxDbDfn_Core.GetTableName(SaxDbDfn_Core.TableIdx.OrganisationOpportunity), aliasActivityIndex_Opportunity);
joinOpportunity.Filters.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.ActivityIndex_ItemType, aliasActivityIndex) + "=" + ((int)Sax.DataItemType.OrganisationOpportunity).ToString());
joinOpportunity.Filters.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.OrganisationOpportunity_Id, aliasActivityIndex_Opportunity) + "=" + SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.ActivityIndex_ItemId, aliasActivityIndex));
query.Joins.Add(joinOpportunity);
List<SaxDbDfn_Core.ColumnIdx> cols = new List<SaxDbDfn_Core.ColumnIdx>();
cols.Add(SaxDbDfn_Core.ColumnIdx.OrganisationOpportunity_Position);
cols.Add(SaxDbDfn_Core.ColumnIdx.OrganisationOpportunity_Outline);
foreach (var col in cols)
query.Columns.Add(SaxDbDfn_Core.GetColumnNameQual(col, aliasActivityIndex_Opportunity), prefixActivityIndex_Opportunity + SaxDbDfn_Core.GetColumnName(col, false));
}
//---------------------------------------------------------------------
// Sport.
if (columns.HasId(SaxActivityIndex.Column.Sport_Name))
@@ -248,8 +248,8 @@ namespace Saxonite.Net.Standard.Dbas.Mgr
private static readonly String prefixJournalistArticle_Jourlalist = "JrnArt_Jrn_";
private static readonly String aliasJournalistArticle_Sport = "JrnArt_Spt";
private static readonly String prefixJournalistArticle_Sport = "JrnArt_Spt_";
private static readonly String aliasJournalistArticle_Region = "JrnArt_Reg";
private static readonly String prefixJournalistArticle_Region = "JrnArt_Reg_";
private static readonly String aliasJournalistArticle_Region = "JrnArt_Rgn";
private static readonly String prefixJournalistArticle_Region = "JrnArt_Rgn_";
private void SetJournalistArticleColDfn
(ColumnDfn col, String caption, int width, SaxDbDfn_Core.ColumnIdx dbCol,
ColumnAlignment align = ColumnAlignment.Default, DataValue.DataType dataType = DataValue.DataType.String,
@@ -253,8 +253,8 @@ namespace Saxonite.Net.Standard.Dbas.Mgr
private static readonly String aliasOrganisationOpportunity_Organisation_UserOrganisation = "OrgOpp_Org_UsrOrg";
private static readonly String aliasOrganisationOpportunity_Sport = "OrgOpp_Spt";
private static readonly String prefixOrganisationOpportunity_Sport = "OrgOpp_Spt_";
private static readonly String aliasOrganisationOpportunity_Region = "OrgOpp_Reg";
private static readonly String prefixOrganisationOpportunity_Region = "OrgOpp_Reg_";
private static readonly String aliasOrganisationOpportunity_Region = "OrgOpp_Rgn";
private static readonly String prefixOrganisationOpportunity_Region = "OrgOpp_Rgn_";
private void SetOrganisationOpportunityColDfn
(ColumnDfn col, String caption, int width, SaxDbDfn_Core.ColumnIdx dbCol,
ColumnAlignment align = ColumnAlignment.Default, DataValue.DataType dataType = DataValue.DataType.String,