Move Saxonite’s projects into a dedicated `Workspace` folder and align the solution with .NET 10 and the current Common workspace. Update encryption integration, test project structure and coverage, and repository guidance and upgrade notes. - Relocate application and library projects while retaining Git rename history. - Split tests into Admin, Online, and Shared projects; add representative behavioral and serialization tests. - Update Common integration, application version, and repository documentation. Application version: 1.3.0 Reviewed-on: #1 Co-authored-by: andy <andy@sarand.com>
555 lines
17 KiB
C#
555 lines
17 KiB
C#
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 System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
|
|
namespace Saxonite.Net.Standard.Base.Bus
|
|
{
|
|
//=======================================================================
|
|
public abstract class SaxUserAccount : SaxDisposableHierarchyItem
|
|
{
|
|
//-------------------------------------------------------------------
|
|
// Columns.
|
|
public enum Column
|
|
{
|
|
Pos,
|
|
Id,
|
|
EmailAddress,
|
|
Title,
|
|
FirstName,
|
|
Initials,
|
|
LastName,
|
|
About,
|
|
PhoneMain,
|
|
PhoneMobile,
|
|
DateOfBirthDay,
|
|
DateOfBirthMonth,
|
|
DateOfBirthYear,
|
|
DateOfBirth,
|
|
// Regions.
|
|
RegionIdCountry,
|
|
RegionCountry_Name,
|
|
RegionIdProvince,
|
|
RegionProvince_Name,
|
|
// Security.
|
|
PasswordToken,
|
|
PasswordChangedOn,
|
|
PasswordChangedAt,
|
|
// Login.
|
|
LastLoginOn,
|
|
LastLoginAt,
|
|
LastInvalidLoginOn,
|
|
LastInvalidLoginAt,
|
|
InvalidLoginCount,
|
|
// Associated data.
|
|
UserSportCount,
|
|
UserSportCount_Active,
|
|
UserRoleCount,
|
|
SystemAdminUser,
|
|
LastAdminLoginOn,
|
|
LastAdminLoginAt,
|
|
LastInvalidAdminLoginOn,
|
|
LastInvalidAdminLoginAt,
|
|
InvalidAdminLoginCount,
|
|
// Common.
|
|
Notes,
|
|
Flags,
|
|
Status,
|
|
Status_Desc,
|
|
Status_Ind,
|
|
CreateOn_Date,
|
|
UpdateOn_Date,
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
/// <summary>Properties other than common.</summary>
|
|
public class DataProperties
|
|
{
|
|
public String EmailAddress { get; set; } = String.Empty;
|
|
public String Title { get; set; } = String.Empty;
|
|
public String FirstName { get; set; } = String.Empty;
|
|
public String Initials { get; set; } = String.Empty;
|
|
public String LastName { get; set; } = String.Empty;
|
|
public String About { get; set; } = String.Empty;
|
|
public String PhoneMain { get; set; } = String.Empty;
|
|
public String PhoneMobile { get; set; } = String.Empty;
|
|
public Sax.Gender Gender { get; set; }
|
|
public String GenderOther { get; set; }
|
|
public byte? DateOfBirthDay { get; set; } = null;
|
|
public byte? DateOfBirthMonth { get; set; } = null;
|
|
public short? DateOfBirthYear { get; set; } = null;
|
|
public SysId RegionIdCountry { get; set; }
|
|
public SysId RegionIdProvince { get; set; }
|
|
// Security.
|
|
public String PasswordHashed { get; set; } = String.Empty;
|
|
public List<String> PasswordHistory { get; } = new List<string>();
|
|
public String PasswordToken { get; set; } = String.Empty;
|
|
public DateTime? PasswordChangedAt { get; set; } = null;
|
|
// Login.
|
|
public DateTime? LastLoginAt { get; set; } = null;
|
|
public DateTime? LastInvalidLoginAt { get; set; } = null;
|
|
public short InvalidLoginCount { get; set; }
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Data.
|
|
public new class Data : SacsHierarchyItem.Data
|
|
{
|
|
// Data members.
|
|
private readonly DataProperties data = new DataProperties();
|
|
// Construction.
|
|
public Data()
|
|
{
|
|
Status = DefaultStatus;
|
|
}
|
|
// Properties.
|
|
public String EmailAddress
|
|
{
|
|
get { return data.EmailAddress; }
|
|
set { CheckChanged(data.EmailAddress, value); data.EmailAddress = value; }
|
|
}
|
|
public String Title
|
|
{
|
|
get { return data.Title; }
|
|
set { CheckChanged(data.Title, value); data.Title = value; }
|
|
}
|
|
public String FirstName
|
|
{
|
|
get { return data.FirstName; }
|
|
set { CheckChanged(data.FirstName, value); data.FirstName = value; }
|
|
}
|
|
public String Initials
|
|
{
|
|
get { return data.Initials; }
|
|
set { CheckChanged(data.Initials, value); data.Initials = value; }
|
|
}
|
|
public String LastName
|
|
{
|
|
get { return data.LastName; }
|
|
set { CheckChanged(data.LastName, value); data.LastName = value; }
|
|
}
|
|
public String About
|
|
{
|
|
get { return data.About; }
|
|
set { CheckChanged(data.About, value); data.About = value; }
|
|
}
|
|
public String PhoneMain
|
|
{
|
|
get { return data.PhoneMain; }
|
|
set { CheckChanged(data.PhoneMain, value); data.PhoneMain = value; }
|
|
}
|
|
public String PhoneMobile
|
|
{
|
|
get { return data.PhoneMobile; }
|
|
set { CheckChanged(data.PhoneMobile, value); data.PhoneMobile = value; }
|
|
}
|
|
public Sax.Gender Gender
|
|
{
|
|
get { return data.Gender; }
|
|
set { CheckChanged((int)data.Gender, (int)value); data.Gender = value; }
|
|
}
|
|
public String GenderOther
|
|
{
|
|
get { return data.GenderOther; }
|
|
set { CheckChanged(data.GenderOther, value); data.GenderOther = value; }
|
|
}
|
|
public byte? DateOfBirthDay
|
|
{
|
|
get { return data.DateOfBirthDay; }
|
|
set { if ((value??0) < 1) value = null; CheckChangedNullable(data.DateOfBirthDay, value); data.DateOfBirthDay = value; }
|
|
}
|
|
public byte? DateOfBirthMonth
|
|
{
|
|
get { return data.DateOfBirthMonth; }
|
|
set { if ((value ?? 0) < 1) value = null; CheckChangedNullable(data.DateOfBirthMonth, value); data.DateOfBirthMonth = value; }
|
|
}
|
|
public short? DateOfBirthYear
|
|
{
|
|
get { return data.DateOfBirthYear; }
|
|
set { if ((value ?? 0) < 1) value = null; CheckChangedNullable(data.DateOfBirthYear, value); data.DateOfBirthYear = value; }
|
|
}
|
|
public SysId RegionIdCountry
|
|
{
|
|
get { return data.RegionIdCountry; }
|
|
set { CheckChanged(data.RegionIdCountry, value); data.RegionIdCountry = value; }
|
|
}
|
|
public SysId RegionIdProvince
|
|
{
|
|
get { return data.RegionIdProvince; }
|
|
set { CheckChanged(data.RegionIdProvince, value); data.RegionIdProvince = value; }
|
|
}
|
|
// Security.
|
|
public String PasswordHashed
|
|
{
|
|
get { return data.PasswordHashed; }
|
|
set { CheckChanged(data.PasswordHashed, value); data.PasswordHashed = value; }
|
|
}
|
|
public List<String> PasswordHistory
|
|
{
|
|
get { return data.PasswordHistory; }
|
|
set
|
|
{
|
|
CheckChanged(data.PasswordHistory, value);
|
|
data.PasswordHistory.Clear();
|
|
data.PasswordHistory.AddRange((value ?? new List<String>()).FindAll(item => !StringUtil.IsEmpty(StringUtil.TrimAll(item))));
|
|
}
|
|
}
|
|
public String PasswordToken
|
|
{
|
|
get { return data.PasswordToken; }
|
|
set { CheckChanged(data.PasswordToken, value); data.PasswordToken = value; }
|
|
}
|
|
public DateTime? PasswordChangedAt
|
|
{
|
|
get { return data.PasswordChangedAt; }
|
|
set { CheckChangedNullable(data.PasswordChangedAt, value); data.PasswordChangedAt = value; }
|
|
}
|
|
// Login.
|
|
public DateTime? LastLoginAt
|
|
{
|
|
get { return data.LastLoginAt; }
|
|
set { CheckChangedNullable(data.LastLoginAt, value); data.LastLoginAt = value; }
|
|
}
|
|
public DateTime? LastInvalidLoginAt
|
|
{
|
|
get { return data.LastInvalidLoginAt; }
|
|
set { CheckChangedNullable(data.LastInvalidLoginAt, value); data.LastInvalidLoginAt = value; }
|
|
}
|
|
public short InvalidLoginCount
|
|
{
|
|
get { return data.InvalidLoginCount; }
|
|
set { CheckChanged(data.InvalidLoginCount, value); data.InvalidLoginCount = value; }
|
|
}
|
|
// Population.
|
|
internal void PopulateFrom(Data data)
|
|
{
|
|
EmailAddress = data.EmailAddress;
|
|
Title = data.Title;
|
|
FirstName = data.FirstName;
|
|
Initials = data.Initials;
|
|
LastName = data.LastName;
|
|
About = data.About;
|
|
PhoneMain = data.PhoneMain;
|
|
PhoneMobile = data.PhoneMobile;
|
|
Gender = data.Gender;
|
|
GenderOther = data.GenderOther;
|
|
DateOfBirthDay = data.DateOfBirthDay;
|
|
DateOfBirthMonth = data.DateOfBirthMonth;
|
|
DateOfBirthYear = data.DateOfBirthYear;
|
|
RegionIdCountry = data.RegionIdCountry;
|
|
RegionIdProvince = data.RegionIdProvince;
|
|
// Security.
|
|
PasswordHashed = data.PasswordHashed;
|
|
PasswordHistory = data.PasswordHistory;
|
|
PasswordToken = data.PasswordToken;
|
|
PasswordChangedAt = data.PasswordChangedAt;
|
|
// Login.
|
|
LastLoginAt = data.LastLoginAt;
|
|
LastInvalidLoginAt = data.LastInvalidLoginAt;
|
|
InvalidLoginCount = data.InvalidLoginCount;
|
|
base.PopulateFrom(data);
|
|
}
|
|
public Data Clone()
|
|
{
|
|
Data data = new Data();
|
|
data.PopulateFrom(this);
|
|
return data;
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Error field - identify properties with validation error.
|
|
public new class ErrField : SacsHierarchyItem.ErrField
|
|
{
|
|
// Identifies the field that has invalid data.
|
|
public ValidValue EmailAddress { get; set; } = ValidValue.Valid;
|
|
public ValidValue Title { get; set; } = ValidValue.Valid;
|
|
public ValidValue FirstName { get; set; } = ValidValue.Valid;
|
|
public ValidValue Initials { get; set; } = ValidValue.Valid;
|
|
public ValidValue LastName { get; set; } = ValidValue.Valid;
|
|
public ValidValue About { get; set; } = ValidValue.Valid;
|
|
public ValidValue PhoneMain { get; set; } = ValidValue.Valid;
|
|
public ValidValue PhoneMobile { get; set; } = ValidValue.Valid;
|
|
public ValidValue Gender { get; set; } = ValidValue.Valid;
|
|
public ValidValue GenderOther { get; set; } = ValidValue.Valid;
|
|
public ValidValue DateOfBirthDay { get; set; } = ValidValue.Valid;
|
|
public ValidValue DateOfBirthMonth { get; set; } = ValidValue.Valid;
|
|
public ValidValue DateOfBirthYear { get; set; } = ValidValue.Valid;
|
|
public ValidValue RegionIdCountry { get; set; } = ValidValue.Valid;
|
|
public ValidValue RegionIdProvince { get; set; } = ValidValue.Valid;
|
|
// Security.
|
|
public ValidValue Password { get; set; } = ValidValue.Valid;
|
|
public ValidValue PasswordHistory { get; set; } = ValidValue.Valid;
|
|
public ValidValue PasswordToken { get; set; } = ValidValue.Valid;
|
|
public ValidValue PasswordChanged { get; set; } = ValidValue.Valid;
|
|
// Login.
|
|
public ValidValue LastLogin { get; set; } = ValidValue.Valid;
|
|
public ValidValue LastInvalidLogin { get; set; } = ValidValue.Valid;
|
|
public ValidValue InvalidLoginCount { get; set; } = ValidValue.Valid;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Data members.
|
|
private readonly Data data = new Data();
|
|
public static readonly String UserRoleDelim = ",";
|
|
public static readonly String PasswordHistoryDelim = ",";
|
|
|
|
//-------------------------------------------------------------------
|
|
// Construction.
|
|
protected SaxUserAccount(SysId id) : base(id)
|
|
{
|
|
base.SetData(data);
|
|
Status = DefaultStatus;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Status.
|
|
private static readonly List<SaStatus> validStatuses = new List<SaStatus>()
|
|
{
|
|
SaStatus.Awaiting,
|
|
SaStatus.Active,
|
|
SaStatus.Inactive,
|
|
SaStatus.Deleted,
|
|
};
|
|
public static SaStatus DefaultStatus { get { return SaStatus.Awaiting; } }
|
|
public static List<SaStatus> ValidStatuses { get { return new List<SaStatus>(validStatuses); } }
|
|
public static DataValue.TriStateType GetStatusTriState(SaStatus status)
|
|
{
|
|
if (status == SaStatus.Active) return DataValue.TriStateType.Green;
|
|
if (status == SaStatus.Awaiting) return DataValue.TriStateType.Amber;
|
|
if (status == SaStatus.Inactive) return DataValue.TriStateType.Red;
|
|
if (status == SaStatus.Deleted) return DataValue.TriStateType.Purple;
|
|
return DataValue.TriStateType.Unknown;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Utilities.
|
|
public ItemId GetItemId()
|
|
{
|
|
return new ItemId((short)Sax.DataItemType.UserAccount, Id);
|
|
}
|
|
public static String GetDisplayName(SaxUserAccount userAccount)
|
|
{
|
|
return GetDisplayName(userAccount.Title, userAccount.FirstName, userAccount.LastName);
|
|
}
|
|
public static String GetDisplayName(String title, String firstName, String lastName)
|
|
{
|
|
return StringUtil.TrimAll(StringUtil.EmptyOnNull(title) + " " + StringUtil.EmptyOnNull(firstName) + " " + StringUtil.EmptyOnNull(lastName));
|
|
}
|
|
public String GetDisplayName()
|
|
{
|
|
return GetDisplayName(this);
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Properties.
|
|
public Data GetData() { return data.Clone(); }
|
|
public void SetData(Data data)
|
|
{
|
|
// Properties that cannot be changed if previously specified.
|
|
if (SysId.IsValid(Id) && SysId.ToInt(Id) != SysId.ToInt(data.Id))
|
|
throw new NotSupportedException($"{SysUtil.Fn} Id cannot be changed");
|
|
this.data.PopulateFrom(data);
|
|
}
|
|
public String EmailAddress
|
|
{
|
|
get { return data.EmailAddress; }
|
|
set { data.EmailAddress = value; }
|
|
}
|
|
public String Title
|
|
{
|
|
get { return data.Title; }
|
|
set { data.Title = value; }
|
|
}
|
|
public String FirstName
|
|
{
|
|
get { return data.FirstName; }
|
|
set { data.FirstName = value; }
|
|
}
|
|
public String Initials
|
|
{
|
|
get { return data.Initials; }
|
|
set { data.Initials = value; }
|
|
}
|
|
public String LastName
|
|
{
|
|
get { return data.LastName; }
|
|
set { data.LastName = value; }
|
|
}
|
|
public String About
|
|
{
|
|
get { return data.About; }
|
|
set { data.About = value; }
|
|
}
|
|
public String PhoneMain
|
|
{
|
|
get { return data.PhoneMain; }
|
|
set { data.PhoneMain = value; }
|
|
}
|
|
public String PhoneMobile
|
|
{
|
|
get { return data.PhoneMobile; }
|
|
set { data.PhoneMobile = value; }
|
|
}
|
|
public Sax.Gender Gender
|
|
{
|
|
get { return data.Gender; }
|
|
set { data.Gender = value; }
|
|
}
|
|
public String GenderOther
|
|
{
|
|
get { return data.GenderOther; }
|
|
set { data.GenderOther = value; }
|
|
}
|
|
public byte? DateOfBirthDay
|
|
{
|
|
get { return data.DateOfBirthDay; }
|
|
set { data.DateOfBirthDay = value; }
|
|
}
|
|
public byte? DateOfBirthMonth
|
|
{
|
|
get { return data.DateOfBirthMonth; }
|
|
set { data.DateOfBirthMonth = value; }
|
|
}
|
|
public short? DateOfBirthYear
|
|
{
|
|
get { return data.DateOfBirthYear; }
|
|
set { data.DateOfBirthYear = value; }
|
|
}
|
|
public SysId RegionIdCountry
|
|
{
|
|
get { return data.RegionIdCountry; }
|
|
set { data.RegionIdCountry = value; }
|
|
}
|
|
public SysId RegionIdProvince
|
|
{
|
|
get { return data.RegionIdProvince; }
|
|
set { data.RegionIdProvince = value; }
|
|
}
|
|
|
|
// Security.
|
|
public String PasswordHashed
|
|
{
|
|
get { return data.PasswordHashed; }
|
|
set { data.PasswordHashed = value; }
|
|
}
|
|
public List<String> PasswordHistory
|
|
{
|
|
get { return data.PasswordHistory; }
|
|
set { data.PasswordHistory = value; }
|
|
}
|
|
public String PasswordToken
|
|
{
|
|
get { return data.PasswordToken; }
|
|
set { data.PasswordToken = value; }
|
|
}
|
|
public DateTime ?PasswordChangedAt
|
|
{
|
|
get { return data.PasswordChangedAt; }
|
|
set { data.PasswordChangedAt = value; }
|
|
}
|
|
|
|
// Login.
|
|
public DateTime? LastLoginAt
|
|
{
|
|
get { return data.LastLoginAt; }
|
|
set { data.LastLoginAt = value; }
|
|
}
|
|
public DateTime? LastInvalidLoginAt
|
|
{
|
|
get { return data.LastInvalidLoginAt; }
|
|
set { data.LastInvalidLoginAt = value; }
|
|
}
|
|
public short InvalidLoginCount
|
|
{
|
|
get { return data.InvalidLoginCount; }
|
|
set { data.InvalidLoginCount = value; }
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Flags - positions 1-16 available.
|
|
public Byte Flag_KeepLoggedIn { get; } = 0x1;
|
|
public bool KeepLoggedIn
|
|
{
|
|
get { return GetFlag(Flag_KeepLoggedIn); }
|
|
set { SetFlag(Flag_KeepLoggedIn, value); }
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// A s s o c i a t e d D a t a
|
|
//-------------------------------------------------------------------
|
|
|
|
//-------------------------------------------------------------------
|
|
// P a r a m s
|
|
//-------------------------------------------------------------------
|
|
// List parameters.
|
|
public enum StatusCat
|
|
{
|
|
Any,
|
|
AwaitingOnly,
|
|
ActiveOnly,
|
|
AwaitingOrActive,
|
|
InactiveOnly,
|
|
NotDeleted,
|
|
DeletedOnly,
|
|
}
|
|
/// <summary>
|
|
/// Item list parameters.
|
|
/// </summary>
|
|
public new class Params : SacsHierarchyItem.Params
|
|
{
|
|
// List filtering.
|
|
public List<SysId> IdsIncluded { get; } = new List<SysId>();
|
|
public List<SysId> IdsExcluded { get; } = new List<SysId>();
|
|
public String EmailAddress { get; set; }
|
|
public String NamePartial { get; set; }
|
|
/// <summary>Exact match on various user account properties including name, email etc.</summary>
|
|
public String UserFilter { get; set; }
|
|
/// <summary>Partial match on various user account properties including name, countries etc. Not to be used in Online application!</summary>
|
|
public String UserFilterPartial { get; set; }
|
|
public List<SysId> SportIdsIncluded { get; } = new List<SysId>();
|
|
public List<SysId> RegionIdsIncluded { get; } = new List<SysId>();
|
|
public List<Sax.UserRole> ActiveRoles { get; } = new List<Sax.UserRole>();
|
|
// Common properties.
|
|
public String NotesPartial { get; set; }
|
|
public StatusCat StatusCat { get; set; } = StatusCat.ActiveOnly;
|
|
// Table aliases - blank if not linked.
|
|
public String Alias_UserSport { get; set; }
|
|
public String Alias_UserSport_Sport { get; set; }
|
|
public String Alias_UserRole { get; set; }
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
}
|
|
//=======================================================================
|
|
// Item list extension methods.
|
|
public static class SaxUserAccountList
|
|
{
|
|
//-------------------------------------------------------------------
|
|
/// <summary>
|
|
/// Extract the User Account Ids.
|
|
/// </summary>
|
|
public static List<SysId> GetIds(this List<SaxUserAccount> values)
|
|
{
|
|
return values.Select(item => item.Id).ToList();
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
/// <summary>
|
|
/// Extract the first User Account in list with the specified Id.
|
|
/// </summary>
|
|
public static SaxUserAccount GetUserAccountById(this List<SaxUserAccount> values, SysId userAccountId)
|
|
{
|
|
return values.Find(item => item.Id.Equals(userAccountId));
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
}
|
|
|
|
//=======================================================================
|
|
}
|