v1.3.0 Restructure Saxonite workspace and upgrade to .NET 10 (#1)

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>
This commit was merged in pull request #1.
This commit is contained in:
2026-10-16 18:24:16 +01:00
committed by andy
parent 2c3fb146ab
commit 9f22b522df
1996 changed files with 1992 additions and 1179 deletions
@@ -0,0 +1,190 @@
using Microsoft.AspNetCore.Mvc;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Dbas;
using Sarand.Net.Standard.Base.Utils;
using Saxonite.Net.Core.Web.Admin.Models;
using Saxonite.Net.Standard.Base.App;
using Saxonite.Net.Standard.Base.Bus;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Admin.Controllers
{
//=======================================================================
partial class UsersOnlineController
{
//-------------------------------------------------------------------
// User data.
private class UserListData : AjaxResponse_Data.ItemData_Base
{
public String EmailAddress { get; set; }
public String DisplayName { get; set; }
public String Title { get; set; }
public String FirstName { get; set; }
public String LastName { get; set; }
public String LastLoginAt { get; set; }
public String OnlineAt { get; set; }
}
private class UserItemData : UserListData
{
}
private void PopulateUserResponseListData(SaxModel_Admin model, UserListData data, SaxUserAccount user, SaxUserOnline userOnline)
{
data.Id = SysId.ToString(user.Id);
data.EmailAddress = user.EmailAddress;
data.DisplayName = user.GetDisplayName();
data.Title = user.Title;
data.FirstName = user.FirstName;
data.LastName = user.LastName;
data.LastLoginAt = model.FormatDateTimeForDisplay(user.LastLoginAt);
if (userOnline != null)
data.OnlineAt = model.FormatDateTimeForDisplay(userOnline.OnlineAt);
}
private void PopulateUserResponseItemData(SaxModel_Admin model, UserItemData data, SaxUserAccount user, SaxUserOnline userOnline)
{
PopulateUserResponseListData(model, data, user, userOnline);
}
//-------------------------------------------------------------------
// Utilities.
//-------------------------------------------------------------------
// Get User List ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_UserList : AjaxResponse_Data
{
// Construction.
public AjaxResponse_UserList(SaxModel_Admin model) : base(model) { }
// User list.
public List<UserListData> Users { get; } = new List<UserListData>();
}
//-------------------------------------------------------------------
[HttpPost]
[Route("UsersOnline/Ajax/GetList")]
[Route("UsersOnline/Ajax/GetUsers")]
[Route("UsersOnline/Ajax/GetUserList")]
public IActionResult Ajax_GetUsersOnlineList(Model_User model)
{
String fn = GetType().Name + ".Ajax_GetUsersOnlineList";
String messageTitle = Localiser["label.users.online"];
AjaxResponse_UserList responseData = new AjaxResponse_UserList(model);
// Validate session and referrer.
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
// Item filter.
SaxUserOnline.Params prmUserOnline = new SaxUserOnline.Params();
prmUserOnline.UserFilterPartial = model.Filter.User;
prmUserOnline.SportIdsIncluded.AddId(SysId.ToSysId(model.Filter.UserSportId));
prmUserOnline.RegionIdsIncluded.AddId(SysId.ToSysId(model.Filter.UserRegionId));
model.Paging.ItemTotal = (ulong)AccountMgr.GetUserOnlineCount(prmUserOnline);
model.PopulatePagingTo(prmUserOnline.ResultsPaging);
// Item columns.
ColumnDfn colUserAccountId = AccountMgr.GetUserOnlineColumnDfn(SaxUserOnline.Column.UserAccountId);
ColumnDfn colEmail = AccountMgr.GetUserOnlineColumnDfn(SaxUserOnline.Column.EmailAddress);
ColumnDfn colTitle = AccountMgr.GetUserOnlineColumnDfn(SaxUserOnline.Column.Title);
ColumnDfn colFirstName = AccountMgr.GetUserOnlineColumnDfn(SaxUserOnline.Column.FirstName);
ColumnDfn colLastName = AccountMgr.GetUserOnlineColumnDfn(SaxUserOnline.Column.LastName);
ColumnDfn colLastLogin = AccountMgr.GetUserOnlineColumnDfn(SaxUserOnline.Column.LastLoginAt);
ColumnDfn colOnlineAt = AccountMgr.GetUserOnlineColumnDfn(SaxUserOnline.Column.OnlineAt);
List<ColumnDfn> columns = new List<ColumnDfn>()
{
colUserAccountId,
colEmail, colTitle, colFirstName, colLastName,
colLastLogin, colOnlineAt
};
// Query items.
using (DbResultSet resultSet = AccountMgr.ExecuteUserOnlineQuery(columns, prmUserOnline))
{
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
{
NamedValue.Map values = resultSet.GetRecordValues();
UserListData userData = new UserListData()
{
Id = values.GetString(colUserAccountId.DataName),
EmailAddress = values.GetString(colEmail.DataName),
Title = values.GetString(colTitle.DataName),
FirstName = values.GetString(colFirstName.DataName),
LastName = values.GetString(colLastName.DataName),
LastLoginAt = model.FormatDateTimeForDisplay(values.GetDateTime(colLastLogin.DataName)),
OnlineAt = model.FormatDateTimeForDisplay(values.GetDateTime(colOnlineAt.DataName)),
};
userData.DisplayName = SaxUserAccount.GetDisplayName(userData.Title, userData.FirstName, userData.LastName);
responseData.Users.Add(userData);
}
}
if (model.Paging.IsPaged)
model.Paging.ItemCount = (ushort)responseData.Users.Count;
// Complete response.
responseData.SetPaging(model);
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Get User ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_User : AjaxResponse_Data
{
// Construction.
public AjaxResponse_User(SaxModel_Admin model) : base(model) { }
// User details.
public UserItemData user { get; } = new UserItemData();
}
//-------------------------------------------------------------------
[HttpPost]
[Route("UsersOnline/Ajax/GetItem")]
[Route("UsersOnline/Ajax/GetUser")]
public IActionResult Ajax_GetUser(Model_User model)
{
String fn = GetType().Name + ".Ajax_GetUser";
String messageTitle = Localiser["user.caption.user"];
AjaxResponse_User responseData = new AjaxResponse_User(model);
// Validate session and referrer.
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
// Identify item.
SysId userAccountId = SysId.ToSysId(model.UserId);
if (!SysId.IsValid(userAccountId))
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.specified"]));
// Retrieve item.
SaxUserAccount user = AccountMgr.GetUserAccountById(userAccountId);
disposableItems.Add(user);
if (user == null)
{
SaLogger.Warn(fn + " Failed to retrieve user for Id: '" + userAccountId + "'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// User Online.
SaxUserOnline userOnline = AccountMgr.GetUserOnlineByUserAccountId(user.Id);
disposableItems.Add(userOnline);
// Construct response.
PopulateUserResponseItemData(model, responseData.user, user, userOnline);
responseData.Success = true;
}
return Json(responseData);
}
//-------------------------------------------------------------------
}
//=======================================================================
}