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,187 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
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 Sarand.Net.Standard.Dbas.Data;
using Saxonite.Net.Core.Web.Admin.Models;
using Saxonite.Net.Standard.Base.App;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Saxonite.Net.Core.Web.Admin.Controllers
{
//=======================================================================
// Home Controller - Ajax - Application Stats
//=======================================================================
partial class HomeController
{
//-------------------------------------------------------------------
// Application Stats data.
private class ApplicationStatsData
{
public class UserStatsData
{
public String Caption { get; set; }
public AjaxResponse_Data.ItemLabelValue Awaiting { get; set; }
public String UsersLink { get; set; }
public String AwaitingLink { get; set; }
public List<AjaxResponse_Data.ItemLabelValue> OnlineStats { get; set; }
public String OnlineLink { get; set; }
}
public UserStatsData User { get; } = new UserStatsData();
public class OrganisationStatsData
{
public String Caption { get; set; }
public AjaxResponse_Data.ItemLabelValue Awaiting { get; set; }
public String OrganisationsLink { get; set; }
public String AwaitingLink { get; set; }
}
public OrganisationStatsData Organisation { get; } = new OrganisationStatsData();
}
//-------------------------------------------------------------------
// Get Application Stats ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_ApplicationStats : AjaxResponse_Data
{
// Construction.
public AjaxResponse_ApplicationStats(SaxModel_Admin model) : base(model) { }
// Table list.
public ApplicationStatsData Stats { get; } = new ApplicationStatsData();
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Home/Ajax/GetApplicationStats")]
public IActionResult Ajax_GetApplicationStats(Model_Sport model)
{
String messageTitle = Localiser["home.caption.application"];
AjaxResponse_ApplicationStats responseData = new AjaxResponse_ApplicationStats(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Captions.
responseData.Stats.User.Caption = Localiser["user.caption.users"];
responseData.Stats.Organisation.Caption = Localiser["caption.organisations"];
// Core Database connection.
ISacsDatabase db = Internals.CoreDatabase;
// Query to count items awaiting activation.
Dictionary<String, int> tableRowCounts = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
List<String> query = new List<string>()
{
// User accounts.
"SELECT",
"ItemType = '" + DbQuery.FormatForCondition(nameof(Sax.DataItemType.UserAccount)) + "',",
"ItemCount = Count(*)",
"FROM UserAccount ua",
"WHERE ua.Status = 'AWA'",
// Organisations.
"UNION SELECT",
"ItemType = '" + DbQuery.FormatForCondition(nameof(Sax.DataItemType.Organisation)) + "',",
"ItemCount = Count(*)",
"FROM Organisation org",
"WHERE org.Status = 'AWA'",
};
using (DbResultSet dbResultSet = DbResultSet_DataReader.ExecuteQuery(db, query.Concat("\n")))
{
while (dbResultSet != null && dbResultSet.NextRecord())
{
NamedValue.Map values = dbResultSet.GetRecordValues();
Sax.DataItemType itemType = EnumUtil.ParseEnum(values.GetValue("ItemType").ToString(), Sax.DataItemType.Unknown);
int itemCount = (int)values.GetValue("ItemCount").AsLong();
switch (itemType)
{
case Sax.DataItemType.UserAccount:
responseData.Stats.User.Awaiting = new AjaxResponse_Data.ItemLabelValue() { Label = Localiser["label.awaiting.activation"], Value = itemCount.ToString() };
responseData.Stats.User.UsersLink = Url.Action("", "User").TrimStart('/');
responseData.Stats.User.AwaitingLink = Url.Action("", "User", new RouteValueDictionary() { { "sta", "Await" } }).TrimStart('/');
break;
case Sax.DataItemType.Organisation:
responseData.Stats.Organisation.Awaiting = new AjaxResponse_Data.ItemLabelValue() { Label = Localiser["label.awaiting.activation"], Value = itemCount.ToString() };
responseData.Stats.Organisation.OrganisationsLink = Url.Action("", "Organisation").TrimStart('/');
responseData.Stats.Organisation.AwaitingLink = Url.Action("", "Organisation", new RouteValueDictionary() { { "sta", "Await" } }).TrimStart('/');
break;
}
}
}
// Query to count users online.
DateTime today = DateTime.Today;
DateTime now = DateTime.Now;
const String online5Mins = "Online5Mins";
const String online15Mins = "Online15Mins";
const String online30Mins = "Online30Mins";
const String online60Mins = "Online60Mins";
const String onlineToday = "OnlineToday";
const String onlineWeek = "OnlineWeek";
const String onlineMonth = "OnlineMonth";
query = new List<string>()
{
// Users Online last 5 mins.
"Select '" + online5Mins + "' ItemType, (Select Count(*) From UserOnline Where OnlineAt>=" + DbQuery.FormatDateTimeForCondition(now.AddMinutes(-5)) + ") ItemCount ",
"Union Select '" + online15Mins + "' ItemType, (Select Count(*) From UserOnline Where OnlineAt>=" + DbQuery.FormatDateTimeForCondition(now.AddMinutes(-15)) + ") ItemCount ",
"Union Select '" + online30Mins + "' ItemType, (Select Count(*) From UserOnline Where OnlineAt>=" + DbQuery.FormatDateTimeForCondition(now.AddMinutes(-30)) + ") ItemCount ",
"Union Select '" + online60Mins + "' ItemType, (Select Count(*) From UserOnline Where OnlineAt>=" + DbQuery.FormatDateTimeForCondition(now.AddMinutes(-60)) + ") ItemCount ",
"Union Select '" + onlineToday + "' ItemType, (Select Count(*) From UserOnline Where OnlineAt>=" + DbQuery.FormatDateForCondition(today) + ") ItemCount ",
"Union Select '" + onlineWeek + "' ItemType, (Select Count(*) From UserOnline Where OnlineAt>=" + DbQuery.FormatDateForCondition(DateUtil.StartOfWorkingWeek(today)) + ") ItemCount ",
"Union Select '" + onlineMonth + "' ItemType, (Select Count(*) From UserOnline Where OnlineAt>=" + DbQuery.FormatDateForCondition(DateUtil.StartOfMonth(today)) + ") ItemCount ",
};
using (DbResultSet dbResultSet = DbResultSet_DataReader.ExecuteQuery(db, query.Concat("\n")))
{
while (dbResultSet != null && dbResultSet.NextRecord())
{
if (responseData.Stats.User.OnlineStats == null)
responseData.Stats.User.OnlineStats = new List<AjaxResponse_Data.ItemLabelValue>();
NamedValue.Map values = dbResultSet.GetRecordValues();
String itemType = values.GetValue("ItemType").ToString();
int itemCount = (int)values.GetValue("ItemCount").AsLong();
switch (itemType)
{
case online5Mins:
if (itemCount > 0)
responseData.Stats.User.OnlineStats.Add(new AjaxResponse_Data.ItemLabelValue() { Label = Localiser["label.online.last5Mins"], Value = itemCount.ToString() });
break;
case online15Mins:
if (itemCount > 0)
responseData.Stats.User.OnlineStats.Add(new AjaxResponse_Data.ItemLabelValue() { Label = Localiser["label.online.last15Mins"], Value = itemCount.ToString() });
break;
case online30Mins:
if (itemCount > 0)
responseData.Stats.User.OnlineStats.Add(new AjaxResponse_Data.ItemLabelValue() { Label = Localiser["label.online.last30Mins"], Value = itemCount.ToString() });
break;
case online60Mins:
if (itemCount > 0)
responseData.Stats.User.OnlineStats.Add(new AjaxResponse_Data.ItemLabelValue() { Label = Localiser["label.online.last60Mins"], Value = itemCount.ToString() });
break;
case onlineToday:
if (itemCount > 0)
responseData.Stats.User.OnlineStats.Add(new AjaxResponse_Data.ItemLabelValue() { Label = Localiser["label.online.today"], Value = itemCount.ToString() });
break;
case onlineWeek:
if (itemCount > 0)
responseData.Stats.User.OnlineStats.Add(new AjaxResponse_Data.ItemLabelValue() { Label = Localiser["label.online.thisWeek"], Value = itemCount.ToString() });
break;
case onlineMonth:
if (itemCount > 0)
responseData.Stats.User.OnlineStats.Add(new AjaxResponse_Data.ItemLabelValue() { Label = Localiser["label.online.thisMonth"], Value = itemCount.ToString() });
break;
}
responseData.Stats.User.OnlineLink = Url.Action("", "UsersOnline", new RouteValueDictionary() { }).TrimStart('/');
}
}
// Complete response.
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,63 @@
using Microsoft.AspNetCore.Mvc;
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.Core.Web.Admin.Models;
using Saxonite.Net.Core.Web.Base.App;
using Saxonite.Net.Core.Web.Base.Controllers;
using Saxonite.Net.Standard.Base.Bus;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Admin.Controllers
{
//=======================================================================
public partial class HomeController : SaxController_Admin
{
//-------------------------------------------------------------------
// Construction.
public HomeController(SaxLocaliser localiser, ILogger<HomeController> logger) :
base(localiser, logger)
{
}
//-------------------------------------------------------------------
/// <summary>
///
/// </summary>
/// <returns></returns>
[HttpGet]
public IActionResult Index()
{
if (!ValidUserSession)
AutoLoginUser();
// Redirect to sign-in if not signed-in.
if (!ValidUserSession)
{
ClearKeepLoggedInCookie();
return RedirectToAction("", "Security");
}
// Home page.
Model_Home model = new Model_Home(Localiser);
model.ProhibitCache = false;
return View(model);
}
//-------------------------------------------------------------------
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel(Localiser) { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,208 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Utils;
using Saxonite.Net.Core.Web.Base.App;
using Saxonite.Net.Standard.Base.App;
using Saxonite.Net.Standard.Base.Bus;
using System;
using System.Collections.Specialized;
namespace Saxonite.Net.Core.Web.Admin.Controllers
{
//=======================================================================
// Admin Image Controller - delivers dynamic images.
//=======================================================================
public partial class ImageController : SaxController_Admin
{
//-------------------------------------------------------------------
// Data members.
public static String ParamName_OrganisationLogoEncrypted { get; } = "ole";
public static String ParamName_ItemImageEncrypted { get; } = "iie";
public static String ParamName_OrganisationId { get; } = "ori";
public static String ParamName_ItemImageId { get; } = "iii";
//-------------------------------------------------------------------
// Construction.
public ImageController(SaxLocaliser localiser, ILogger<HomeController> logger) :
base(localiser, logger)
{
}
//-------------------------------------------------------------------
// Account Contact image request.
//-------------------------------------------------------------------
/// <summary>
/// Get the Account Contact Image for the specified account.
/// <para>Current User Account is used if no other specified in the encrypted parameter.</para>
/// <para>Encrypted parameter:</para>
/// <para>- uaie: User Account Id encrypted.</para>
/// </summary>
[HttpGet]
[Route("Image/Account/Contact")]
public IActionResult GetAccountContactImage(String uaie)
{
String userAccountIdParam = "";// ToDo Decrypt from uaie value.
SysId userAccountId = SysId.ToSysId(userAccountIdParam);
if (!SysId.IsValid(userAccountId))
userAccountId = SessionUserAccountId;
return GetAccountContactImage(userAccountId, Sax.ImageType.Avatar);
}
//-------------------------------------------------------------------
// Account Contact Image Profile/Upload request.
//-------------------------------------------------------------------
/// <summary>
/// Get the current Account Profile Contact Image for the specified account.
/// <para>Account is always current User Account - this request is only fir use from the user's own profile page.</para>
/// <para>'Uploaded' avatar image will take precedence as this will be the latest if present.</para>
/// <para>NB Avoid caching this image as it will change whenever a new one is uploaded.</para>
/// </summary>
[HttpGet]
[Route("Image/Account/Contact/Profile")]
[Route("Image/Account/Contact/Upload")]
[ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
public IActionResult GetAccountContactImage()
{
// Response should not be cached.
Response.Headers[HeaderNames.CacheControl] = "no-cache, no-store, must-revalidate, max-age=0";
Response.Headers[HeaderNames.Expires] = "-1";
SysId userAccountId = SessionUserAccountId;
return GetAccountContactImage(userAccountId, Sax.ImageType.Avatar_Upload);
}
//-------------------------------------------------------------------
/// <summary>
/// Get the Avatar image for the specified user account.
/// </summary>
/// <param name="imageTypePriority"></param>
/// <returns></returns>
private IActionResult GetAccountContactImage(SysId userAccountId, Sax.ImageType imageTypePriority)
{
// Validate session and referrer.
if (!ValidReferer || !ValidUserSession) return Forbid();
ItemId userItemId = new ItemId((short)Sax.DataItemType.UserAccount, userAccountId);
// Retrieve account contact images.
SaxItemImage.Params prmItemImage = new SaxItemImage.Params();
prmItemImage.ItemIdsIncluded.Add(userItemId);
prmItemImage.ImageTypesIncluded.Add(Sax.ImageType.Avatar);
prmItemImage.ImageTypesIncluded.Add(Sax.ImageType.Avatar_Upload);
prmItemImage.StatusCat = SaxItemImage.StatusCat.ActiveOnly;
SaxItemImage itemImage = null;
foreach (SaxItemImage itemImageCandidate in ItemDataMgr.GetItemImageList(prmItemImage))
{
// One of the 'Avatar' image types will take precedence.
if (itemImage == null || itemImageCandidate.ImageType == imageTypePriority)
itemImage = itemImageCandidate;
}
if (itemImage == null)
return NotFound();
// Construct response.
return File(itemImage.ImageData, itemImage.ContentType);
}
//-------------------------------------------------------------------
// Organisation logo.
//-------------------------------------------------------------------
/// <summary>
/// Get the logo for the specified Organisation.
/// <para>Method parameter name (ole) must match name of encrypted parameter.</para>
/// <para>Encrypted parameters:</para>
/// <para>- ori: Organisation Id.</para>
/// </summary>
[HttpGet]
[Route("Image/Organisation/Logo")]
public IActionResult GetOrganisationLogo(String ole)
{
return GetOrganisationLogo(ole, false);
}
/// <summary>
/// Get the logo for the specified Organisation.
/// <para>Method parameter name (ole) must match name of encrypted parameter.</para>
/// <para>Encrypted parameters:</para>
/// <para>- ori: Organisation Id.</para>
/// </summary>
[HttpGet]
[Route("Image/Organisation/Logo/Upload")]
public IActionResult GetOrganisationLogoUpload(String ole)
{
return GetOrganisationLogo(ole, true);
}
private IActionResult GetOrganisationLogo(String encryptedParams, bool allowUploadImage)
{
// Validate session and referrer.
if (!ValidReferer || !ValidUserSession) return Forbid();
// Extract encrypted parameters.
NameValueCollection parameters = ExtractSessionParams(encryptedParams);
if (parameters.Count < 1) return Forbid();
// Organisation Id.
SysId organisationId = SysId.ToSysId(parameters.Get(ParamName_OrganisationId));
if (!SysId.IsValid(organisationId)) return Forbid();
ItemId organisationItemId = new ItemId((short)Sax.DataItemType.Organisation, organisationId);
// Query Organisation Logo images.
SaxItemImage.Params prmItemImage = new SaxItemImage.Params();
prmItemImage.ItemIdsIncluded.Add(organisationItemId);
prmItemImage.ImageTypesIncluded.Add(Sax.ImageType.OrganisationLogo);
if (allowUploadImage)
prmItemImage.ImageTypesIncluded.Add(Sax.ImageType.OrganisationLogo_Upload);
prmItemImage.StatusCat = SaxItemImage.StatusCat.ActiveOnly;
SaxItemImage itemImage = null;
foreach (SaxItemImage itemImageCandidate in ItemDataMgr.GetItemImageList(prmItemImage))
{
// The 'Upload' image will take precedence if allowed.
if (itemImage == null || itemImageCandidate.ImageType == Sax.ImageType.OrganisationLogo_Upload)
itemImage = itemImageCandidate;
}
if (itemImage == null)
return NotFound();
// Construct response.
Response.Headers[HeaderNames.CacheControl] = "no-cache, no-store, must-revalidate, max-age=0";
Response.Headers[HeaderNames.Expires] = "-1";
return File(itemImage.ImageData, itemImage.ContentType);
}
//-------------------------------------------------------------------
// Item image.
//-------------------------------------------------------------------
/// <summary>
/// Get the specified Item Image.
/// <para>Method parameter name (iie) must match name of encrypted parameter.</para>
/// <para>Encrypted parameters:</para>
/// <para>- iii: Item Image Id.</para>
/// </summary>
[HttpGet]
[Route("Image/Item")]
public IActionResult GetItemImage(String iie)
{
// Validate session and referrer.
if (!ValidReferer || !ValidUserSession) return Forbid();
ItemId userItemId = new ItemId((short)Sax.DataItemType.UserAccount, SessionUserAccountId);
// Extract encrypted parameters.
NameValueCollection parameters = ExtractSessionParams(iie);
if (parameters.Count < 1) return Forbid();
// Item Image.
SysId itemImageId = SysId.ToSysId(parameters.Get(ParamName_ItemImageId));
if (!SysId.IsValid(itemImageId)) return Forbid();
SaxItemImage itemImage = ItemDataMgr.GetItemImageById(itemImageId);
if (itemImage == null)
return NotFound();
// Construct response.
return File(itemImage.ImageData, itemImage.ContentType);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,255 @@
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.Email;
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 Saxonite.Net.Standard.Base.Helpers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Admin.Controllers
{
//=======================================================================
// Controller: Organisation - Ajax - Activation
//=======================================================================
partial class OrganisationController
{
//-------------------------------------------------------------------
// Get Organisation Activation Info ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_OrganisationActivationInfo : AjaxResponse_Data
{
// Construction.
public AjaxResponse_OrganisationActivationInfo(SaxModel_Admin model) : base(model) { }
// Organisation details.
public OrganisationItemData organisation { get; } = new OrganisationItemData();
// Member awaiting activation.
public List<MemberListData> Members { get; } = new List<MemberListData>();
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Organisation/Ajax/GetActivationInfo")]
public IActionResult Ajax_GetActivationInfo(Model_Organisation model)
{
String messageTitle = Localiser["caption.organisation"];
AjaxResponse_OrganisationActivationInfo responseData = new AjaxResponse_OrganisationActivationInfo(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
if (StringUtil.IsEmpty(AppConfig.URL.Online.Root))
return Json(responseData.SetPopupMessage(messageTitle, "Root online URL not specified."));
// Identify item.
SysId organisationId = SysId.ToSysId(model.OrganisationId);
if (!SysId.IsValid(organisationId))
{
SaLogger.Warn($"{SysUtil.Fn} Request to get activation info without valid organisation Id.");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.specified"]));
}
// Retrieve item.
SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationId);
disposableItems.Add(organisation);
if (organisation == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve organisation. Id: '{model.OrganisationId}'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Organisation address.
SaxItemAddress address = ItemDataMgr.GetFirstItemAddressByItemAndType(organisation.GetItemId());
disposableItems.Add(address);
// Construct response.
PopulateOrganisationResponseItemData(organisation, address, responseData.organisation);
GetOrganisationMembers(model, organisationId, responseData.Members, true);
}
// Complete response.
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
private class OrganisationMemberData : SacsDisposable
{
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (UserOrganisation != null) UserOrganisation.Dispose(); UserOrganisation = null;
if (UserAccount != null) UserAccount.Dispose(); UserAccount = null;
}
}
public SaxUserOrganisation UserOrganisation { get; set; }
public SaxUserAccount UserAccount { get; set; }
}
//-------------------------------------------------------------------
// Activate Organisation ajax request.
//-------------------------------------------------------------------
[HttpPost]
[Route("Organisation/Ajax/Activate")]
[Route("Organisation/Ajax/ActivateItem")]
public IActionResult Ajax_Activate(Model_OrganisationActivation model)
{
String messageTitle = Localiser["caption.organisation"];
AjaxResponse_Organisation responseData = new AjaxResponse_Organisation(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
// Retrieve existing item.
SysId organisationId = SysId.ToSysId(model.OrganisationId);
SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationId);
disposableItems.Add(organisation);
if (organisation == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve organisation. Id: '{model.OrganisationId}'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Organisation address.
SaxItemAddress address = ItemDataMgr.GetFirstItemAddressByItemAndType(organisation.GetItemId());
disposableItems.Add(address);
// Retrieve users.
List<OrganisationMemberData> organisationMembersToActivate = new List<OrganisationMemberData>();
foreach (String userOrganisationId in model.OrganisationMemberIds)
{
if (userOrganisationId == null) continue;
OrganisationMemberData organisationMember = new OrganisationMemberData();
disposableItems.Add(organisationMember);
organisationMember.UserOrganisation = AccountMgr.GetUserOrganisationById(SysId.ToSysId(userOrganisationId));
organisationMember.UserAccount = AccountMgr.GetUserAccountById(organisationMember.UserOrganisation?.UserAccountId ?? null);
if (organisationMember.UserOrganisation == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve user organisation. Id: '{userOrganisationId}'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.system"]));
}
if (organisationMember.UserAccount == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve user account . Id: '{organisationMember.UserOrganisation.UserAccountId}'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.system"]));
}
if (organisationMember.UserAccount.Status != SaStatus.Awaiting)
{
SaLogger.Warn($"{SysUtil.Fn} User organisation is not awaiting. Id: '{userOrganisationId}'. Status: {organisationMember.UserAccount.StatusDesc}");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.system"]));
}
// Check if this user is to be activated.
bool activateUser = false;
String controlName_Activate = $"MemberActivate_{userOrganisationId}";
foreach (String key in Request.Form.Keys)
{
if (key.EqualsCI(controlName_Activate))
{
activateUser = true;
break;
}
}
if (activateUser)
organisationMembersToActivate.Add(organisationMember);
}
if (organisationMembersToActivate.IsEmpty())
{
// No users to be activated.
// Check that organisation already has active members.
if (AccountMgr.GetOrganisationMemberCount(organisation.Id) < 1)
{
SaLogger.Warn($"{SysUtil.Fn} Unable to activate Organisation - no active members. Id: '{SysId.ToString(organisationId)}");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["organisation.activate.msg.no.active.members"]));
}
}
// Activate organisation.
organisation.Status = Sarand.Net.Standard.Base.Core.SaStatus.Active;
if (!AccountMgr.UpdateOrganisation(organisation, DbUpdateInfo))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to update organisation [Id:{SysId.ToString(organisation.Id)}]: {AccountMgr.ErrorStateMessage}");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + AccountMgr.ErrorStateMessage));
}
// Activate awaiting members and send welcome email to each.
foreach (OrganisationMemberData organisationMember in organisationMembersToActivate)
{
if (organisationMember.UserOrganisation == null) continue;
if (AccountMgr.UpdateUserOrganisation(organisationMember.UserOrganisation, DbUpdateInfo))
{
if (organisationMember.UserAccount == null) continue;
organisationMember.UserAccount.Status = SaStatus.Active;
if (AccountMgr.UpdateUserAccount(organisationMember.UserAccount, DbUpdateInfo))
{
// Send activation email.
if (SendAccountActivatedEmail(this, organisationMember.UserAccount))
SaLogger.Info($"{SysUtil.Fn} User Activation email sent to {organisationMember.UserAccount.EmailAddress}", true);
else SaLogger.Warn($"{SysUtil.Fn} Failed to send User Activation email to {organisationMember.UserAccount.EmailAddress}");
}
else
SaLogger.Warn($"{SysUtil.Fn} Failed to update user account [Id:{SysId.ToString(organisationMember.UserAccount.Id)}]: {AccountMgr.ErrorStateMessage}");
}
else
SaLogger.Warn($"{SysUtil.Fn} Failed to update user organisation [Id:{SysId.ToString(organisationMember.UserOrganisation.Id)}]: {AccountMgr.ErrorStateMessage}");
}
// Construct response.
PopulateOrganisationResponseItemData(organisation, address, responseData.organisation);
}
// Complete response.
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
/// <summary>
/// Generate the User Activation email.
/// <para>NB URL must be to the Online application.</para>
/// <para>NB Encryption must be done with the Online logic.</para>
/// </summary>
public static bool SendAccountActivatedEmail(SaxController_Admin controller, SaxUserAccount userAccount)
{
SaEmailData emailData = new SaEmailData();
emailData.Config = controller.EmailConfig;
emailData.AddressesTo.Add(userAccount.EmailAddress);
emailData.Subject = controller.Localiser["user.activate.email.subject"];
PopulateAccountActivatedEmailBody(controller, emailData, userAccount);
return controller.EmailConsole.SendEmail(emailData);
}
private static void PopulateAccountActivatedEmailBody(SaxController_Admin controller, SaEmailData emailData, SaxUserAccount userAccount)
{
// Will need to handle culture (language/country) as and when introduced.
emailData.MessageBody = SaxResourceHelper.GetResourceText(SaxResourceHelper.TextResource.Email_Security_AccountActivated_Html);
emailData.HtmlBody = true;
// Replace dynamic tags.
controller.EmailConsole.ReplaceDynamicEmailBodyTags(emailData, null, userAccount);
// Replace dynamic tag for link back to change password page.
SaEmailConsole.ReplaceDynamicEmailTag(emailData, "[#Link.RequestPassword]", SecurityController.GenerateRequestPasswordLink(controller, userAccount.Id, userAccount.EmailAddress, "", true));
SaEmailConsole.ReplaceDynamicEmailTag(emailData, "[#Link.SignIn]", controller.AppConfig.URL.Online.Root + "Security/SignIn");
SaEmailConsole.ReplaceDynamicEmailTag(emailData, "[#Link.ExpiresMinutes]", SecurityController.GetRequestPasswordEmailLinkExpiresMinutes(controller).ToString());
}
//-------------------------------------------------------------------
}
}
@@ -0,0 +1,656 @@
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.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Admin.Controllers
{
//=======================================================================
// Controller: Organisation - Ajax - Associations
//=======================================================================
partial class OrganisationController
{
//-------------------------------------------------------------------
// Association type combinations.
//-------------------------------------------------------------------
private static readonly String optionLabel_AffiliatedTo = "Affiliated to";
private static readonly String optionLabel_Affiliate = "Affiliation for";
private static readonly String optionLabel_Partner = "Partner";
// Associations are between two items (From & To) but in the context of
// displaying associations for an organisation, the organisation may be
// the 'From' item (normal) or the 'To' item (reversed).
private class AssociationTypeData
{
public AssociationTypeData()
{
// No parameter construction - populating association details for individual item (see Populate()).
}
public AssociationTypeData(Sax.AssociationTypeOption associationTypeOption)
{
// Population on construction - generic association type definitions, eg. for drop down menu options.
Populate(associationTypeOption);
}
public void Populate(Sax.AssociationTypeOption associationTypeOption)
{
// Default labels - option to select association etc.
Type = ((int)associationTypeOption).ToString();
Label = GetOptionLabel(associationTypeOption);
}
public void Populate(Sax.AssociationType associationType, bool reversed)
{
// Default labels - option to select association etc.
Populate(GetAssociationTypeOption(associationType, reversed));
}
public void Populate(Sax.AssociationType associationType, bool reversed, String label)
{
// Populate for specific labels, eg. to identify nature of associated organisation.
Sax.AssociationTypeOption associationTypeOption = GetAssociationTypeOption(associationType, reversed);
Type = ((int)associationTypeOption).ToString();
Label = label;
}
// Properties.
public String Type { get; set; }
public String Label { get; set; }
// Utilities.
public static Sax.AssociationTypeOption GetAssociationTypeOption(Sax.AssociationType associationType, bool reversed)
{
switch (associationType)
{
case Sax.AssociationType.Affiliate: return reversed ? Sax.AssociationTypeOption.AffiliateChild : Sax.AssociationTypeOption.AffiliateParent;
case Sax.AssociationType.Partner: return Sax.AssociationTypeOption.Partner;
}
return Sax.AssociationTypeOption.Unknown;
}
public static String GetOptionLabel(Sax.AssociationTypeOption associationTypeOption)
{
switch (associationTypeOption)
{
case Sax.AssociationTypeOption.AffiliateParent: return optionLabel_AffiliatedTo;
case Sax.AssociationTypeOption.AffiliateChild: return optionLabel_Affiliate;
case Sax.AssociationTypeOption.Partner: return optionLabel_Partner;
}
return "";
}
}
//-------------------------------------------------------------------
// Association data.
//-------------------------------------------------------------------
private class AssociationListData : AjaxResponse_Data.ItemData_Base
{
public class OrganisationData
{
public String Id { get; set; }
public String Name { get; set; }
public String Sport { get; set; }
public String Region { get; set; }
}
public OrganisationData Organisation { get; } = new OrganisationData();
public OrganisationData OrganisationAssoc { get; } = new OrganisationData();
public AssociationTypeData Assoc { get; } = new AssociationTypeData();
}
private class AssociationItemData : AssociationListData
{
}
private void PopulateAssociationResponseListData(SaxModel_Admin model, AssociationListData data, SaxItemAssociation itemAssociation, SaxOrganisation organisation, SaxOrganisation organisationAssoc)
{
data.Id = SysId.ToString(itemAssociation.Id);
// Organisation.
data.Organisation.Id = SysId.ToString(organisation.Id);
data.Organisation.Name = organisation.Name;
// Associated Organisation.
data.OrganisationAssoc.Id = SysId.ToString(organisationAssoc.Id);
data.OrganisationAssoc.Name = organisationAssoc.Name;
if (!organisation.SportIdPrimary.Equals(organisationAssoc.SportIdPrimary))
data.OrganisationAssoc.Sport = model.GetSportName(organisationAssoc.SportIdPrimary);
if (!organisation.RegionIdPrimary.Equals(organisationAssoc.RegionIdPrimary))
data.OrganisationAssoc.Region = model.GetRegionName(organisationAssoc.RegionIdPrimary);
// Association type.
bool associationReversed = itemAssociation.ItemIdTo.Id.Equals(organisation.Id);
String associationLabel = "";
if (itemAssociation.AssociationType == Sax.AssociationType.Affiliate)
associationLabel = associationReversed ? "Member organisation" : associationLabel = "Parent organisation";
else if (itemAssociation.AssociationType == Sax.AssociationType.Partner)
associationLabel = "Partner organisation";
data.Assoc.Populate(itemAssociation.AssociationType, associationReversed, associationLabel);
// Common properties.
data.StatusCode = itemAssociation.StatusCode;
data.StatusDesc = itemAssociation.StatusDesc;
}
private void PopulateAssociationResponseItemData(SaxModel_Admin model, AssociationItemData data, SaxItemAssociation itemAssociation, SaxOrganisation organisation, SaxOrganisation organisationAssoc)
{
PopulateAssociationResponseListData(model, data, itemAssociation, organisation, organisationAssoc);
}
//-------------------------------------------------------------------
// Get Organisation Association List ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_AssociationList : AjaxResponse_Data
{
// Construction.
public AjaxResponse_AssociationList(SaxModel_Admin model) : base(model) { }
// Association list.
public List<AssociationListData> Associations { get; } = new List<AssociationListData>();
// Reference data.
public List<AssociationTypeData> AssociationTypes { get; } = new List<AssociationTypeData>();
public List<CodeDesc> Statuses { get; } = new List<CodeDesc>();
public String DefaultStatusCode { get; set; }
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Organisation/Ajax/GetAssociationList")]
[Route("Organisation/Ajax/GetAssociations")]
public IActionResult Ajax_GetAssociationList(Model_OrganisationAssociations model)
{
String messageTitle = Localiser["caption.organisation.associations"];
AjaxResponse_AssociationList responseData = new AjaxResponse_AssociationList(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Organisation Id is mandatory.
SysId organisationId = SysId.ToSysId(model.Filter.OrganisationId);
if (!SysId.IsValid(organisationId))
{
SaLogger.Warn($"{SysUtil.Fn} Request to retrieve associations without valid organisation Id.");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.specified"]));
}
// Reference data - association types.
// 'Label' is option caption to add new association of the specified type.
responseData.AssociationTypes.Add(new AssociationTypeData(Sax.AssociationTypeOption.AffiliateParent));
responseData.AssociationTypes.Add(new AssociationTypeData(Sax.AssociationTypeOption.AffiliateChild));
responseData.AssociationTypes.Add(new AssociationTypeData(Sax.AssociationTypeOption.Partner));
// Reference data - statuses.
foreach (SaStatus status in SaxItemAssociation.ValidStatuses.FindAll(item => item != SaStatus.Deleted))
responseData.Statuses.Add(SacsSystem.GetStatusCode(status), Localiser.GetStatusCaption(status));
responseData.DefaultStatusCode = SacsSystem.GetStatusCode(SaxItemAssociation.DefaultStatus);
// Get organisation associations.
GetOrganisationAssociations(model, organisationId, responseData.Associations);
if (model.Paging.IsPaged)
model.Paging.ItemCount = (ushort)responseData.Associations.Count;
// Complete response.
responseData.SetPaging(model);
responseData.Success = true;
return Json(responseData);
}
private void GetOrganisationAssociations(SaxModel_Admin model, SysId organisationId, List<AssociationListData> associations)
{
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
// Item filter.
SaxItemAssociation.Params prmItemAssociation = new SaxItemAssociation.Params();
prmItemAssociation.ItemIdsIncluded.Add(new ItemId((short)Sax.DataItemType.Organisation, organisationId));
prmItemAssociation.StatusCat = SaxItemAssociation.StatusCat.NotDeleted;
model.Paging.ItemTotal = (ulong)ItemDataMgr.GetItemAssociationCount(prmItemAssociation);
model.PopulatePagingTo(prmItemAssociation.ResultsPaging);
// Item columns.
ColumnDfn colId = ItemDataMgr.GetItemAssociationColumnDfn(SaxItemAssociation.Column.Id);
ColumnDfn colItemTypeFrom = ItemDataMgr.GetItemAssociationColumnDfn(SaxItemAssociation.Column.ItemTypeFrom);
ColumnDfn colItemIdFrom = ItemDataMgr.GetItemAssociationColumnDfn(SaxItemAssociation.Column.ItemIdFrom);
ColumnDfn colOrganisationFrom_Name = ItemDataMgr.GetItemAssociationColumnDfn(SaxItemAssociation.Column.OrganisationFrom_Name);
ColumnDfn colOrganisationFrom_SportIdPrimary = ItemDataMgr.GetItemAssociationColumnDfn(SaxItemAssociation.Column.OrganisationFrom_SportIdPrimary);
ColumnDfn colOrganisationFrom_RegionIdPrimary = ItemDataMgr.GetItemAssociationColumnDfn(SaxItemAssociation.Column.OrganisationFrom_RegionIdPrimary);
ColumnDfn colItemTypeTo = ItemDataMgr.GetItemAssociationColumnDfn(SaxItemAssociation.Column.ItemTypeTo);
ColumnDfn colItemIdTo = ItemDataMgr.GetItemAssociationColumnDfn(SaxItemAssociation.Column.ItemIdTo);
ColumnDfn colOrganisationTo_Name = ItemDataMgr.GetItemAssociationColumnDfn(SaxItemAssociation.Column.OrganisationTo_Name);
ColumnDfn colOrganisationTo_SportIdPrimary = ItemDataMgr.GetItemAssociationColumnDfn(SaxItemAssociation.Column.OrganisationTo_SportIdPrimary);
ColumnDfn colOrganisationTo_RegionIdPrimary = ItemDataMgr.GetItemAssociationColumnDfn(SaxItemAssociation.Column.OrganisationTo_RegionIdPrimary);
ColumnDfn colAssociationType = ItemDataMgr.GetItemAssociationColumnDfn(SaxItemAssociation.Column.AssociationType);
ColumnDfn colStatus = ItemDataMgr.GetItemAssociationColumnDfn(SaxItemAssociation.Column.Status);
List<ColumnDfn> columns = new List<ColumnDfn>()
{
colId,
colItemTypeFrom, colItemIdFrom, colOrganisationFrom_Name, colOrganisationFrom_SportIdPrimary, colOrganisationFrom_RegionIdPrimary,
colItemTypeTo, colItemIdTo, colOrganisationTo_Name, colOrganisationTo_SportIdPrimary, colOrganisationTo_RegionIdPrimary,
colAssociationType,
colStatus
};
// Query items.
using (DbResultSet resultSet = ItemDataMgr.ExecuteItemAssociationQuery(columns, prmItemAssociation))
{
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
{
NamedValue.Map values = resultSet.GetRecordValues();
AssociationListData associationData = new AssociationListData()
{
Id = values.GetString(colId.DataName),
StatusCode = values.GetString(colStatus.DataName),
StatusDesc = SacsSystem.GetStatusDescForCode(values.GetString(colStatus.DataName)),
};
// Items.
bool associationReversed = false;
ItemId itemIdFrom = new ItemId((short)values.GetLong(colItemTypeFrom.DataName), new SysId(values.GetLong(colItemIdFrom.DataName)));
String organisationNameFrom = values.GetString(colOrganisationFrom_Name.DataName);
SysId sportIdFrom = new SysId(values.GetLong(colOrganisationFrom_SportIdPrimary.DataName));
SysId regionIdFrom = new SysId(values.GetLong(colOrganisationFrom_RegionIdPrimary.DataName));
ItemId itemIdTo = new ItemId((short)values.GetLong(colItemTypeTo.DataName), new SysId(values.GetLong(colItemIdTo.DataName)));
String organisationNameTo = values.GetString(colOrganisationTo_Name.DataName);
SysId sportIdTo = new SysId(values.GetLong(colOrganisationTo_SportIdPrimary.DataName));
SysId regionIdTo = new SysId(values.GetLong(colOrganisationTo_RegionIdPrimary.DataName));
if (ItemId.IsValid(itemIdFrom) && ItemId.IsValid(itemIdTo) && itemIdFrom.Type == (int)Sax.DataItemType.Organisation && itemIdTo.Type == (int)Sax.DataItemType.Organisation)
{
if (itemIdFrom.Id.Equals(organisationId))
{
associationData.Organisation.Id = SysId.ToString(itemIdFrom.Id);
associationData.Organisation.Name = organisationNameFrom;
associationData.OrganisationAssoc.Id = SysId.ToString(itemIdTo.Id);
associationData.OrganisationAssoc.Name = organisationNameTo;
if (!sportIdFrom.Equals(sportIdTo))
associationData.OrganisationAssoc.Sport = model.GetSportName(sportIdTo);
if (!regionIdFrom.Equals(regionIdTo))
associationData.OrganisationAssoc.Sport = model.GetRegionName(regionIdTo);
}
else if (itemIdTo.Id.Equals(organisationId))
{
associationReversed = true;
associationData.Organisation.Id = SysId.ToString(itemIdTo.Id);
associationData.Organisation.Name = organisationNameTo;
associationData.OrganisationAssoc.Id = SysId.ToString(itemIdFrom.Id);
associationData.OrganisationAssoc.Name = organisationNameFrom;
if (!sportIdFrom.Equals(sportIdTo))
associationData.OrganisationAssoc.Sport = model.GetSportName(sportIdFrom);
if (!regionIdFrom.Equals(regionIdTo))
associationData.OrganisationAssoc.Sport = model.GetRegionName(regionIdFrom);
}
else if (SacsSystem.DebugMode)
// Track invalid associations, ie. where neither item is this organisation.
Debugger.Break();
}
else if (SacsSystem.DebugMode)
// Track invalid associations, ie. where either item is not an organisation.
Debugger.Break();
// Association type.
Sax.AssociationType associationType = (Sax.AssociationType)values.GetLong(colAssociationType.DataName);
associationData.Assoc.Populate(associationType, associationReversed);
// Add association to the list.
associations.Add(associationData);
}
}
}
}
//-------------------------------------------------------------------
// Get Organisation Association ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_Association : AjaxResponse_Data
{
// Construction.
public AjaxResponse_Association(SaxModel_Admin model) : base(model) { }
// Association details.
public AssociationItemData Association { get; } = new AssociationItemData();
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Organisation/Ajax/GetAssociation")]
public IActionResult Ajax_GetAssociation(Model_OrganisationAssociations model)
{
String messageTitle = Localiser["caption.organisation.association"];
AjaxResponse_Association responseData = new AjaxResponse_Association(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
// Identify item.
SysId itemAssociationId = SysId.ToSysId(model.ItemAssociationId);
if (!SysId.IsValid(itemAssociationId))
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.specified"]));
// Retrieve association.
SaxItemAssociation itemAssociation = ItemDataMgr.GetItemAssociationById(itemAssociationId);
disposableItems.Add(itemAssociation);
if (itemAssociation == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve Item Association for Id: '{model.ItemAssociationId}'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Identify organisations.
SysId organisationId = SysId.ToSysId(model.OrganisationId);
SysId organisationIdAssoc = null;
if (!SysId.IsValid(organisationId))
{
SaLogger.Warn($"{SysUtil.Fn} Organisation Id must be supplied.");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.data.incomplete"]));
}
// Validate the association.
if (itemAssociation.ItemIdFrom.Type != (int)Sax.DataItemType.Organisation)
{
SaLogger.Warn($"{SysUtil.Fn} Inconsistent association 'From' item type: {AssociationTypeCodes.GetCodeForType(itemAssociation.ItemIdFrom.Type)}. Expected: Organisation.");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.data.inconsistent"]));
}
if (itemAssociation.ItemIdTo.Type != (int)Sax.DataItemType.Organisation)
{
SaLogger.Warn($"{SysUtil.Fn} Inconsistent association 'To' item type: {AssociationTypeCodes.GetCodeForType(itemAssociation.ItemIdTo.Type)}. Expected: Organisation.");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.data.inconsistent"]));
}
// Identify organisations from association items.
// The specified organisation must be one or other of the association's items.
if (itemAssociation.ItemIdFrom.Id.Equals(organisationId))
organisationIdAssoc = itemAssociation.ItemIdTo.Id;
else if (itemAssociation.ItemIdTo.Id.Equals(organisationId))
organisationIdAssoc = itemAssociation.ItemIdFrom.Id;
else
{
SaLogger.Warn($"{SysUtil.Fn} Inconsistent association - Specified organisation is neither item.");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.data.inconsistent"]));
}
// Retrieve Organisation.
SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationId);
disposableItems.Add(organisation);
if (organisation == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve Organisation for Id: '{organisationId}'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.system"]));
}
// Retrieve associated Organisation.
SaxOrganisation organisationAssoc = AccountMgr.GetOrganisationById(organisationIdAssoc);
disposableItems.Add(organisationAssoc);
if (organisationAssoc == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve associated Organisation for Id: '{organisationIdAssoc}'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.system"]));
}
// Construct response.
PopulateAssociationResponseItemData(model, responseData.Association, itemAssociation, organisation, organisationAssoc);
}
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Store Organisation Association ajax request.
//-------------------------------------------------------------------
[HttpPost]
[Route("Organisation/Ajax/StoreAssociation")]
public IActionResult Ajax_StoreAssociation(Model_OrganisationAssociations model)
{
String messageTitle = Localiser["caption.organisation.association"];
AjaxResponse_Association responseData = new AjaxResponse_Association(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Validate item properties.
model.ValidateOrganisationAssociationData(responseData);
if (responseData.HasControlErrors())
return Json(responseData);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
// Organisation.
SysId organisationId = SysId.ToSysId(model.OrganisationId);
SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationId);
disposableItems.Add(organisation);
if (organisation == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve Organisation for Id: {model.OrganisationId}");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.data.incomplete"]));
}
// Associated Organisation.
SysId organisationIdAssoc = SysId.ToSysId(model.OrganisationIdAssoc);
SaxOrganisation organisationAssoc = AccountMgr.GetOrganisationById(organisationIdAssoc);
disposableItems.Add(organisationAssoc);
if (organisationAssoc == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve associated Organisation for Id: {model.OrganisationIdAssoc}");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.data.incomplete"]));
}
if (organisationIdAssoc.Equals(organisationId))
{
SaLogger.Warn($"{SysUtil.Fn} Associated organisation must not be the same as the organisation.");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.data.inconsistent"]));
}
// Association type.
Sax.AssociationType associationType = model.GetAssociationTypeForOption(model.Association.GetAssociationTypeOption());
if ((int)associationType < 1)
{
SaLogger.Warn($"{SysUtil.Fn} Association Type must be specified.");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.data.inconsistent"]));
}
Sax.AssociationTypeOption associationTypeOption = model.Association.GetAssociationTypeOption();
if ((int)associationTypeOption < 1)
{
SaLogger.Warn($"{SysUtil.Fn} Association Type Option must be specified.");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.data.inconsistent"]));
}
if (!model.IsConsistentWith(associationTypeOption, associationType))
{
SaLogger.Warn($"{SysUtil.Fn} Association Type Option ({associationTypeOption}) is inconsistent with Association Type ({associationType}).");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.data.inconsistent"]));
}
// Identify From/To items.
ItemId itemIdFrom = organisation.GetItemId();
ItemId itemIdTo = organisationAssoc.GetItemId();
if (model.IsAssociationTypeDirectionReversed(associationTypeOption))
{
itemIdFrom = organisationAssoc.GetItemId();
itemIdTo = organisation.GetItemId();
}
// Retrieve existing association.
SysId itemAssociationId = SysId.ToSysId(model.ItemAssociationId);
if (SysId.IsValid(itemAssociationId) == StringUtil.ToBool(model.NewItem))
{
// Inconsistentcy between the 'Existing' flag and whether the Item Association Id has been specified.
SaLogger.Warn($"{SysUtil.Fn} Item Association Id " + (StringUtil.ToBool(model.NewItem) ? "specified for new item" : "not specified for existing item") + ".");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.data.inconsistent"]));
}
SaxItemAssociation itemAssociation = ItemDataMgr.GetItemAssociationById(itemAssociationId);
if (itemAssociation == null && StringUtil.ToBool(model.NewItem))
{
// Check whether there's a matching association for the 'new' association with 'deleted' status.
SaxItemAssociation.Params prmItemAsociation = new SaxItemAssociation.Params();
prmItemAsociation.ItemIdsIncluded.Add(organisation.GetItemId());
prmItemAsociation.AssociationTypesIncluded.Add(associationType);
prmItemAsociation.StatusCat = SaxItemAssociation.StatusCat.DeletedOnly;
foreach (SaxItemAssociation itemAssociationCandidate in ItemDataMgr.GetItemAssociationList(prmItemAsociation))
{
if (itemAssociationCandidate.ItemIdFrom.Equals(organisationAssoc.GetItemId()) || itemAssociationCandidate.ItemIdTo.Equals(organisationAssoc.GetItemId()))
{
itemAssociation = itemAssociationCandidate;
itemAssociation.Status = SaxItemAssociation.DefaultStatus;
break;
}
disposableItems.Add(itemAssociationCandidate);
}
}
disposableItems.Add(itemAssociation);
if (itemAssociation == null && SysId.IsValid(itemAssociationId))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve item association for Id: '{itemAssociationId}'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
if (itemAssociation != null)
{
// Check if item relationship has been reversed, eg. from parent to child.
bool associationReversed = itemAssociation.ItemIdTo.Id.Equals(organisation.Id);
if (model.IsAssociationTypeDirectionReversed(associationTypeOption) != model.IsAssociationTypeDirectionReversed(AssociationTypeData.GetAssociationTypeOption(itemAssociation.AssociationType, associationReversed)))
{
// Logical direction has been reversed from that stored.
// Items must be switched.
ItemId itemAssociationItemIdFrom = itemAssociation.ItemIdFrom;
itemAssociation.ItemIdFrom = itemAssociation.ItemIdTo;
itemAssociation.ItemIdTo = itemAssociationItemIdFrom;
}
// Validate From/To items.
if (!itemAssociation.ItemIdFrom.Equals(itemIdFrom))
{
SaLogger.Warn($"{SysUtil.Fn} Inconsistent association - expected 'From' Item Id: {SysId.ToString(itemIdFrom.Id)}. Found: {SysId.ToString(itemAssociation.ItemIdFrom.Id)}");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.system"]));
}
if (!itemAssociation.ItemIdTo.Equals(itemIdTo))
{
SaLogger.Warn($"{SysUtil.Fn} Inconsistent association - expected 'To' Item Id: {SysId.ToString(itemIdTo.Id)}. Found: {SysId.ToString(itemAssociation.ItemIdTo.Id)}");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.system"]));
}
}
else
{
// Item Association not identified.
// Check for deleted association that can be reused.
SaxItemAssociation.Params itemAssociationParams = new SaxItemAssociation.Params();
if (itemAssociation != null)
itemAssociation.Status = SaxItemAssociation.DefaultStatus;
else
{
// Association not found - create a new one.
itemAssociation = ItemDataMgr.CreateItemAssociation(itemIdFrom, itemIdTo, associationType);
}
disposableItems.Add(itemAssociation);
}
// Populate item properties.
if (model.Association.StatusCode != null)
itemAssociation.Status = model.Association.GetStatus();
// Store item.
if (!SysId.IsValid(itemAssociation.Id))
{
if (!ItemDataMgr.InsertItemAssociation(itemAssociation, DbUpdateInfo))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to insert item association: {ItemDataMgr.ErrorStateMessage}");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.insert.failed"] + "\n\n" + ItemDataMgr.ErrorStateMessage));
}
}
else
{
if (!ItemDataMgr.UpdateItemAssociation(itemAssociation, DbUpdateInfo))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to update item association [Id:{SysId.ToString(itemAssociation.Id)}]: {ItemDataMgr.ErrorStateMessage}");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + ItemDataMgr.ErrorStateMessage));
}
}
// Construct response.
PopulateAssociationResponseItemData(model, responseData.Association, itemAssociation, organisation, organisationAssoc);
}
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Remove Organisation Association ajax request.
//-------------------------------------------------------------------
[HttpPost]
[Route("Organisation/Ajax/RemoveAssociation")]
public IActionResult Ajax_RemoveAssociation(Model_OrganisationAssociations model)
{
String messageTitle = Localiser["caption.organisation.association"];
AjaxResponse_Association responseData = new AjaxResponse_Association(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
// Organisation.
SysId organisationId = SysId.ToSysId(model.OrganisationId);
SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationId);
disposableItems.Add(organisation);
if (organisation == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve Organisation for Id: '{model.OrganisationId}'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.data.incomplete"]));
}
// Association Organisation.
SysId organisationIdAssoc = SysId.ToSysId(model.OrganisationIdAssoc);
SaxOrganisation organisationAssoc = AccountMgr.GetOrganisationById(organisationIdAssoc);
disposableItems.Add(organisationAssoc);
if (organisationAssoc == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve associated Organisation for Id: '{model.OrganisationIdAssoc}'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.data.incomplete"]));
}
if (organisationIdAssoc.Equals(organisationId))
{
SaLogger.Warn($"{SysUtil.Fn} Associated organisation must not be the same as the organisation.");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.data.inconsistent"]));
}
// Item Association.
SysId itemAssociationId = SysId.ToSysId(model.ItemAssociationId);
SaxItemAssociation itemAssociation = ItemDataMgr.GetItemAssociationById(itemAssociationId);
disposableItems.Add(itemAssociation);
if (itemAssociation == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve Item Association for Id: '{itemAssociationId}'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Validate association Item Ids.
if (!itemAssociation.ItemIdFrom.Id.Equals(organisation.Id) && !itemAssociation.ItemIdTo.Id.Equals(organisation.Id))
{
SaLogger.Warn($"{SysUtil.Fn} Inconsistent item association properties. Organisation must be 'From' or 'To' item");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.data.inconsistent"]));
}
if (!itemAssociation.ItemIdFrom.Id.Equals(organisationAssoc.Id) && !itemAssociation.ItemIdTo.Id.Equals(organisationAssoc.Id))
{
SaLogger.Warn($"{SysUtil.Fn} Inconsistent item association properties. Associated Organisation must be 'From' or 'To' item");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.data.inconsistent"]));
}
// Logically delete item.
itemAssociation.Status = SaStatus.Deleted;
if (!ItemDataMgr.UpdateItemAssociation(itemAssociation, DbUpdateInfo))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to update Item Association [Id:{SysId.ToString(itemAssociation.Id)}]: {ItemDataMgr.ErrorStateMessage}");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + ItemDataMgr.ErrorStateMessage));
}
// Construct response.
PopulateAssociationResponseItemData(model, responseData.Association, itemAssociation, organisation, organisationAssoc);
}
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,315 @@
using Microsoft.AspNetCore.Http;
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.Helpers;
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.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Admin.Controllers
{
//=======================================================================
// Controller: Organisation - Ajax - Images
//=======================================================================
partial class OrganisationController
{
//-------------------------------------------------------------------
private class OrganisationImagesData : OrganisationItemData
{
}
private void PopulateOrganisationResponseImagesData(SaxOrganisation organisation, SaxItemAddress address, OrganisationImagesData data)
{
PopulateOrganisationResponseItemData(organisation, address, data);
}
//-------------------------------------------------------------------
// Get Organisation Images ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_OrganisationImages : AjaxResponse_Data
{
// Construction.
public AjaxResponse_OrganisationImages(SaxModel_Admin model) : base(model) { }
// Organisation details.
public OrganisationImagesData organisation { get; } = new OrganisationImagesData();
// Reference data.
public List<String> ImageContentTypes { get; set; }
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Organisation/Ajax/GetImages")]
[Route("Organisation/Ajax/GetItemImages")]
public IActionResult Ajax_GetImages(Model_Organisation model)
{
String messageTitle = Localiser["caption.organisation"];
AjaxResponse_OrganisationImages responseData = new AjaxResponse_OrganisationImages(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
// Reference data - Image content types.
responseData.ImageContentTypes = SaxItemImage.GetValidContentTypes(Sax.DataItemType.Organisation);
// Identify item.
SysId organisationId = SysId.ToSysId(model.OrganisationId);
if (!SysId.IsValid(organisationId))
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.specified"]));
// Retrieve item.
SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationId);
disposableItems.Add(organisation);
if (organisation == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve organisation for Id: '{model.OrganisationId}'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Organisation address.
SaxItemAddress address = ItemDataMgr.GetFirstItemAddressByItemAndType(organisation.GetItemId());
disposableItems.Add(address);
// Construct response.
PopulateOrganisationResponseImagesData(organisation, address, responseData.organisation);
// Complete response.
responseData.Success = true;
}
return Json(responseData);
}
//-------------------------------------------------------------------
// Account - Upload Contact Image.
// Ref. http://www.binaryintellect.net/articles/474dc031-087f-4b81-b994-9ca2adb949d6.aspx
//-------------------------------------------------------------------
private class AjaxResponse_UploadOrganisationLogoImage : AjaxResponse_Data
{
// Construction.
public AjaxResponse_UploadOrganisationLogoImage(SaxModel_Admin model) : base(model) { }
// Uploaded image URL.
public String ImageUrl { get; set; }
// Upload message.
public String UploadMsg { get; set; }
}
// Ajax request.
[HttpPost]
[Route("Organisation/Ajax/UploadLogo")]
[Route("Organisation/Ajax/UploadLogoImage")]
public IActionResult UploadOrganisationLogoImage(Model_Organisation model)
{
AjaxResponse_UploadOrganisationLogoImage responseData = new AjaxResponse_UploadOrganisationLogoImage(model);
String messageTitle = Localiser["caption.organisation"];
// 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>())
{
// Retrieve organisation.
SysId organisationId = SysId.ToSysId(model.OrganisationId);
if (!SysId.IsValid(organisationId))
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.specified"]));
SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationId);
disposableItems.Add(organisation);
if (organisation == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve organisation for Id: '{model.OrganisationId}'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Validate number of images.
if (model.OrganisationLogoImage.IsEmpty())
{
SaLogger.Warn($"{SysUtil.Fn} No image file supplied for upload");
return Json(responseData);
}
if (model.OrganisationLogoImage.Count != 1)
{
SaLogger.Warn($"{SysUtil.Fn} Invalid number of image files uploaded: {model.OrganisationLogoImage.Count}");
return Json(responseData.SetControlError("OrganisationLogoImage", Localiser["msg.upload.image.single"]));
}
IFormFile file = model.OrganisationLogoImage[0];
// Image properties.
byte[] imageData = null;
using (MemoryStream fileStream = new MemoryStream())
{
file.CopyTo(fileStream);
imageData = fileStream.ToArray();
}
// Validate content type.
if (StringUtil.IsEmpty(file.ContentType) || !SaxItemImage.GetValidContentTypes(Sax.DataItemType.Organisation).ContainsCI(file.ContentType))
return Json(responseData.SetControlError("OrganisationLogoImage", Localiser["msg.upload.image.invalid.content.type"]));
Sarand.Net.Standard.Base.Core.Sacs.MimeType mimeTypeExpected = SacsContentTypeHelper.GetMimeTypeFromContentType(file.ContentType);
// Validate the image.
ImageUtils.ImageConfig imageConfig = null;
if (mimeTypeExpected == Sarand.Net.Standard.Base.Core.Sacs.MimeType.Svg)
imageConfig = ImageUtils.VerifySvgImageData(imageData);
else
imageConfig = ImageUtils.VerifyImageData(imageData);
if (!imageConfig.ValidImage)
return Json(responseData.SetControlError("OrganisationLogoImage", Localiser["msg.upload.image.invalid"]));
// Validate mime type.
if (!SaxItemImage.GetValidMimeTypes(Sax.DataItemType.Organisation).Contains(imageConfig.MimeType))
return Json(responseData.SetControlError("OrganisationLogoImage", Localiser["msg.upload.image.invalid.mime"]));
if (imageConfig.MimeType != mimeTypeExpected)
{
SaLogger.Warn($"{SysUtil.Fn} Image upload - inconsistent Mime. Filename: {file.FileName}. Mime: {imageConfig.MimeType}. Expected Mime: {mimeTypeExpected}.");
return Json(responseData.SetControlError("OrganisationLogoImage", Localiser["msg.upload.image.invalid"]));
}
// Resize the image if required.
if (!ImageUtils.ConstrainImage(imageConfig, Sax.Config.Organisation.Logo.WidthMax, Sax.Config.Organisation.Logo.HeightMax))
return Json(responseData.SetControlError("OrganisationLogoImage", Localiser["msg.upload.image.resize.failed"]));
// Image belongs to the organisation.
ItemId organisationItemId = organisation.GetItemId();
// Store uploaded image as 'Upload' Logo - awaiting confirmation that the details are to be saved.
SaxItemImage itemImage = ItemDataMgr.GetItemImage(organisationItemId, Sax.ImageType.OrganisationLogo_Upload, SaxItemImage.StatusCat.Any);
if (itemImage == null)
itemImage = ItemDataMgr.CreateItemImage(organisationItemId, Sax.ImageType.OrganisationLogo_Upload);
itemImage.Filename = file.FileName;
itemImage.Mime = SacsContentTypeHelper.GetMimeTypeFromContentType(file.ContentType).ToString().ToLower();
itemImage.ContentType = file.ContentType;
itemImage.ImageData = imageConfig.ImageData;
itemImage.Width = imageConfig.Width;
itemImage.Height = imageConfig.Height;
itemImage.Status = SaxItemImage.DefaultStatus;
if (!SysId.IsValid(itemImage.Id))
{
if (!ItemDataMgr.InsertItemImage(itemImage, DbUpdateInfo))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to insert uploaded image - {ItemDataMgr.ErrorStateMessage}");
return Json(responseData.SetControlError("OrganisationLogoImage", Localiser["msg.error.item.insert.failed"]));
}
}
else
{
if (!ItemDataMgr.UpdateItemImage(itemImage, DbUpdateInfo))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to update uploaded image - {ItemDataMgr.ErrorStateMessage}");
return Json(responseData.SetControlError("OrganisationLogoImage", Localiser["msg.error.item.insert.failed"]));
}
}
// Return image url with response.
responseData.ImageUrl = GetOrganisationLogoLink(organisation.Id, true);
}
// Complete response.
responseData.SetNotifyMessage(messageTitle, "Image uploaded.");
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Store Organisation Images ajax request.
//-------------------------------------------------------------------
[HttpPost]
[Route("Organisation/Ajax/StoreImages")]
public IActionResult Ajax_StoreImages(Model_Organisation model)
{
String messageTitle = Localiser["caption.organisation"];
AjaxResponse_OrganisationImages responseData = new AjaxResponse_OrganisationImages(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
// Validate item properties.
model.ValidateOrganisationData(responseData);
if (responseData.HasControlErrors())
return Json(responseData);
// Retrieve organisation.
SysId organisationId = SysId.ToSysId(model.OrganisationId);
SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationId);
disposableItems.Add(organisation);
if (organisation == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve organisation for Id: '{model.OrganisationId}'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Organisation address.
SaxItemAddress address = ItemDataMgr.GetFirstItemAddressByItemAndType(organisation.GetItemId());
disposableItems.Add(address);
// Check for uploaded logo image.
ItemId organisationItemId = organisation.GetItemId();
SaxItemImage itemImageLogoUploaded = ItemDataMgr.GetItemImage(organisationItemId, Sax.ImageType.OrganisationLogo_Upload, SaxItemImage.StatusCat.ActiveOnly);
disposableItems.Add(itemImageLogoUploaded);
if (itemImageLogoUploaded != null)
{
// Retrieve current OrganisationLogo image.
SaxItemImage itemImageLogo = ItemDataMgr.GetItemImage(organisationItemId, Sax.ImageType.OrganisationLogo, SaxItemImage.StatusCat.Any);
if (itemImageLogo == null)
itemImageLogo = ItemDataMgr.CreateItemImage(organisationItemId, Sax.ImageType.OrganisationLogo);
disposableItems.Add(itemImageLogo);
// Copy properties from uploaded image.
itemImageLogo.Filename = itemImageLogoUploaded.Filename;
itemImageLogo.Mime = itemImageLogoUploaded.Mime;
itemImageLogo.ContentType = itemImageLogoUploaded.ContentType;
itemImageLogo.Width = itemImageLogoUploaded.Width;
itemImageLogo.Height = itemImageLogoUploaded.Height;
itemImageLogo.ImageData = itemImageLogoUploaded.ImageData;
itemImageLogo.Status = SaxItemImage.DefaultStatus;
if (!SysId.IsValid(itemImageLogo.Id))
{
if (!ItemDataMgr.InsertItemImage(itemImageLogo, DbUpdateInfo))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to insert logo image - {ItemDataMgr.ErrorStateMessage}");
return Json(responseData.SetControlError("AccountContactImage", Localiser["msg.error.item.insert.failed"]));
}
}
else
{
if (!ItemDataMgr.UpdateItemImage(itemImageLogo, DbUpdateInfo))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to update logo image - {ItemDataMgr.ErrorStateMessage}");
return Json(responseData.SetControlError("AccountContactImage", Localiser["msg.error.item.update.failed"]));
}
}
// Logically delete the uplaoded image.
itemImageLogoUploaded.Status = SaStatus.Deleted;
if (!ItemDataMgr.UpdateItemImage(itemImageLogoUploaded, DbUpdateInfo))
SaLogger.Warn($"{SysUtil.Fn} Failed to logically delete uploaded logo image - {ItemDataMgr.ErrorStateMessage}");
}
// Construct response.
PopulateOrganisationResponseImagesData(organisation, address, responseData.organisation);
}
// Complete response.
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,466 @@
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
{
//=======================================================================
// Controller: Organisation - Ajax - Members
//=======================================================================
partial class OrganisationController
{
//-------------------------------------------------------------------
// Member data.
private class MemberListData : AjaxResponse_Data.ItemData_Base
{
// User.
public String UserAccountId { get; set; }
public bool? Self { get; set; } = null;
public String EmailAddress { get; set; }
public String DisplayName { get; set; }
public String UserStatusCode { get; set; }
public String UserStatusDesc { get; set; }
public bool? UserActive { get { return UserStatusCode.EqualsCI(SacsSystem.GetStatusCode(SaStatus.Active)) ? true : (bool?)null; } }
// Organisation.
public String OrganisationId { get; set; }
public String OrganisationName { get; set; }
// Organisation user.
public int RoleCount { get; set; }
public bool? Member { get; set; } = null;
}
private class MemberItemData : MemberListData
{
// Organisation user.
public List<String> RoleCodes { get; } = new List<string>();
}
private void PopulateMemberResponseListData(MemberListData data, SaxUserOrganisation userOrganisation, SaxUserAccount userAccount, SaxOrganisation organisation)
{
data.Id = SysId.ToString(userOrganisation.Id);
// User.
data.UserAccountId = SysId.ToString(userAccount.Id);
if (userAccount.Id.Equals(SessionUserAccountId)) data.Self = true;
data.EmailAddress = userAccount.EmailAddress;
data.DisplayName = userAccount.GetDisplayName();
data.UserStatusCode = userAccount.StatusCode;
data.UserStatusDesc = userAccount.StatusDesc;
// Organisation.
data.OrganisationId = SysId.ToString(organisation.Id);
data.OrganisationName = organisation.Name;
// Organisation user:
data.RoleCount = userOrganisation.OrganisationRoleCount - 1;
data.StatusCode = userOrganisation.StatusCode;
data.StatusDesc = userOrganisation.StatusDesc;
if (userOrganisation.GetOrganisationRoles().ToList().Exists(item => item == Sax.OrganisationRole.Member))
data.Member = true;
}
private void PopulateMemberResponseItemData(MemberItemData data, SaxUserOrganisation userOrganisation, SaxUserAccount userAccount, SaxOrganisation organisation)
{
PopulateMemberResponseListData(data, userOrganisation, userAccount, organisation);
// Organisation user:
// Exclude 'Member' role.
foreach (Sax.OrganisationRole organisationRole in userOrganisation.GetOrganisationRoles().ToList().FindAll(item => item != Sax.OrganisationRole.Member))
data.RoleCodes.Add(OrganisationRoleCodes.GetCodeForType(organisationRole));
}
//-------------------------------------------------------------------
// Get Organisation Member List ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_MemberList : AjaxResponse_Data
{
// Construction.
public AjaxResponse_MemberList(SaxModel_Admin model) : base(model) { }
// Member list.
public List<MemberListData> Members { get; } = new List<MemberListData>();
// Reference data.
public List<CodeDesc> MemberRoles { get; } = new List<CodeDesc>();
public List<CodeDesc> Statuses { get; } = new List<CodeDesc>();
public String DefaultStatusCode { get; set; }
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Organisation/Ajax/GetMemberList")]
public IActionResult Ajax_GetMemberList(Model_OrganisationMembers model)
{
String messageTitle = Localiser["caption.organisation.members"];
AjaxResponse_MemberList responseData = new AjaxResponse_MemberList(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Organisation Id filter is mandatory.
SysId organisationId = SysId.ToSysId(model.Filter.OrganisationId);
if (!SysId.IsValid(organisationId))
{
SaLogger.Warn($"{SysUtil.Fn} Request to retrieve members without valid organisation Id.");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.specified"]));
}
// Reference data - organisation roles.
// Exclude 'Member' as this is added automatically when stored.
foreach (TypeCodeDesc organisationRole in OrganisationRoleCodes.FindAll(item => item.Type != (int)Sax.OrganisationRole.Member))
responseData.MemberRoles.Add(organisationRole.Code, Localiser.GetOrganisationRoleCaption((Sax.OrganisationRole)organisationRole.Type));
// Reference data - statuses.
foreach (SaStatus status in SaxUserOrganisation.ValidStatuses.FindAll(item => item != SaStatus.Deleted))
responseData.Statuses.Add(SacsSystem.GetStatusCode(status), Localiser.GetStatusCaption(status));
responseData.DefaultStatusCode = SacsSystem.GetStatusCode(SaxUserOrganisation.DefaultStatus);
// Get organisation members.
GetOrganisationMembers(model, organisationId, responseData.Members, false);
if (model.Paging.IsPaged)
model.Paging.ItemCount = (ushort)responseData.Members.Count;
// Complete response.
responseData.SetPaging(model);
responseData.Success = true;
return Json(responseData);
}
private void GetOrganisationMembers(SaxModel_Admin model, SysId organisationId, List<MemberListData> members, bool inactiveOnly)
{
// Item filter.
SaxUserOrganisation.Params prmUserOrganisation = new SaxUserOrganisation.Params();
prmUserOrganisation.OrganisationIdsIncluded.Add(organisationId);
prmUserOrganisation.OrganisationMembersOnly = true;
prmUserOrganisation.UserAccountStatusCat = inactiveOnly ? SaxUserAccount.StatusCat.InactiveOnly : SaxUserAccount.StatusCat.NotDeleted;
prmUserOrganisation.StatusCat = SaxUserOrganisation.StatusCat.NotDeleted;
model.Paging.ItemTotal = (ulong)AccountMgr.GetUserOrganisationCount(prmUserOrganisation);
model.PopulatePagingTo(prmUserOrganisation.ResultsPaging);
// Item columns.
ColumnDfn colId = AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.Id);
ColumnDfn colUserAccountId = AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.UserAccountId);
ColumnDfn colUserEmailAddress = AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.User_EmailAddress);
ColumnDfn colUserDisplayName = AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.User_DisplayName);
ColumnDfn colUserStatus = AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.User_Status);
ColumnDfn colOrganisationId = AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.OrganisationId);
ColumnDfn colOrganisationName = AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.Organisation_Name);
ColumnDfn colRoleCodes = AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.OrganisationRoleCodes);
ColumnDfn colRoleCount = AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.OrganisationRoleCount);
ColumnDfn colStatus = AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.Status);
List<ColumnDfn> columns = new List<ColumnDfn>()
{
colId, colUserAccountId,
colUserEmailAddress, colUserDisplayName, colUserStatus,
colOrganisationId, colOrganisationName,
colRoleCodes, colRoleCount,
colStatus
};
// Query items.
using (DbResultSet resultSet = AccountMgr.ExecuteUserOrganisationQuery(columns, prmUserOrganisation))
{
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
{
NamedValue.Map values = resultSet.GetRecordValues();
MemberListData memberData = new MemberListData()
{
Id = values.GetString(colId.DataName),
// User.
UserAccountId = values.GetString(colUserAccountId.DataName),
EmailAddress = values.GetString(colUserEmailAddress.DataName),
DisplayName = AccountMgr.GetUserOrganisationDataValue(colUserDisplayName, values).ToString(),
UserStatusCode = values.GetString(colUserStatus.DataName),
UserStatusDesc = SacsSystem.GetStatusDescForCode(values.GetString(colUserStatus.DataName)),
// Organisation.
OrganisationId = values.GetString(colOrganisationId.DataName),
OrganisationName = values.GetString(colOrganisationName.DataName),
// Role count - 1 to ignore 'Member' role.
RoleCount = (int)values.GetLong(colRoleCount.DataName) - 1,
StatusCode = values.GetString(colStatus.DataName),
StatusDesc = SacsSystem.GetStatusDescForCode(values.GetString(colStatus.DataName)),
Member = (values.GetLong(colRoleCount.DataName) > 0) ? true : (bool?)null
};
members.Add(memberData);
}
}
}
//-------------------------------------------------------------------
// Get Organisation Member ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_Member : AjaxResponse_Data
{
// Construction.
public AjaxResponse_Member(SaxModel_Admin model) : base(model) { }
// Member details.
public MemberItemData Member { get; } = new MemberItemData();
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Organisation/Ajax/GetMember")]
public IActionResult Ajax_GetMember(Model_OrganisationMembers model)
{
String messageTitle = Localiser["caption.organisation.member"];
AjaxResponse_Member responseData = new AjaxResponse_Member(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
// Identify item.
SysId userOrganisationId = SysId.ToSysId(model.UserOrganisationId);
if (!SysId.IsValid(userOrganisationId))
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.specified"]));
// Retrieve item.
SaxUserOrganisation userOrganisation = AccountMgr.GetUserOrganisationById(userOrganisationId);
disposableItems.Add(userOrganisation);
if (userOrganisation == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve User Organisation for Id: '{model.UserOrganisationId}'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Retrieve User Account.
SaxUserAccount userAccount = AccountMgr.GetUserAccountById(userOrganisation.UserAccountId);
disposableItems.Add(userAccount);
if (userAccount == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve user account for Id: '{userOrganisation.UserAccountId}'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Retrieve Organisation.
SaxOrganisation organisation = AccountMgr.GetOrganisationById(userOrganisation.OrganisationId);
disposableItems.Add(organisation);
if (organisation == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve organisation for Id: '{userOrganisation.OrganisationId}'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Construct response.
PopulateMemberResponseItemData(responseData.Member, userOrganisation, userAccount, organisation);
}
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Store Organisation Member ajax request.
//-------------------------------------------------------------------
[HttpPost]
[Route("Organisation/Ajax/StoreMember")]
public IActionResult Ajax_StoreMember(Model_OrganisationMembers model)
{
String messageTitle = Localiser["caption.organisation.member"];
AjaxResponse_Member responseData = new AjaxResponse_Member(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Validate item properties.
model.ValidateOrganisationMemberData(responseData);
if (responseData.HasControlErrors())
return Json(responseData);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
// User account.
SysId userAccountId = SysId.ToSysId(model.MemberUserAccountId);
SaxUserAccount userAccount = null;
if (!SysId.IsValid(userAccountId) && !StringUtil.IsEmpty(model.MemberUserEmailAddress))
{
userAccount = AccountMgr.GetUserAccountByEmailAddress(model.MemberUserEmailAddress);
disposableItems.Add(userAccount);
userAccountId = userAccount?.Id ?? new SysId();
}
if (!SysId.IsValid(userAccountId))
{
SaLogger.Warn($"{SysUtil.Fn} User Account must be identified");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.data.incomplete"]));
}
// Organisation.
SysId organisationId = SysId.ToSysId(model.MemberOrganisationId);
SaxOrganisation organisation = null;
if (!SysId.IsValid(organisationId))
{
SaLogger.Warn($"{SysUtil.Fn} Organisation Id must be specified");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.data.incomplete"]));
}
// Retrieve existing item.
SysId userOrganisationId = SysId.ToSysId(model.UserOrganisationId);
SaxUserOrganisation userOrganisation = AccountMgr.GetUserOrganisationById(userOrganisationId);
disposableItems.Add(userOrganisation);
if (userOrganisation == null && SysId.IsValid(userOrganisationId))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve user organisation for Id: '{userOrganisationId}'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
if (userOrganisation != null && (!userOrganisation.UserAccountId.Equals(userAccountId) || !userOrganisation.OrganisationId.Equals(organisationId)))
{
// Either User Account and/or Organisation Ids don't match.
SaLogger.Warn($"{SysUtil.Fn} Inconsistent user organisation properties. UserAccountId: {userOrganisation.UserAccountId} vs {userAccountId}. OrganisationId: {userOrganisation.OrganisationId} vs {organisationId}.");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// User Organisation not identified.
if (userOrganisation == null)
{
// See whether user has an 'interest' in the organisation.
SaxUserOrganisation.Params prmUserOrganisation = new SaxUserOrganisation.Params();
prmUserOrganisation.UserAccountIdsIncluded.Add(userAccountId);
prmUserOrganisation.OrganisationIdsIncluded.Add(organisationId);
prmUserOrganisation.OrganisationMembersOnly = false;
prmUserOrganisation.StatusCat = SaxUserOrganisation.StatusCat.Any;
userOrganisation = AccountMgr.GetUserOrganisation(prmUserOrganisation);
if (userOrganisation != null)
userOrganisation.Status = SaStatus.Active;
else
// Organisation not found for user.
userOrganisation = AccountMgr.CreateUserOrganisation(userAccountId, organisationId);
disposableItems.Add(userOrganisation);
}
// Retrieve associated items.
if (userAccount == null)
{
userAccount = AccountMgr.GetUserAccountById(userAccountId);
disposableItems.Add(userAccount);
}
if (organisation == null)
{
organisation = AccountMgr.GetOrganisationById(organisationId);
disposableItems.Add(organisation);
}
// Populate item properties.
if (model.MemberRoleCodes != null)
userOrganisation.OrganisationRoles = SaxUserOrganisation.GetOrganisationRolesFromCodes(model.GetMemberRoleCodes());
userOrganisation.AddOrganisationRole(Sax.OrganisationRole.Member);
if (model.MemberStatusCode != null)
userOrganisation.Status = model.MemberStatus;
// Store item.
if (!SysId.IsValid(userOrganisation.Id))
{
if (!AccountMgr.InsertUserOrganisation(userOrganisation, DbUpdateInfo))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to insert user organisation: {AccountMgr.ErrorStateMessage}");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.insert.failed"] + "\n\n" + AccountMgr.ErrorStateMessage));
}
}
else
{
if (!AccountMgr.UpdateUserOrganisation(userOrganisation, DbUpdateInfo))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to update user organisation [Id:{SysId.ToString(userOrganisation.Id)}]: {AccountMgr.ErrorStateMessage}");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + AccountMgr.ErrorStateMessage));
}
}
// Construct response.
PopulateMemberResponseItemData(responseData.Member, userOrganisation, userAccount, organisation);
}
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Remove Organisation Member ajax request.
//-------------------------------------------------------------------
[HttpPost]
[Route("Organisation/Ajax/RemoveMember")]
public IActionResult Ajax_RemoveMember(Model_OrganisationMembers model)
{
String messageTitle = Localiser["caption.organisation.member"];
AjaxResponse_Member responseData = new AjaxResponse_Member(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
// Organisation.
SysId organisationId = SysId.ToSysId(model.OrganisationId);
SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationId);
if (organisation == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve Organisation for Id: '{model.OrganisationId}'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.data.incomplete"]));
}
disposableItems.Add(organisation);
// User Organisation.
SysId userOrganisationId = SysId.ToSysId(model.UserOrganisationId);
SaxUserOrganisation userOrganisation = AccountMgr.GetUserOrganisationById(userOrganisationId);
disposableItems.Add(userOrganisation);
if (userOrganisation == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve user organisation for Id: '{userOrganisationId}'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
if (!userOrganisation.OrganisationId.Equals(organisation.Id))
{
SaLogger.Warn($"{SysUtil.Fn} Inconsistent user organisation properties. OrganisationId: {userOrganisation.OrganisationId} vs {organisation.Id}.");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.data.inconsistent"]));
}
// User Account.
SaxUserAccount userAccount = AccountMgr.GetUserAccountById(userOrganisation.UserAccountId);
disposableItems.Add(userAccount);
if (userAccount == null)
{
SaLogger.Warn($"{SysUtil.Fn} Failed to retrieve user account for Id: '{userOrganisation.UserAccountId}'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.system"]));
}
// Remove all roles and store.
userOrganisation.ClearOrganisationRoles();
// Store item.
if (!SysId.IsValid(userOrganisation.Id))
{
if (!AccountMgr.InsertUserOrganisation(userOrganisation, DbUpdateInfo))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to insert user organisation: {AccountMgr.ErrorStateMessage}");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.insert.failed"] + "\n\n" + AccountMgr.ErrorStateMessage));
}
}
else
{
if (!AccountMgr.UpdateUserOrganisation(userOrganisation, DbUpdateInfo))
{
SaLogger.Warn($"{SysUtil.Fn} Failed to update user organisation [Id:{SysId.ToString(userOrganisation.Id)}]: {AccountMgr.ErrorStateMessage}");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + AccountMgr.ErrorStateMessage));
}
}
// Construct response.
PopulateMemberResponseItemData(responseData.Member, userOrganisation, userAccount, organisation);
}
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,147 @@
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.Email;
using Sarand.Net.Standard.Base.Utils;
using Saxonite.Net.Core.Web.Admin.Consoles;
using Saxonite.Net.Core.Web.Admin.Models;
using Saxonite.Net.Core.Web.Admin.Procs;
using Saxonite.Net.Standard.Base.App;
using Saxonite.Net.Standard.Base.Bus;
using Saxonite.Net.Standard.Base.Helpers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Admin.Controllers
{
//=======================================================================
// Controller: Organisation - Ajax - Upload
//=======================================================================
partial class OrganisationController
{
//-------------------------------------------------------------------
// Definitions.
//-------------------------------------------------------------------
// Organisation Uploader process info.
//-------------------------------------------------------------------
private class AjaxResponse_OrganisationUploadInfo : AjaxResponse_Data
{
// Construction.
public AjaxResponse_OrganisationUploadInfo(SaxModel_Admin model) : base(model) { }
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Organisation/Ajax/UploadInfo")]
public IActionResult Ajax_OrganisationUploadInfo(SaxModel_Admin model)
{
String fn = GetType().Name + ".Ajax_OrganisationUploadInfo";
String messageTitle = Localiser["caption.upload.organisations"];
AjaxResponse_OrganisationUploadInfo responseData = new AjaxResponse_OrganisationUploadInfo(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
// Process console.
SaxProcessConsole_Admin processConsole = GetProcessConsole_Admin(SessionContext);
if (processConsole == null)
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.system"]));
try
{
// Check whether upload is in progress.
if (processConsole.IsProcessing(SaxProcessConsole_Admin.ProcessType.OrganisationUpload))
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.process.in.progress"]));
}
catch (Exception exc)
{
SaLogger.Warn(fn + " " + exc.GetType().Name + " initialising organisation uploader.");
return Json(responseData.SetPopupMessage(messageTitle, exc.GetType().Name + " initialising organisation uploader."));
}
}
// Complete response.
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Upload Organisations.
//-------------------------------------------------------------------
private class AjaxResponse_OrganisationUpload : AjaxResponse_Data
{
// Construction.
public AjaxResponse_OrganisationUpload(SaxModel_Admin model) : base(model) { }
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Organisation/Ajax/Upload")]
public IActionResult Ajax_UploadOrganisations(Model_OrganisationUpload model)
{
String fn = GetType().Name + ".Ajax_UploadOrganisations";
String messageTitle = Localiser["caption.upload.organisations"];
AjaxResponse_OrganisationUpload responseData = new AjaxResponse_OrganisationUpload(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
// Validate supplied data properties.
if (!model.ValidateProperties(responseData))
return Json(responseData);
// Process console.
SaxProcessConsole_Admin processConsole = GetProcessConsole_Admin(SessionContext);
if (processConsole == null)
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.system"]));
try
{
// Check whether upload is in progress.
if (processConsole.IsProcessing(SaxProcessConsole_Admin.ProcessType.OrganisationUpload))
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.process.in.progress"]));
// Create the upload process.
SaxOrganisationUploader.Config processConfig = new SaxOrganisationUploader.Config(SessionContext, DbUpdateInfo, model.OrganisationDataFile[0]);
processConfig.SheetNameOrNumber = model.SheetNameOrNumber;
processConfig.HeaderRowPos = (uint)StringUtil.ToInt(model.HeaderRow);
processConfig.FirstDataRowPos = (uint)StringUtil.ToInt(model.FirstDataRow);
SaxOrganisationUploader process = new SaxOrganisationUploader(processConfig);
// Store the process and run.
processConsole.SetProcess(process);
process.RunAsync();
}
catch (Exception exc)
{
SaLogger.Warn(fn + " " + exc.GetType().Name + " initialising organisation uploader.");
return Json(responseData.SetPopupMessage(messageTitle, exc.GetType().Name + " initialising organisation uploader."));
}
}
// Complete response.
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
}
}
@@ -0,0 +1,491 @@
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.Core.Web.Admin.Procs;
using Saxonite.Net.Standard.Base.App;
using Saxonite.Net.Standard.Base.Bus;
using Saxonite.Net.Standard.Base.Mgr;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Admin.Controllers
{
//=======================================================================
// Controller: Organisation - Ajax
//=======================================================================
partial class OrganisationController
{
//-------------------------------------------------------------------
// Organisation data.
private class OrganisationListData : AjaxResponse_Data.ItemData_Base
{
public String Name { get; set; }
public String TypeCode { get; set; }
public String TypeDesc { get; set; }
public String CategoryCode { get; set; }
public String CategoryDesc { get; set; }
public long MemberCount { get; set; }
// Sport.
public String SportId { get; set; }
public String SportName { get; set; }
// Primary Region.
public class RegionData
{
public String Id { get; set; }
public String Name { get; set; }
public bool? Country { get; set; } = null;
}
public RegionData RegionPrimary { get; } = new RegionData();
// Address.
public String AddressSummary { get; set; }
// Logo.
public String LogoImageUrl { get; set; }
}
private class OrganisationItemData : OrganisationListData
{
public String About { get; set; }
public String Facilities { get; set; }
public List<AjaxResponse_Data.ContactMethodData> ContactMethods { get; set; }
public AjaxResponse_Data.AddressData Address { get; set; }
}
private void PopulateOrganisationResponseListData(SaxOrganisation organisation, OrganisationListData data)
{
data.Id = SysId.ToString(organisation.Id);
data.Name = organisation.Name;
data.TypeCode = OrganisationTypeCodes.GetCodeForType(organisation.OrganisationType);
data.TypeDesc = OrganisationTypeCodes.GetDescForType(organisation.OrganisationType);
data.CategoryCode = OrganisationCategoryCodes.GetCodeForType(organisation.OrganisationCategory);
data.CategoryDesc = OrganisationCategoryCodes.GetDescForType(organisation.OrganisationCategory);
data.MemberCount = AccountMgr.GetOrganisationMemberCount(organisation.Id);
// Sport.
data.SportId = SysId.ToString(organisation.SportIdPrimary, null);
using (SaxSport sport = ReferenceMgr.GetSportById(organisation.SportIdPrimary))
data.SportName = sport?.Name ?? null;
// Primary region.
data.RegionPrimary.Id = SysId.ToString(organisation.RegionIdPrimary, null);
SaxRegion.Data region = Internals.GetRegion(organisation.RegionIdPrimary);
if (region != null)
{
data.RegionPrimary.Name = region.Name;
data.RegionPrimary.Country = region.RegionType == Sax.RegionType.Country ? true : (bool?)null;
}
// Common properties.
data.StatusCode = organisation.StatusCode;
data.StatusDesc = organisation.StatusDesc;
// Organisation Logo image url.
if (ItemDataMgr.ItemHasActiveImage(organisation.GetItemId(), Sax.ImageType.OrganisationLogo))
data.LogoImageUrl = GetOrganisationLogoLink(organisation.Id, false);
}
private void PopulateOrganisationResponseItemData(SaxOrganisation organisation, SaxItemAddress address, OrganisationItemData data)
{
PopulateOrganisationResponseListData(organisation, data);
data.About = StringUtil.OnEmpty(organisation.About, null);
data.Facilities = StringUtil.OnEmpty(organisation.Facilities, null);
// Contact methods.
SaxContactData contactData = new SaxContactData(organisation.ContactData);
foreach (Sax.ContactMethod method in contactData.Methods)
{
if (data.ContactMethods == null) data.ContactMethods = new List<AjaxResponse_Data.ContactMethodData>();
data.ContactMethods.Add(new AjaxResponse_Data.ContactMethodData() { Method = method.ToString(), MethodCaption = Localiser.GetContactMethodCaption(method), Value = contactData.GetContactMethodValue(method) });
}
// Address.
if (address != null && !address.IsStatusDeleted)
{
data.Address = new AjaxResponse_Data.AddressData();
data.Address.Id = address.Id.ToString(null);
data.Address.Line1 = StringUtil.OnEmpty(address.Line1, null);
data.Address.Line2 = StringUtil.OnEmpty(address.Line2, null);
data.Address.Line3 = StringUtil.OnEmpty(address.Line3, null);
data.Address.Town = address.Town;
data.Address.RegionIdProvince = SysId.ToString(address.RegionIdProvince, null);
data.Address.RegionNameProvince = StringUtil.OnEmpty(address.Province, null);
data.Address.RegionIdCountry = SysId.ToString(address.RegionIdCountry, null);
data.Address.RegionNameCountry = StringUtil.OnEmpty(address.Country, null);
data.Address.PostalCode = StringUtil.OnEmpty(address.PostalCode, null);
}
// Common properties.
data.Notes = organisation.Notes;
}
//-------------------------------------------------------------------
// Get Organisation List ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_OrganisationList : AjaxResponse_Data
{
// Construction.
public AjaxResponse_OrganisationList(SaxModel_Admin model) : base(model) { }
// Organisation list.
public List<OrganisationListData> Organisations { get; } = new List<OrganisationListData>();
// Reference data.
public List<AjaxResponse_Data.ItemCodeDesc> ContactMethods { get; } = new List<ItemCodeDesc>();
public List<CodeDesc> Statuses { get; } = new List<CodeDesc>();
public String DefaultStatusCode { get; set; }
public List<String> UploadContentTypes { get; set; }
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Organisation/Ajax/GetList")]
public IActionResult Ajax_GetList(Model_Organisation model)
{
String fn = GetType().Name + ".Ajax_GetList";
String messageTitle = Localiser["caption.organisations"];
AjaxResponse_OrganisationList responseData = new AjaxResponse_OrganisationList(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Reference data - contact methods.
List<Sax.ContactMethod> validContactMethods = SaxOrganisation.ValidContactMethods;
foreach (TypeCodeDesc contactMethod in ContactMethodCodes.OrderBy(item => item.Desc))
{
// Skip methods that aren't associated with Organisations.
if (!validContactMethods.Contains((Sax.ContactMethod)contactMethod.Type)) continue;
responseData.ContactMethods.Add(new AjaxResponse_Data.ItemCodeDesc() { Code = contactMethod.Code, Desc = Localiser.GetContactMethodCaption((Sax.ContactMethod)contactMethod.Type) });
}
// Reference data - statuses.
foreach (SaStatus status in SaxOrganisation.ValidStatuses.FindAll(item => item != SaStatus.Deleted))
responseData.Statuses.Add(SacsSystem.GetStatusCode(status), Localiser.GetStatusCaption(status));
responseData.DefaultStatusCode = SacsSystem.GetStatusCode(SaxOrganisation.DefaultStatus);
// Reference data - Image content types.
responseData.UploadContentTypes = SaxOrganisationUploader.ValidContentTypes;
// Store filter values.
SysId filterSportId = SysId.ToSysId(model.Filter.OrganisationSportId);
SessionData_Admin.Filter.SportId = SysId.IsValid(filterSportId) ? filterSportId : null;
SysId filterRegionId = SysId.ToSysId(model.Filter.OrganisationRegionId);
SessionData_Admin.Filter.RegionId = SysId.IsValid(filterRegionId) ? filterRegionId : null;
// Item filter.
SaxOrganisation.Params prmOrganisation = GetOrganisationListFilter(model, AccountMgr);
model.Paging.ItemTotal = (ulong)AccountMgr.GetOrganisationCount(prmOrganisation);
model.PopulatePagingTo(prmOrganisation.ResultsPaging);
// Item columns.
ColumnDfn colId = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.Id);
ColumnDfn colName = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.Name);
ColumnDfn colOrganisationType = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.OrganisationType);
ColumnDfn colOrganisationCategory = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.OrganisationCategory);
ColumnDfn colUserCount = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.User_Count);
ColumnDfn colMemberCount = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.Member_Count);
ColumnDfn colLogoCount = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.LogoImage_Count);
ColumnDfn colSportId = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.SportId);
ColumnDfn colSportName = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.Sport_Name);
ColumnDfn colRegionId = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.RegionId);
ColumnDfn colRegionName = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.Region_Name);
ColumnDfn colStatus = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.Status);
List<ColumnDfn> columns = new List<ColumnDfn>()
{
colId,
colName,
colOrganisationType,
colOrganisationCategory,
colUserCount, colMemberCount, colLogoCount,
colSportId, colSportName,
colRegionId, colRegionName,
colStatus
};
// Query items.
using (DbResultSet resultSet = AccountMgr.ExecuteOrganisationQuery(columns, prmOrganisation))
{
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
{
NamedValue.Map values = resultSet.GetRecordValues();
SysId organisationId = new SysId(values.GetLong(colId.DataName));
OrganisationListData organisationData = new OrganisationListData()
{
Id = organisationId.ToString(),
Name = values.GetString(colName.DataName),
TypeCode = values.GetString(colOrganisationType.DataName),
TypeDesc = OrganisationTypeCodes.GetDescForCode(values.GetString(colOrganisationType.DataName)),
CategoryCode = values.GetString(colOrganisationCategory.DataName),
CategoryDesc = OrganisationCategoryCodes.GetDescForCode(values.GetString(colOrganisationCategory.DataName)),
MemberCount = StringUtil.ToLong(values.GetString(colMemberCount.DataName)),
SportId = SysId.ToString(new SysId(values.GetLong(colSportId.DataName)), null),
SportName = StringUtil.OnEmpty(values.GetString(colSportName.DataName), null),
StatusCode = values.GetString(colStatus.DataName),
StatusDesc = SacsSystem.GetStatusDescForCode(values.GetString(colStatus.DataName))
};
// Logo image.
if (StringUtil.ToLong(values.GetString(colLogoCount.DataName)) > 0)
organisationData.LogoImageUrl = GetOrganisationLogoLink(organisationId, false);
// Primary region.
organisationData.RegionPrimary.Id = SysId.ToString(new SysId(values.GetLong(colRegionId.DataName)), null);
organisationData.RegionPrimary.Name = StringUtil.OnEmpty(values.GetString(colRegionName.DataName), null);
// Add item to list.
responseData.Organisations.Add(organisationData);
}
}
if (model.Paging.IsPaged)
model.Paging.ItemCount = (ushort)responseData.Organisations.Count;
// Complete response.
responseData.SetPaging(model);
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
/// <summary>
/// Construct Organisation filter params.
/// <para>- Shared with Organisation Exporter process.</para>
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public static SaxOrganisation.Params GetOrganisationListFilter(Model_Organisation model, SaxAccountMgr accountMgr)
{
// Item filter.
SaxOrganisation.Params prmOrganisation = new SaxOrganisation.Params();
prmOrganisation.OrganisationFilter = model.Filter.Organisation;
prmOrganisation.OrganisationType = model.FilterOrganisationType;
prmOrganisation.OrganisationCategory = model.FilterOrganisationCategory;
prmOrganisation.SportIdsIncluded.AddId(SysId.ToSysId(model.Filter.OrganisationSportId));
prmOrganisation.RegionIdsIncluded.AddId(SysId.ToSysId(model.Filter.OrganisationRegionId));
prmOrganisation.StatusCat = model.Filter.OrganisationStatusCategoryCode != null ? model.FilterOrganisationStatusCategory : SaxOrganisation.StatusCat.NotDeleted;
prmOrganisation.SortColumns.Add(accountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.Name));
return prmOrganisation;
}
//-------------------------------------------------------------------
// Get Organisation ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_Organisation : AjaxResponse_Data
{
// Construction.
public AjaxResponse_Organisation(SaxModel_Admin model) : base(model) { }
// Organisation details.
public OrganisationItemData organisation { get; } = new OrganisationItemData();
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Organisation/Ajax/GetItem")]
public IActionResult Ajax_GetItem(Model_Organisation model)
{
String fn = GetType().Name + ".Ajax_GetItem";
String messageTitle = Localiser["caption.organisation"];
AjaxResponse_Organisation responseData = new AjaxResponse_Organisation(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
// Identify item.
SysId organisationId = SysId.ToSysId(model.OrganisationId);
if (!SysId.IsValid(organisationId))
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.specified"]));
// Retrieve item.
SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationId);
disposableItems.Add(organisation);
if (organisation == null)
{
SaLogger.Warn(fn + " Failed to retrieve organisation for Id: '" + model.OrganisationId + "'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Organisation address.
SaxItemAddress address = ItemDataMgr.GetFirstItemAddressByItemAndType(organisation.GetItemId());
disposableItems.Add(address);
// Construct response.
PopulateOrganisationResponseItemData(organisation, address, responseData.organisation);
// Complete response.
responseData.Success = true;
}
return Json(responseData);
}
//-------------------------------------------------------------------
// Store Organisation ajax request.
//-------------------------------------------------------------------
[HttpPost]
[Route("Organisation/Ajax/StoreItem")]
public IActionResult Ajax_StoreItem(Model_Organisation model)
{
String fn = GetType().Name + ".Ajax_StoreItem";
String messageTitle = Localiser["caption.organisation"];
AjaxResponse_Organisation responseData = new AjaxResponse_Organisation(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
// Validate item properties.
model.ValidateOrganisationData(responseData);
if (responseData.HasControlErrors())
return Json(responseData);
// Retrieve existing item.
SysId organisationId = SysId.ToSysId(model.OrganisationId);
SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationId);
disposableItems.Add(organisation);
if (organisation == null && SysId.IsValid(organisationId))
{
SaLogger.Warn(fn + " Failed to retrieve organisation for Id: '" + model.OrganisationId + "'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Create new item if required.
if (organisation == null)
{
organisation = AccountMgr.CreateOrganisation();
disposableItems.Add(organisation);
}
// Organisation address.
SaxItemAddress organisationAddress = ItemDataMgr.GetFirstItemAddressByItemAndType(organisation.GetItemId());
disposableItems.Add(organisationAddress);
// Populate item properties.
if (model.OrganisationName != null)
organisation.Name = model.OrganisationName;
if (model.OrganisationTypeValue != null)
organisation.OrganisationType = model.OrganisationType;
if (model.OrganisationCategoryValue != null)
organisation.OrganisationCategory = model.OrganisationCategory;
if (model.OrganisationSportId != null)
organisation.SportIdPrimary = SysId.ToSysId(model.OrganisationSportId);
if (model.OrganisationRegionIdPrimary != null)
organisation.RegionIdPrimary = SysId.ToSysId(model.OrganisationRegionIdPrimary);
if (model.OrganisationAbout != null)
organisation.About = model.OrganisationAbout;
if (model.OrganisationFacilities != null)
organisation.Facilities = model.OrganisationFacilities;
if (model.ContactData != null)
organisation.ContactData = model.ContactData.GetContactData();
// Common properties.
if (model.OrganisationStatusCode != null)
organisation.Status = model.OrganisationStatus;
if (model.OrganisationNotes != null)
organisation.Notes = model.OrganisationNotes;
// Store item.
if (!SysId.IsValid(organisation.Id))
{
if (!AccountMgr.InsertOrganisation(organisation, DbUpdateInfo))
{
SaLogger.Warn(fn + " Failed to insert organisation: " + AccountMgr.ErrorStateMessage);
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.insert.failed"] + "\n\n" + AccountMgr.ErrorStateMessage));
}
}
else
{
if (!AccountMgr.UpdateOrganisation(organisation, DbUpdateInfo))
{
SaLogger.Warn(fn + " Failed to update organisation [Id:" + SysId.ToString(organisation.Id) + "]: " + AccountMgr.ErrorStateMessage);
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + AccountMgr.ErrorStateMessage));
}
}
if (model.Address.IsSpecified())
{
// The form includes address fields.
if (model.Address.IsPopulated())
{
// Organisation regions.
SaxRegion.Data regionCountry = Internals.GetRegion(SysId.ToSysId(model.Address.RegionIdCountry));
if (regionCountry == null)
// Default to Organisation's primary region.
regionCountry = Internals.GetRegion(organisation.RegionIdPrimary);
if (regionCountry != null && regionCountry.RegionType != Sax.RegionType.Country)
regionCountry = null;
SaxRegion.Data regionProvince = null;
// Create Organisation address if required.
if (organisationAddress == null)
{
organisationAddress = ItemDataMgr.CreateItemAddress(organisation.GetItemId(), SaxItemAddress.DefaultOrganisationAddressType);
disposableItems.Add(organisationAddress);
}
// Store address.
if (model.Address.Line1 != null)
organisationAddress.Line1 = model.Address.Line1;
if (model.Address.Line2 != null)
organisationAddress.Line2 = model.Address.Line2;
if (model.Address.Line3 != null)
organisationAddress.Line3 = model.Address.Line3;
if (model.Address.Town != null)
organisationAddress.Town = model.Address.Town;
if (regionProvince != null)
{
organisationAddress.Province = regionProvince.Name;
organisationAddress.RegionIdProvince = regionProvince.Id;
}
else
{
if (model.Address.Province != null)
organisationAddress.Province = model.Address.Province;
}
if (regionCountry != null)
{
organisationAddress.Country = regionCountry.Name;
organisationAddress.RegionIdCountry = regionCountry.Id;
}
organisationAddress.PostalCode = model.Address.PostalCode;
organisationAddress.Status = SaxItemAddress.DefaultStatus;
// Store address.
if (!SysId.IsValid(organisationAddress.Id))
{
// Create new address.
if (!ItemDataMgr.InsertItemAddress(organisationAddress, DbUpdateInfo))
{
SaLogger.Warn(fn + " Failed to insert organisation address - " + ItemDataMgr.ErrorStateMessage);
return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"]));
}
}
else
{
// Update existing address.
if (!ItemDataMgr.UpdateItemAddress(organisationAddress, DbUpdateInfo))
{
SaLogger.Warn(fn + " Failed to update organisation address - " + ItemDataMgr.ErrorStateMessage);
return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"]));
}
}
}
else if (organisationAddress != null && !organisationAddress.IsStatusDeleted)
{
// Address not populated - delete it.
if (!ItemDataMgr.DeleteItemAddress(organisationAddress, DbUpdateInfo))
{
SaLogger.Warn(fn + " Failed to delete organisation address - " + ItemDataMgr.ErrorStateMessage);
return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"]));
}
}
}
// Construct response.
PopulateOrganisationResponseItemData(organisation, organisationAddress, responseData.organisation);
// Complete response.
responseData.Success = true;
}
return Json(responseData);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,79 @@
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.Email;
using Sarand.Net.Standard.Base.Helpers;
using Sarand.Net.Standard.Base.Utils;
using Sarand.Net.Standard.Proc.Excel;
using Saxonite.Net.Core.Web.Admin.Consoles;
using Saxonite.Net.Core.Web.Admin.Models;
using Saxonite.Net.Core.Web.Admin.Procs;
using Saxonite.Net.Core.Web.Base.Controllers;
using Saxonite.Net.Standard.Base.App;
using Saxonite.Net.Standard.Base.Bus;
using Saxonite.Net.Standard.Base.Helpers;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Admin.Controllers
{
//=======================================================================
// Controller: Organisation - Download
//=======================================================================
partial class OrganisationController
{
//-------------------------------------------------------------------
// Definitions.
//-------------------------------------------------------------------
// Download Organisations.
//-------------------------------------------------------------------
[HttpPost]
[Route("Organisation/Download")]
public IActionResult Ajax_DownloadOrganisations(Model_OrganisationDownload model)
{
String fn = GetType().Name + ".Ajax_DownloadOrganisations";
// Validate session.
if (!ValidUserSession)
return Unauthorized();
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
// Validate supplied data properties.
if (!model.ValidateProperties())
return BadRequest();
// Export data.
SaxOrganisationExporter.Config exporterConfig = new SaxOrganisationExporter.Config(SessionContext, DbUpdateInfo, GetOrganisationListFilter(model, AccountMgr));
exporterConfig.Format = SaxOrganisationExporter.ExportFormat.Excel_Open;
exporterConfig.IncludeId = StringUtil.ToBool(model.IncludeId);
SaxOrganisationExporter exporter = new SaxOrganisationExporter(exporterConfig);
if (exporter.RunProcess())
{
// Organisations exported successfully.
byte[] data = exporter.GetDocumentData();
using (MemoryStream content = new MemoryStream(data))
{
String contentType = exporter.GetContentType();
var fileName = "Sportierre-Organisations-" + DateTime.Now.ToString("yyyyMMdd") + ".xlsx";
return File(content.ToArray(), contentType, fileName);
}
}
else
{
// Problems occurred.
}
}
return NoContent();
}
//-------------------------------------------------------------------
}
}
@@ -0,0 +1,119 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Utils;
using Saxonite.Net.Core.Web.Admin.Models;
using Saxonite.Net.Core.Web.Base.App;
using Saxonite.Net.Core.Web.Base.Controllers;
using Saxonite.Net.Standard.Base.Bus;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Admin.Controllers
{
//=======================================================================
public partial class OrganisationController : SaxController_Admin
{
//-------------------------------------------------------------------
// Construction.
public OrganisationController(SaxLocaliser localiser, ILogger<HomeController> logger) :
base(localiser, logger)
{
}
//-------------------------------------------------------------------
[HttpGet]
public IActionResult Index(String sta)
{
// Validate session.
if (!ValidUserSession)
AutoLoginUser();
if (!ValidUserSession)
return RedirectToAction("", "");
// Model.
Model_Organisation model = new Model_Organisation(Localiser);
// Stored Sport filter.
model.Filter.OrganisationSportId = SessionData_Admin.Filter.SportId?.ToString() ?? null;
using (SaxSport filterSport = ReferenceMgr.GetSportById(SessionData_Admin.Filter.SportId))
model.Filter.OrganisationSportName = Localiser.GetSportName(SessionData_Admin.Filter.SportId, filterSport?.Name ?? "");
// Stored Region filter.
model.Filter.OrganisationRegionId = SessionData_Admin.Filter.RegionId?.ToString() ?? null;
using (SaxRegion filterRegion = ReferenceMgr.GetRegionById(SessionData_Admin.Filter.RegionId))
model.Filter.OrganisationRegionName = Localiser.GetRegionName(SessionData_Admin.Filter.RegionId, filterRegion?.Name ?? "");
// Paging.
model.Paging.PageNo = 1;
model.Paging.PageLength = 8;
// Default status.
if (sta.StartsWithCI("Awa"))
model.Filter.OrganisationStatusCategoryCode = nameof(SaxOrganisation.StatusCat.AwaitingOnly);
return View(model);
}
//-------------------------------------------------------------------
/// <summary>
/// Organisation Members action.
/// <para>- POST action to allow Organisation Id to be supplied, eg. from Organsiations page.</para>
/// </summary>
[HttpPost]
public IActionResult Members(Model_OrganisationMembers model)
{
// Validate session.
if (!ValidUserSession)
AutoLoginUser();
if (!ValidUserSession)
return RedirectToAction("", "");
// Retrieve organisation.
SaxOrganisation organisation = AccountMgr.GetOrganisationById(SysId.ToSysId(model.OrganisationId));
if (organisation == null)
RedirectToAction("", "Organisation");
model.Filter.OrganisationId= SysId.ToString(organisation.Id);
model.Filter.OrganisationName = organisation.Name;
// Paging.
model.Paging.PageNo = 1;
model.Paging.PageLength = 12;
return View(model);
}
//-------------------------------------------------------------------
/// <summary>
/// Organisation Associations action.
/// <para>- POST action to allow Organisation Id to be supplied, eg. from Organsiations page.</para>
/// </summary>
[HttpPost]
public IActionResult Associations(Model_OrganisationAssociations model)
{
// Validate session.
if (!ValidUserSession)
AutoLoginUser();
if (!ValidUserSession)
return RedirectToAction("", "");
// Retrieve organisation.
SaxOrganisation organisation = AccountMgr.GetOrganisationById(SysId.ToSysId(model.OrganisationId));
if (organisation == null)
RedirectToAction("", "Organisation");
model.Filter.OrganisationId = SysId.ToString(organisation.Id);
model.Filter.OrganisationName = organisation.Name;
// Paging.
model.Paging.PageNo = 1;
model.Paging.PageLength = 12;
return View(model);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,43 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Saxonite.Net.Core.Web.Admin.Models;
using Saxonite.Net.Core.Web.Base.App;
using Saxonite.Net.Core.Web.Base.Controllers;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Admin.Controllers
{
//=======================================================================
public class PoliciesController : SaxController_Admin
{
//-------------------------------------------------------------------
// Construction.
public PoliciesController(SaxLocaliser localiser, ILogger<HomeController> logger) :
base(localiser, logger)
{
}
//-------------------------------------------------------------------
[HttpGet]
public IActionResult Index()
{
Model_Policies model = new Model_Policies(Localiser);
return View(model);
}
//-------------------------------------------------------------------
[HttpGet]
public IActionResult Privacy()
{
Model_Policies model = new Model_Policies(Localiser);
return View(model);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,46 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Saxonite.Net.Core.Web.Admin.Models.Reference;
using Saxonite.Net.Core.Web.Base.App;
using Saxonite.Net.Core.Web.Base.Controllers;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Admin.Controllers.Reference
{
//=======================================================================
public partial class ReferenceBaseballController : SaxController_Admin
{
//-------------------------------------------------------------------
// Construction.
public ReferenceBaseballController(SaxLocaliser localiser, ILogger<HomeController> logger) :
base(localiser, logger)
{
}
//-------------------------------------------------------------------
[HttpGet]
[Route("Reference/Baseball")]
public IActionResult Index()
{
// Validate session.
if (!ValidUserSession)
AutoLoginUser();
if (!ValidUserSession)
return RedirectToAction("", "");
// Paging.
Model_ReferenceBaseball model = new Model_ReferenceBaseball(Localiser);
//model.Paging.PageNo = 1;
//model.Paging.PageLength = 12;
return View("/Views/Reference/Baseball/Index.cshtml",model);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,76 @@
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.Core.Web.Admin.Models.Reference;
using Saxonite.Net.Standard.Base.App;
using Saxonite.Net.Standard.Base.Bus;
using Saxonite.Net.Standard.Base.Bus.Reference;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Admin.Controllers.Reference
{
//=======================================================================
// Reference Baseball Controller - Ajax requests - Stats.
//=======================================================================
partial class ReferenceBaseballController
{
//-------------------------------------------------------------------
// Get Stats ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_ReferenceBaseballStats : AjaxResponse_Data
{
// Construction.
public AjaxResponse_ReferenceBaseballStats(SaxModel_Admin model) : base(model) { }
// Statistics.
public List<StatsGroupData> Groups { get; } = new List<StatsGroupData>();
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Reference/Baseball/Ajax/GetStats")]
[Route("Reference/Baseball/Ajax/Stats")]
public IActionResult Ajax_GetUsgaStats(Model_ReferenceBaseball model)
{
String fn = GetType().Name + ".Ajax_GetUsgaStats";
String messageTitle = Localiser["caption.reference.baseball"];
AjaxResponse_ReferenceBaseballStats responseData = new AjaxResponse_ReferenceBaseballStats(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Statistics.
responseData.Groups.Add(PopulateCoreStatsData(model));
// Complete response.
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
private AjaxResponse_Data.StatsGroupData PopulateCoreStatsData(Model_ReferenceBaseball model)
{
AjaxResponse_Data.StatsGroupData group = new AjaxResponse_Data.StatsGroupData() { Caption = "Core" };
// Sports.
List<SaxSport.Data> sports = GetSportsByName("Baseball", true);
// Organisations.
SaxOrganisation.Params prmOrganisation = new SaxOrganisation.Params();
prmOrganisation.SportIdsIncluded.AddIds(sports.Select(item => item.Id));
prmOrganisation.StatusCat = SaxOrganisation.StatusCat.ActiveOnly;
group.Stats.Add(new AjaxResponse_Data.StatData() { Caption = "Active Organisations", Value = AccountMgr.GetOrganisationCount(prmOrganisation).ToString() });
return group;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,33 @@
using Microsoft.AspNetCore.Mvc;
using NPOI.SS.Formula.Functions;
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.Consoles;
using Saxonite.Net.Core.Web.Admin.Models;
using Saxonite.Net.Core.Web.Admin.Models.Reference;
using Saxonite.Net.Core.Web.Admin.Procs;
using Saxonite.Net.Core.Web.Base.Procs;
using Saxonite.Net.Standard.Base.App;
using Saxonite.Net.Standard.Base.Bus;
using Saxonite.Net.Standard.Base.Bus.Reference;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Admin.Controllers.Reference
{
//=======================================================================
// Reference Baseball Controller - Ajax requests.
//=======================================================================
partial class ReferenceBaseballController
{
//-------------------------------------------------------------------
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,64 @@
using Microsoft.AspNetCore.Mvc;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Utils;
using Saxonite.Net.Core.Web.Admin.Models;
using Saxonite.Net.Core.Web.Base.Models;
using Saxonite.Net.Standard.Base.App;
using Saxonite.Net.Standard.Base.Helpers;
using System;
namespace Saxonite.Net.Core.Web.Admin.Controllers.Reference
{
//=======================================================================
// Admin Reference Controller - Contact Method Ajax actions only.
//=======================================================================
partial class ReferenceController
{
//-------------------------------------------------------------------
// Validate Contact Method.
//-------------------------------------------------------------------
private class AjaxResponse_ValidateContactMethod : AjaxResponse_Data
{
public AjaxResponse_ValidateContactMethod(SaxModel_Base model) : base(model) { }
}
[HttpPost]
[Route("Reference/Ajax/ValidateContactMethod")]
public IActionResult Ajax_ValidateContactMethod(Model_ContactMethod model)
{
String fn = GetType().Name + ".Ajax_ValidateContactMethod";
String messageTitle = Localiser["caption.contact.method"];
AjaxResponse_ValidateContactMethod responseData = new AjaxResponse_ValidateContactMethod(model);
// Validate session and referrer.
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
// Validate method.
Sax.ContactMethod method = EnumUtil.ParseEnum(model.ContactMethod, Sax.ContactMethod.Unknown);
if (model.ContactMethod.IsEmpty() || (int)method < 1)
responseData.SetControlError(nameof(model.ContactMethod), Localiser["msg.validation.mandatory.value"]);
// Validate method.
if (model.ContactValue.IsEmpty())
responseData.SetControlError(nameof(model.ContactValue), Localiser["msg.validation.mandatory.value"]);
// Method specific value validation.
if (!model.ContactValue.IsEmpty() && !SaxContactMethodHelper.IsValidContactMethodValue(method, model.ContactValue))
{
// Log invalid values so that the validation can be adjusted if needed.
SaLogger.Warn(fn + " Invalid contact method value. Mathod: " + method.ToString() + ". Value: " + model.ContactValue);
responseData.SetControlError(nameof(model.ContactValue), Localiser["msg.validation.invalid.value"]);
}
if (responseData.HasControlErrors())
return Json(responseData);
// Complete response.
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,31 @@
using Microsoft.AspNetCore.Mvc;
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.Dbas;
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.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Admin.Controllers.Reference
{
//=======================================================================
// Admin Reference Controller - Ajax actions only.
//=======================================================================
public partial class ReferenceController : SaxController_Admin
{
//-------------------------------------------------------------------
// Construction.
public ReferenceController(SaxLocaliser localiser, ILogger<HomeController> logger) :
base(localiser, logger)
{}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,300 @@
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.Core.Web.Admin.Models.Reference;
using Saxonite.Net.Standard.Base.App;
using Saxonite.Net.Standard.Base.Bus;
using Saxonite.Net.Standard.Base.Bus.Reference;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Admin.Controllers.Reference
{
//=======================================================================
// Reference Golf Controller - Ajax requests - Stats.
//=======================================================================
partial class ReferenceGolfController
{
//-------------------------------------------------------------------
// Get Stats ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_ReferenceGolfStats : AjaxResponse_Data
{
// Construction.
public AjaxResponse_ReferenceGolfStats(SaxModel_Admin model) : base(model) { }
// Statistics.
public List<StatsGroupData> Groups { get; } = new List<StatsGroupData>();
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Reference/Golf/Ajax/GetUsgaStats")]
[Route("Reference/Golf/Ajax/GetStats")]
[Route("Reference/Golf/Ajax/Stats")]
public IActionResult Ajax_GetUsgaStats(Model_ReferenceGolf model)
{
String fn = GetType().Name + ".Ajax_GetUsgaStats";
String messageTitle = Localiser["caption.reference.golf"];
AjaxResponse_ReferenceGolfStats responseData = new AjaxResponse_ReferenceGolfStats(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Statistics.
responseData.Groups.Add(PopulateCountryStatsData());
responseData.Groups.Add(PopulateProvinceStatsData());
responseData.Groups.Add(PopulateFacilityStatsData());
responseData.Groups.Add(PopulateCourseStatsData());
responseData.Groups.Add(PopulateMarkerStatsData());
// Complete response.
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
private AjaxResponse_Data.StatsGroupData PopulateCountryStatsData()
{
AjaxResponse_Data.StatsGroupData group = new AjaxResponse_Data.StatsGroupData() { Caption = "Countries" };
// All countries.
SaxGolfUsgaCountry.Params prmCountry = new SaxGolfUsgaCountry.Params();
prmCountry.StatusCat = SaxGolfUsgaCountry.StatusCat.Any;
group.Stats.Add(new AjaxResponse_Data.StatData() { Caption = "Total", Value = ReferenceGolfMgr.GetUsgaCountryCount(prmCountry).ToString() });
// Active countries.
prmCountry = new SaxGolfUsgaCountry.Params();
prmCountry.StatusCat = SaxGolfUsgaCountry.StatusCat.ActiveOnly;
group.Stats.Add(new AjaxResponse_Data.StatData() { Caption = "Active", Value = ReferenceGolfMgr.GetUsgaCountryCount(prmCountry).ToString() });
// Inctive countries.
prmCountry = new SaxGolfUsgaCountry.Params();
prmCountry.StatusCat = SaxGolfUsgaCountry.StatusCat.InactiveOnly;
group.Stats.Add(new AjaxResponse_Data.StatData() { Caption = "Inactive", Value = ReferenceGolfMgr.GetUsgaCountryCount(prmCountry).ToString() });
// Deleted countries.
prmCountry = new SaxGolfUsgaCountry.Params();
prmCountry.StatusCat = SaxGolfUsgaCountry.StatusCat.DeletedOnly;
group.Stats.Add(new AjaxResponse_Data.StatData() { Caption = "Deleted", Value = ReferenceGolfMgr.GetUsgaCountryCount(prmCountry).ToString() });
// Priority countries without provinces.
prmCountry = new SaxGolfUsgaCountry.Params();
prmCountry.PriorityFlagState = SacsHierarchyItem.Params.FlagState.True;
prmCountry.HasProvinces = SacsHierarchyItem.Params.BoolState.False;
prmCountry.StatusCat = SaxGolfUsgaCountry.StatusCat.ActiveOnly;
group.Stats.Add(new AjaxResponse_Data.StatData() { Caption = "Priority no provinces", Value = ReferenceGolfMgr.GetUsgaCountryCount(prmCountry).ToString() });
// Non-Priority countries without provinces.
prmCountry = new SaxGolfUsgaCountry.Params();
prmCountry.PriorityFlagState = SacsHierarchyItem.Params.FlagState.False;
prmCountry.HasProvinces = SacsHierarchyItem.Params.BoolState.False;
prmCountry.StatusCat = SaxGolfUsgaCountry.StatusCat.ActiveOnly;
group.Stats.Add(new AjaxResponse_Data.StatData() { Caption = "Non-priority no provinces", Value = ReferenceGolfMgr.GetUsgaCountryCount(prmCountry).ToString() });
// Priority countries with provinces.
prmCountry = new SaxGolfUsgaCountry.Params();
prmCountry.PriorityFlagState = SacsHierarchyItem.Params.FlagState.True;
prmCountry.HasProvinces = SacsHierarchyItem.Params.BoolState.True;
prmCountry.StatusCat = SaxGolfUsgaCountry.StatusCat.ActiveOnly;
group.Stats.Add(new AjaxResponse_Data.StatData() { Caption = "Priority with provinces", Value = ReferenceGolfMgr.GetUsgaCountryCount(prmCountry).ToString() });
// Non-Priority countries with provinces.
prmCountry = new SaxGolfUsgaCountry.Params();
prmCountry.PriorityFlagState = SacsHierarchyItem.Params.FlagState.False;
prmCountry.HasProvinces = SacsHierarchyItem.Params.BoolState.True;
prmCountry.StatusCat = SaxGolfUsgaCountry.StatusCat.ActiveOnly;
group.Stats.Add(new AjaxResponse_Data.StatData() { Caption = "Non-priority with provinces", Value = ReferenceGolfMgr.GetUsgaCountryCount(prmCountry).ToString() });
return group;
}
//-------------------------------------------------------------------
private AjaxResponse_Data.StatsGroupData PopulateProvinceStatsData()
{
AjaxResponse_Data.StatsGroupData group = new AjaxResponse_Data.StatsGroupData() { Caption = "Provinces" };
// All provinces.
SaxGolfUsgaProvince.Params prmProvince = new SaxGolfUsgaProvince.Params();
prmProvince.StatusCat = SaxGolfUsgaProvince.StatusCat.Any;
group.Stats.Add(new AjaxResponse_Data.StatData() { Caption = "Total", Value = ReferenceGolfMgr.GetUsgaProvinceCount(prmProvince).ToString() });
// Active provinces.
prmProvince = new SaxGolfUsgaProvince.Params();
prmProvince.StatusCat = SaxGolfUsgaProvince.StatusCat.ActiveOnly;
group.Stats.Add(new AjaxResponse_Data.StatData() { Caption = "Active", Value = ReferenceGolfMgr.GetUsgaProvinceCount(prmProvince).ToString() });
// Inctive provinces.
prmProvince = new SaxGolfUsgaProvince.Params();
prmProvince.StatusCat = SaxGolfUsgaProvince.StatusCat.InactiveOnly;
group.Stats.Add(new AjaxResponse_Data.StatData() { Caption = "Inactive", Value = ReferenceGolfMgr.GetUsgaProvinceCount(prmProvince).ToString() });
// Deleted provinces.
prmProvince = new SaxGolfUsgaProvince.Params();
prmProvince.StatusCat = SaxGolfUsgaProvince.StatusCat.DeletedOnly;
group.Stats.Add(new AjaxResponse_Data.StatData() { Caption = "Deleted", Value = ReferenceGolfMgr.GetUsgaProvinceCount(prmProvince).ToString() });
// Priority provinces without facilities.
prmProvince = new SaxGolfUsgaProvince.Params();
prmProvince.PriorityFlagState = SacsHierarchyItem.Params.FlagState.True;
prmProvince.HasFacilities = SacsHierarchyItem.Params.BoolState.False;
prmProvince.StatusCat = SaxGolfUsgaProvince.StatusCat.ActiveOnly;
group.Stats.Add(new AjaxResponse_Data.StatData() { Caption = "Priority no facilities", Value = ReferenceGolfMgr.GetUsgaProvinceCount(prmProvince).ToString() });
// Non-Priority provinces without facilities.
prmProvince = new SaxGolfUsgaProvince.Params();
prmProvince.PriorityFlagState = SacsHierarchyItem.Params.FlagState.False;
prmProvince.HasFacilities = SacsHierarchyItem.Params.BoolState.False;
prmProvince.StatusCat = SaxGolfUsgaProvince.StatusCat.ActiveOnly;
group.Stats.Add(new AjaxResponse_Data.StatData() { Caption = "Non-priority no facilities", Value = ReferenceGolfMgr.GetUsgaProvinceCount(prmProvince).ToString() });
// Priority provinces with facilities.
prmProvince = new SaxGolfUsgaProvince.Params();
prmProvince.PriorityFlagState = SacsHierarchyItem.Params.FlagState.True;
prmProvince.HasFacilities = SacsHierarchyItem.Params.BoolState.True;
prmProvince.StatusCat = SaxGolfUsgaProvince.StatusCat.ActiveOnly;
group.Stats.Add(new AjaxResponse_Data.StatData() { Caption = "Priority with facilities", Value = ReferenceGolfMgr.GetUsgaProvinceCount(prmProvince).ToString() });
// Non-Priority provinces with facilities.
prmProvince = new SaxGolfUsgaProvince.Params();
prmProvince.PriorityFlagState = SacsHierarchyItem.Params.FlagState.False;
prmProvince.HasFacilities = SacsHierarchyItem.Params.BoolState.True;
prmProvince.StatusCat = SaxGolfUsgaProvince.StatusCat.ActiveOnly;
group.Stats.Add(new AjaxResponse_Data.StatData() { Caption = "Non-priority with facilities", Value = ReferenceGolfMgr.GetUsgaProvinceCount(prmProvince).ToString() });
return group;
}
//-------------------------------------------------------------------
private AjaxResponse_Data.StatsGroupData PopulateFacilityStatsData()
{
AjaxResponse_Data.StatsGroupData group = new AjaxResponse_Data.StatsGroupData() { Caption = "Facilities" };
// All facilities.
SaxGolfUsgaFacility.Params prmFacility = new SaxGolfUsgaFacility.Params();
prmFacility.StatusCat = SaxGolfUsgaFacility.StatusCat.Any;
group.Stats.Add(new AjaxResponse_Data.StatData() { Caption = "Total", Value = ReferenceGolfMgr.GetUsgaFacilityCount(prmFacility).ToString() });
// Active facilities.
prmFacility = new SaxGolfUsgaFacility.Params();
prmFacility.StatusCat = SaxGolfUsgaFacility.StatusCat.ActiveOnly;
group.Stats.Add(new AjaxResponse_Data.StatData() { Caption = "Active", Value = ReferenceGolfMgr.GetUsgaFacilityCount(prmFacility).ToString() });
// Inctive facilities.
prmFacility = new SaxGolfUsgaFacility.Params();
prmFacility.StatusCat = SaxGolfUsgaFacility.StatusCat.InactiveOnly;
group.Stats.Add(new AjaxResponse_Data.StatData() { Caption = "Inactive", Value = ReferenceGolfMgr.GetUsgaFacilityCount(prmFacility).ToString() });
// Deleted facilities.
prmFacility = new SaxGolfUsgaFacility.Params();
prmFacility.StatusCat = SaxGolfUsgaFacility.StatusCat.DeletedOnly;
group.Stats.Add(new AjaxResponse_Data.StatData() { Caption = "Deleted", Value = ReferenceGolfMgr.GetUsgaFacilityCount(prmFacility).ToString() });
// Facilities without courses.
prmFacility = new SaxGolfUsgaFacility.Params();
prmFacility.HasCourses = SacsHierarchyItem.Params.BoolState.False;
prmFacility.StatusCat = SaxGolfUsgaFacility.StatusCat.ActiveOnly;
group.Stats.Add(new AjaxResponse_Data.StatData() { Caption = "No courses", Value = ReferenceGolfMgr.GetUsgaFacilityCount(prmFacility).ToString() });
// Priority facilities with courses.
prmFacility = new SaxGolfUsgaFacility.Params();
prmFacility.HasCourses = SacsHierarchyItem.Params.BoolState.True;
prmFacility.StatusCat = SaxGolfUsgaFacility.StatusCat.ActiveOnly;
group.Stats.Add(new AjaxResponse_Data.StatData() { Caption = "With courses", Value = ReferenceGolfMgr.GetUsgaFacilityCount(prmFacility).ToString() });
return group;
}
//-------------------------------------------------------------------
private AjaxResponse_Data.StatsGroupData PopulateCourseStatsData()
{
AjaxResponse_Data.StatsGroupData group = new AjaxResponse_Data.StatsGroupData() { Caption = "Courses" };
// All courses.
SaxGolfUsgaCourse.Params prmCourse = new SaxGolfUsgaCourse.Params();
prmCourse.StatusCat = SaxGolfUsgaCourse.StatusCat.Any;
group.Stats.Add(new AjaxResponse_Data.StatData() { Caption = "Total", Value = ReferenceGolfMgr.GetUsgaCourseCount(prmCourse).ToString() });
// Active courses.
prmCourse = new SaxGolfUsgaCourse.Params();
prmCourse.StatusCat = SaxGolfUsgaCourse.StatusCat.ActiveOnly;
group.Stats.Add(new AjaxResponse_Data.StatData() { Caption = "Active", Value = ReferenceGolfMgr.GetUsgaCourseCount(prmCourse).ToString() });
// Inctive courses.
prmCourse = new SaxGolfUsgaCourse.Params();
prmCourse.StatusCat = SaxGolfUsgaCourse.StatusCat.InactiveOnly;
group.Stats.Add(new AjaxResponse_Data.StatData() { Caption = "Inactive", Value = ReferenceGolfMgr.GetUsgaCourseCount(prmCourse).ToString() });
// Deleted courses.
prmCourse = new SaxGolfUsgaCourse.Params();
prmCourse.StatusCat = SaxGolfUsgaCourse.StatusCat.DeletedOnly;
group.Stats.Add(new AjaxResponse_Data.StatData() { Caption = "Deleted", Value = ReferenceGolfMgr.GetUsgaCourseCount(prmCourse).ToString() });
// Priority courses without markers.
prmCourse = new SaxGolfUsgaCourse.Params();
prmCourse.PriorityFlagState = SacsHierarchyItem.Params.FlagState.True;
prmCourse.HasMarkers = SacsHierarchyItem.Params.BoolState.False;
prmCourse.StatusCat = SaxGolfUsgaCourse.StatusCat.ActiveOnly;
group.Stats.Add(new AjaxResponse_Data.StatData() { Caption = "Priority no markers", Value = ReferenceGolfMgr.GetUsgaCourseCount(prmCourse).ToString() });
// Non-Priority courses without markers.
prmCourse = new SaxGolfUsgaCourse.Params();
prmCourse.PriorityFlagState = SacsHierarchyItem.Params.FlagState.False;
prmCourse.HasMarkers = SacsHierarchyItem.Params.BoolState.False;
prmCourse.StatusCat = SaxGolfUsgaCourse.StatusCat.ActiveOnly;
group.Stats.Add(new AjaxResponse_Data.StatData() { Caption = "Non-priority no markers", Value = ReferenceGolfMgr.GetUsgaCourseCount(prmCourse).ToString() });
// Priority courses with markers.
prmCourse = new SaxGolfUsgaCourse.Params();
prmCourse.PriorityFlagState = SacsHierarchyItem.Params.FlagState.True;
prmCourse.HasMarkers = SacsHierarchyItem.Params.BoolState.True;
prmCourse.StatusCat = SaxGolfUsgaCourse.StatusCat.ActiveOnly;
group.Stats.Add(new AjaxResponse_Data.StatData() { Caption = "Priority with markers", Value = ReferenceGolfMgr.GetUsgaCourseCount(prmCourse).ToString() });
// Non-Priority courses with markers.
prmCourse = new SaxGolfUsgaCourse.Params();
prmCourse.PriorityFlagState = SacsHierarchyItem.Params.FlagState.False;
prmCourse.HasMarkers = SacsHierarchyItem.Params.BoolState.True;
prmCourse.StatusCat = SaxGolfUsgaCourse.StatusCat.ActiveOnly;
group.Stats.Add(new AjaxResponse_Data.StatData() { Caption = "Non-priority with markers", Value = ReferenceGolfMgr.GetUsgaCourseCount(prmCourse).ToString() });
return group;
}
//-------------------------------------------------------------------
private AjaxResponse_Data.StatsGroupData PopulateMarkerStatsData()
{
AjaxResponse_Data.StatsGroupData group = new AjaxResponse_Data.StatsGroupData() { Caption = "Markers" };
// All markers.
SaxGolfUsgaMarker.Params prmMarker = new SaxGolfUsgaMarker.Params();
prmMarker.StatusCat = SaxGolfUsgaMarker.StatusCat.Any;
group.Stats.Add(new AjaxResponse_Data.StatData() { Caption = "Total", Value = ReferenceGolfMgr.GetUsgaMarkerCount(prmMarker).ToString() });
// Active markers.
prmMarker = new SaxGolfUsgaMarker.Params();
prmMarker.StatusCat = SaxGolfUsgaMarker.StatusCat.ActiveOnly;
group.Stats.Add(new AjaxResponse_Data.StatData() { Caption = "Active", Value = ReferenceGolfMgr.GetUsgaMarkerCount(prmMarker).ToString() });
// Inctive markers.
prmMarker = new SaxGolfUsgaMarker.Params();
prmMarker.StatusCat = SaxGolfUsgaMarker.StatusCat.InactiveOnly;
group.Stats.Add(new AjaxResponse_Data.StatData() { Caption = "Inactive", Value = ReferenceGolfMgr.GetUsgaMarkerCount(prmMarker).ToString() });
// Deleted markers.
prmMarker = new SaxGolfUsgaMarker.Params();
prmMarker.StatusCat = SaxGolfUsgaMarker.StatusCat.DeletedOnly;
group.Stats.Add(new AjaxResponse_Data.StatData() { Caption = "Deleted", Value = ReferenceGolfMgr.GetUsgaMarkerCount(prmMarker).ToString() });
return group;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,590 @@
using Microsoft.AspNetCore.Mvc;
using NPOI.SS.Formula.Functions;
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.Consoles;
using Saxonite.Net.Core.Web.Admin.Models;
using Saxonite.Net.Core.Web.Admin.Models.Reference;
using Saxonite.Net.Core.Web.Admin.Procs;
using Saxonite.Net.Core.Web.Base.Procs;
using Saxonite.Net.Standard.Base.App;
using Saxonite.Net.Standard.Base.Bus;
using Saxonite.Net.Standard.Base.Bus.Reference;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Admin.Controllers.Reference
{
//=======================================================================
// Reference Golf Controller - Ajax requests.
//=======================================================================
partial class ReferenceGolfController
{
//-------------------------------------------------------------------
// USGA Country data.
private class UsgaCountryListData : AjaxResponse_Data.ItemData_Base
{
public String Code { get; set; }
public String Name { get; set; }
public bool? PriorityPopulate { get; set; }
public long? ProvinceCount { get; set; }
public long? ProvinceCountActive { get; set; }
public long? FacilityCount { get; set; }
public long? FacilityCountActive { get; set; }
public decimal? CoursesPercent { get; set; }
public String CoursesPercentFmt { get { return (CoursesPercent ?? 0.0m) > 0 ? $"{((decimal)CoursesPercent).ToString("N0")}%" : null; } }
}
private class UsgaCountryItemData : UsgaCountryListData
{
}
private void PopulateUsgaCountryResponseListData(SaxGolfUsgaCountry country, UsgaCountryListData data)
{
data.Id = SysId.ToString(country.Id);
data.Code = country.CountryCode;
data.Name = country.CountryName;
data.StatusCode = country.StatusCode;
data.StatusDesc = country.StatusDesc;
}
private void PopulateUsgaCountryResponseItemData(SaxGolfUsgaCountry country, UsgaCountryItemData data)
{
PopulateUsgaCountryResponseListData(country, data);
}
//-------------------------------------------------------------------
// USGA Province data.
private class UsgaProvinceListData : AjaxResponse_Data.ItemData_Base
{
public String Code { get; set; }
public String Name { get; set; }
public bool? PriorityPopulate { get; set; }
public long? FacilityCount { get; set; }
public long? FacilityCountActive { get; set; }
public long? CourseCount { get; set; }
public long? CourseCountActive { get; set; }
public decimal? MarkersPercent { get; set; }
public String MarkersPercentFmt { get { return (MarkersPercent ?? 0.0m) > 0 ? $"{((decimal)MarkersPercent).ToString("N0")}%" : null; } }
}
private class UsgaProvinceItemData : UsgaProvinceListData
{
}
private void PopulateUsgaProvinceResponseListData(SaxGolfUsgaProvince province, UsgaProvinceListData data)
{
data.Id = SysId.ToString(province.Id);
data.Code = province.ProvinceCode;
data.Name = province.ProvinceName;
data.StatusCode = province.StatusCode;
data.StatusDesc = province.StatusDesc;
}
private void PopulateUsgaProvinceResponseItemData(SaxGolfUsgaProvince province, UsgaProvinceItemData data)
{
PopulateUsgaProvinceResponseListData(province, data);
}
//-------------------------------------------------------------------
// USGA Course data.
private class UsgaCourseListData : AjaxResponse_Data.ItemData_Base
{
public long? UsgaCourseId { get; set; }
public String Caption { get; set; }
public String Name { get; set; }
public String FullName { get; set; }
public String Address1 { get; set; }
public String Address2 { get; set; }
public String City { get; set; }
public bool? PriorityPopulate { get; set; }
public long? MarkerCount { get; set; }
public long? MarkerCountActive { get; set; }
}
private class UsgaCourseItemData : UsgaCourseListData
{
}
private void PopulateUsgaCourseResponseListData(SaxGolfUsgaCourse course, UsgaCourseListData data)
{
data.Id = SysId.ToString(course.Id);
data.Caption = course.Caption;
data.Name = course.CourseName;
data.FullName = course.FullName;
data.Address1 = course.Address1;
data.Address2 = course.Address2;
data.City = course.City;
data.StatusCode = course.StatusCode;
data.StatusDesc = course.StatusDesc;
}
private void PopulateUsgaCourseResponseItemData(SaxGolfUsgaCourse course, UsgaCourseItemData data)
{
PopulateUsgaCourseResponseListData(course, data);
}
//-------------------------------------------------------------------
// USGA Marker data.
private class UsgaMarkerListData : AjaxResponse_Data.ItemData_Base
{
public long? UsgaMarkerId { get; set; }
public String Caption { get; set; }
public short? Holes { get; set; }
public String HolesCaption { get; set; }
public String Tee { get; set; }
public String Gender { get; set; }
public short? SlopeRating { get; set; }
public short? Par { get; set; }
public decimal? CourseRating { get; set; }
public decimal? BogeyRating { get; set; }
public short? Length { get; set; }
}
private class UsgaMarkerItemData : UsgaMarkerListData
{
}
private void PopulateUsgaMarkerResponseListData(SaxGolfUsgaMarker marker, UsgaMarkerListData data)
{
data.Id = SysId.ToString(marker.Id);
data.Caption = marker.Caption;
data.Holes = marker.Holes;
data.HolesCaption = marker.HolesCaption;
data.Tee = marker.Tee;
data.Gender = marker.Gender;
data.SlopeRating = marker.SlopeRating;
data.Par = marker.Par;
data.Gender = marker.Gender;
data.BogeyRating = marker.BogeyRating;
data.Length = marker.Length;
data.StatusCode = marker.StatusCode;
data.StatusDesc = marker.StatusDesc;
}
private void PopulateUsgaMarkerResponseItemData(SaxGolfUsgaMarker course, UsgaMarkerItemData data)
{
PopulateUsgaMarkerResponseListData(course, data);
}
//-------------------------------------------------------------------
// Get Country List ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_ReferenceGolf_CountryList : AjaxResponse_Data
{
// Construction.
public AjaxResponse_ReferenceGolf_CountryList(SaxModel_Admin model) : base(model) { }
// Country list.
public List<UsgaCountryListData> Countries { get; } = new List<UsgaCountryListData>();
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Reference/Golf/Ajax/GetUsgaCountryList")]
[Route("Reference/Golf/Ajax/GetUsgaCountries")]
[Route("Reference/Golf/Ajax/GetCountryList")]
[Route("Reference/Golf/Ajax/GetCountries")]
public IActionResult Ajax_GetCountryList(Model_ReferenceGolf model)
{
String messageTitle = Localiser["caption.reference.golf"];
AjaxResponse_ReferenceGolf_CountryList responseData = new AjaxResponse_ReferenceGolf_CountryList(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Item columns.
ColumnDfn colId = ReferenceGolfMgr.GetUsgaCountryColumnDfn(SaxGolfUsgaCountry.Column.Id);
ColumnDfn colCode = ReferenceGolfMgr.GetUsgaCountryColumnDfn(SaxGolfUsgaCountry.Column.CountryCode);
ColumnDfn colName = ReferenceGolfMgr.GetUsgaCountryColumnDfn(SaxGolfUsgaCountry.Column.CountryName);
ColumnDfn colProvinceCount_All = ReferenceGolfMgr.GetUsgaCountryColumnDfn(SaxGolfUsgaCountry.Column.ProvinceCount_All);
ColumnDfn colProvinceCount_Active = ReferenceGolfMgr.GetUsgaCountryColumnDfn(SaxGolfUsgaCountry.Column.ProvinceCount_Active);
ColumnDfn colFacilityCount_All = ReferenceGolfMgr.GetUsgaCountryColumnDfn(SaxGolfUsgaCountry.Column.FacilityCount_All);
ColumnDfn colFacilityCount_Active = ReferenceGolfMgr.GetUsgaCountryColumnDfn(SaxGolfUsgaCountry.Column.FacilityCount_Active);
ColumnDfn colCoursesPercent = ReferenceGolfMgr.GetUsgaCountryColumnDfn(SaxGolfUsgaCountry.Column.FacilityPercent);
ColumnDfn colFlags = ReferenceGolfMgr.GetUsgaCountryColumnDfn(SaxGolfUsgaCountry.Column.Flags);
ColumnDfn colStatus = ReferenceGolfMgr.GetUsgaCountryColumnDfn(SaxGolfUsgaCountry.Column.Status);
List<ColumnDfn> columns = new List<ColumnDfn>()
{
colId, colCode, colName, colProvinceCount_All, colProvinceCount_Active, colFacilityCount_All, colFacilityCount_Active, colCoursesPercent, colFlags, colStatus
};
// Item filter.
SaxGolfUsgaCountry.Params prmUsgaCountry = new SaxGolfUsgaCountry.Params();
prmUsgaCountry.CountryFilter = model.Filter.CountrySearch;
prmUsgaCountry.StatusCat = SaxGolfUsgaCountry.StatusCat.Any;
model.Paging.ItemTotal = (ulong)ReferenceGolfMgr.GetUsgaCountryCount(prmUsgaCountry);
model.PopulatePagingTo(prmUsgaCountry.ResultsPaging);
// Sort order.
prmUsgaCountry.SortColumns.Add(colName);
// Query items.
using (DbResultSet resultSet = ReferenceGolfMgr.ExecuteUsgaCountryQuery(columns, prmUsgaCountry))
{
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
{
NamedValue.Map values = resultSet.GetRecordValues();
ushort flags = (ushort)values.GetLong(colFlags.DataName);
UsgaCountryListData provinceData = new UsgaCountryListData()
{
Id = values.GetString(colId.DataName),
Code = values.GetString(colCode.DataName),
Name = values.GetString(colName.DataName),
PriorityPopulate = SacsHierarchyItem.GetFlag(flags, SaxGolfUsgaCountry.Flag_Priority),
ProvinceCount = values.GetLong(colProvinceCount_All.DataName),
ProvinceCountActive = values.GetLong(colProvinceCount_Active.DataName),
FacilityCount = values.GetLong(colFacilityCount_All.DataName),
FacilityCountActive = values.GetLong(colFacilityCount_Active.DataName),
CoursesPercent = values.GetDecimal(colCoursesPercent.DataName),
StatusCode = values.GetString(colStatus.DataName),
StatusDesc = SacsSystem.GetStatusDescForCode(values.GetString(colStatus.DataName))
};
responseData.Countries.Add(provinceData);
}
}
if (model.Paging.IsPaged)
model.Paging.ItemCount = (ushort)responseData.Countries.Count;
// Complete response.
responseData.SetPaging(model);
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Get Province List ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_ReferenceGolf_ProvinceList : AjaxResponse_Data
{
// Construction.
public AjaxResponse_ReferenceGolf_ProvinceList(SaxModel_Admin model) : base(model) { }
// Province list.
public List<UsgaProvinceListData> Provinces { get; } = new List<UsgaProvinceListData>();
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Reference/Golf/Ajax/GetUsgaProvinceList")]
[Route("Reference/Golf/Ajax/GetUsgaProvinces")]
[Route("Reference/Golf/Ajax/GetProvinceList")]
[Route("Reference/Golf/Ajax/GetProvinces")]
public IActionResult Ajax_GetProvinceList(Model_ReferenceGolf model)
{
String messageTitle = Localiser["caption.reference.golf"];
AjaxResponse_ReferenceGolf_ProvinceList responseData = new AjaxResponse_ReferenceGolf_ProvinceList(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Item columns.
ColumnDfn colId = ReferenceGolfMgr.GetUsgaProvinceColumnDfn(SaxGolfUsgaProvince.Column.Id);
ColumnDfn colCode = ReferenceGolfMgr.GetUsgaProvinceColumnDfn(SaxGolfUsgaProvince.Column.ProvinceCode);
ColumnDfn colName = ReferenceGolfMgr.GetUsgaProvinceColumnDfn(SaxGolfUsgaProvince.Column.ProvinceName);
ColumnDfn colFacilityCount_All = ReferenceGolfMgr.GetUsgaProvinceColumnDfn(SaxGolfUsgaProvince.Column.FacilityCount_All);
ColumnDfn colFacilityCount_Active = ReferenceGolfMgr.GetUsgaProvinceColumnDfn(SaxGolfUsgaProvince.Column.FacilityCount_Active);
ColumnDfn colCourseCount_All = ReferenceGolfMgr.GetUsgaProvinceColumnDfn(SaxGolfUsgaProvince.Column.CourseCount_All);
ColumnDfn colCourseCount_Active = ReferenceGolfMgr.GetUsgaProvinceColumnDfn(SaxGolfUsgaProvince.Column.CourseCount_Active);
ColumnDfn colMarkersPercent = ReferenceGolfMgr.GetUsgaProvinceColumnDfn(SaxGolfUsgaProvince.Column.MarkerPercent);
ColumnDfn colFlags = ReferenceGolfMgr.GetUsgaProvinceColumnDfn(SaxGolfUsgaProvince.Column.Flags);
ColumnDfn colStatus = ReferenceGolfMgr.GetUsgaProvinceColumnDfn(SaxGolfUsgaProvince.Column.Status);
List<ColumnDfn> columns = new List<ColumnDfn>()
{
colId, colCode, colName, colFacilityCount_All, colFacilityCount_Active, colCourseCount_All, colCourseCount_Active, colMarkersPercent, colFlags, colStatus
};
// Item filter.
SaxGolfUsgaProvince.Params prmUsgaProvince = new SaxGolfUsgaProvince.Params();
if (StringUtil.ToLong(model.Filter.CountryId) > 0)
prmUsgaProvince.CountryIdsIncluded.Add(SysId.ToSysId(model.Filter.CountryId));
prmUsgaProvince.ProvinceFilter = model.Filter.ProvinceSearch;
prmUsgaProvince.StatusCat = SaxGolfUsgaProvince.StatusCat.Any;
model.Paging.ItemTotal = (ulong)ReferenceGolfMgr.GetUsgaProvinceCount(prmUsgaProvince);
model.PopulatePagingTo(prmUsgaProvince.ResultsPaging);
// Sort order.
prmUsgaProvince.SortColumns.Add(colCode);
// Query items.
using (DbResultSet resultSet = ReferenceGolfMgr.ExecuteUsgaProvinceQuery(columns, prmUsgaProvince))
{
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
{
NamedValue.Map values = resultSet.GetRecordValues();
ushort flags = (ushort)values.GetLong(colFlags.DataName);
UsgaProvinceListData provinceData = new UsgaProvinceListData()
{
Id = values.GetString(colId.DataName),
Code = values.GetString(colCode.DataName),
Name = values.GetString(colName.DataName),
PriorityPopulate = SacsHierarchyItem.GetFlag(flags, SaxGolfUsgaProvince.Flag_Priority),
FacilityCount = values.GetLong(colFacilityCount_All.DataName),
FacilityCountActive = values.GetLong(colFacilityCount_Active.DataName),
CourseCount = values.GetLong(colCourseCount_All.DataName),
CourseCountActive = values.GetLong(colCourseCount_Active.DataName),
MarkersPercent = values.GetDecimal(colMarkersPercent.DataName),
StatusCode = values.GetString(colStatus.DataName),
StatusDesc = SacsSystem.GetStatusDescForCode(values.GetString(colStatus.DataName))
};
responseData.Provinces.Add(provinceData);
}
}
if (model.Paging.IsPaged)
model.Paging.ItemCount = (ushort)responseData.Provinces.Count;
// Complete response.
responseData.SetPaging(model);
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Get Course List ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_ReferenceGolf_CourseList : AjaxResponse_Data
{
// Construction.
public AjaxResponse_ReferenceGolf_CourseList(SaxModel_Admin model) : base(model) { }
// Course list.
public List<UsgaCourseListData> Courses { get; } = new List<UsgaCourseListData>();
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Reference/Golf/Ajax/GetUsgaCourseList")]
[Route("Reference/Golf/Ajax/GetUsgaCourses")]
[Route("Reference/Golf/Ajax/GetCourseList")]
[Route("Reference/Golf/Ajax/GetCourses")]
public IActionResult Ajax_GetCourseList(Model_ReferenceGolf model)
{
String messageTitle = Localiser["caption.reference.golf"];
AjaxResponse_ReferenceGolf_CourseList responseData = new AjaxResponse_ReferenceGolf_CourseList(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Item columns.
ColumnDfn colId = ReferenceGolfMgr.GetUsgaCourseColumnDfn(SaxGolfUsgaCourse.Column.Id);
ColumnDfn colUsgaCourseId = ReferenceGolfMgr.GetUsgaCourseColumnDfn(SaxGolfUsgaCourse.Column.UsgaCourseId);
ColumnDfn colCaption = ReferenceGolfMgr.GetUsgaCourseColumnDfn(SaxGolfUsgaCourse.Column.Caption);
ColumnDfn colCourseName = ReferenceGolfMgr.GetUsgaCourseColumnDfn(SaxGolfUsgaCourse.Column.CourseName);
ColumnDfn colFullName = ReferenceGolfMgr.GetUsgaCourseColumnDfn(SaxGolfUsgaCourse.Column.FullName);
ColumnDfn colAddress1 = ReferenceGolfMgr.GetUsgaCourseColumnDfn(SaxGolfUsgaCourse.Column.Address1);
ColumnDfn colAddress2 = ReferenceGolfMgr.GetUsgaCourseColumnDfn(SaxGolfUsgaCourse.Column.Address2);
ColumnDfn colCity = ReferenceGolfMgr.GetUsgaCourseColumnDfn(SaxGolfUsgaCourse.Column.City);
ColumnDfn colMarkerCount_All = ReferenceGolfMgr.GetUsgaCourseColumnDfn(SaxGolfUsgaCourse.Column.MarkerCount_All);
ColumnDfn colMarkerCount_Active = ReferenceGolfMgr.GetUsgaCourseColumnDfn(SaxGolfUsgaCourse.Column.MarkerCount_Active);
ColumnDfn colFlags = ReferenceGolfMgr.GetUsgaCourseColumnDfn(SaxGolfUsgaCourse.Column.Flags);
ColumnDfn colStatus = ReferenceGolfMgr.GetUsgaCourseColumnDfn(SaxGolfUsgaCourse.Column.Status);
List<ColumnDfn> columns = new List<ColumnDfn>()
{
colId, colUsgaCourseId, colCaption, colCourseName, colFullName, colAddress1, colAddress2, colCity, colMarkerCount_All, colMarkerCount_Active, colFlags, colStatus
};
// Item filter.
SaxGolfUsgaCourse.Params prmUsgaCourse = new SaxGolfUsgaCourse.Params();
if (StringUtil.ToLong(model.Filter.ProvinceId) > 0)
prmUsgaCourse.ProvinceIdsIncluded.Add(SysId.ToSysId(model.Filter.ProvinceId));
prmUsgaCourse.CourseFilter = model.Filter.CourseSearch;
prmUsgaCourse.StatusCat = SaxGolfUsgaCourse.StatusCat.Any;
model.Paging.ItemTotal = (ulong)ReferenceGolfMgr.GetUsgaCourseCount(prmUsgaCourse);
model.PopulatePagingTo(prmUsgaCourse.ResultsPaging);
// Restrict results is not paged.
if (!prmUsgaCourse.ResultsPaging.Paged)
{
prmUsgaCourse.ResultsPaging.PageNo = 1;
prmUsgaCourse.ResultsPaging.PageLength = 200;
}
// Sort order.
prmUsgaCourse.SortColumns.Add(colCaption);
// Query items.
using (DbResultSet resultSet = ReferenceGolfMgr.ExecuteUsgaCourseQuery(columns, prmUsgaCourse))
{
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
{
NamedValue.Map values = resultSet.GetRecordValues();
ushort flags = (ushort)values.GetLong(colFlags.DataName);
UsgaCourseListData courseData = new UsgaCourseListData()
{
Id = values.GetString(colId.DataName),
UsgaCourseId = values.GetLong(colUsgaCourseId.DataName),
Caption = values.GetString(colCaption.DataName),
Name = values.GetString(colCourseName.DataName),
FullName = values.GetString(colFullName.DataName),
Address1 = values.GetString(colAddress1.DataName),
Address2 = values.GetString(colAddress2.DataName),
City = values.GetString(colCity.DataName),
PriorityPopulate = SacsHierarchyItem.GetFlag(flags, SaxGolfUsgaCourse.Flag_Priority),
MarkerCount = values.GetLong(colMarkerCount_All.DataName),
MarkerCountActive = values.GetLong(colMarkerCount_Active.DataName),
StatusCode = values.GetString(colStatus.DataName),
StatusDesc = SacsSystem.GetStatusDescForCode(values.GetString(colStatus.DataName))
};
responseData.Courses.Add(courseData);
}
}
if (model.Paging.IsPaged)
model.Paging.ItemCount = (ushort)responseData.Courses.Count;
// Complete response.
responseData.SetPaging(model);
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Get Marker List ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_ReferenceGolf_CleanseUsgaCourseCaptions : AjaxResponse_Data
{
// Construction.
public AjaxResponse_ReferenceGolf_CleanseUsgaCourseCaptions(SaxModel_Admin model) : base(model) { }
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Reference/Golf/Ajax/CleanseUsgaCourseCaptions")]
public IActionResult Ajax_CleanseUsgaCourseCaptions(Model_ReferenceGolf model)
{
String messageTitle = Localiser["caption.reference.golf"];
AjaxResponse_ReferenceGolf_CleanseUsgaCourseCaptions responseData = new AjaxResponse_ReferenceGolf_CleanseUsgaCourseCaptions(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
// Process console.
SaxProcessConsole_Admin processConsole = GetProcessConsole_Admin(SessionContext);
if (processConsole == null)
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.system"]));
try
{
// Check whether process is in progress.
if (processConsole.IsProcessing(SaxProcessConsole_Admin.ProcessType.GolfUsgaDataLookup))
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.process.in.progress"]));
// Configure the process.
SaxGolfUsgaDataLookup.Config processConfig = new SaxGolfUsgaDataLookup.Config(SessionContext, DbUpdateInfo);
processConfig.Tasks.Add(SaxGolfUsgaDataLookup.Task.CleanseCourses);
SaxGolfUsgaDataLookup process = new SaxGolfUsgaDataLookup(processConfig);
// Store the process and run.
processConsole.SetProcess(process);
process.RunAsync();
}
catch (Exception exc)
{
SaLogger.Warn($"{SysUtil.Fn} {exc.GetType().Name} initialising organisation uploader.");
return Json(responseData.SetPopupMessage(messageTitle, exc.GetType().Name + " initialising USGA Golf Data Loader."));
}
}
// Complete response.
Json(responseData.SetPopupMessage(messageTitle, "Course caption cleansing is underway."));
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Get Marker List ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_ReferenceGolf_MarkerList : AjaxResponse_Data
{
// Construction.
public AjaxResponse_ReferenceGolf_MarkerList(SaxModel_Admin model) : base(model) { }
// Marker list.
public List<UsgaMarkerListData> Markers { get; } = new List<UsgaMarkerListData>();
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Reference/Golf/Ajax/GetUsgaMarkerList")]
[Route("Reference/Golf/Ajax/GetUsgaMarkers")]
[Route("Reference/Golf/Ajax/GetMarkerList")]
[Route("Reference/Golf/Ajax/GetMarkers")]
public IActionResult Ajax_GetMarkerList(Model_ReferenceGolf model)
{
String messageTitle = Localiser["caption.reference.golf"];
AjaxResponse_ReferenceGolf_MarkerList responseData = new AjaxResponse_ReferenceGolf_MarkerList(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Item columns.
ColumnDfn colId = ReferenceGolfMgr.GetUsgaMarkerColumnDfn(SaxGolfUsgaMarker.Column.Id);
ColumnDfn colUsgaMarkerId = ReferenceGolfMgr.GetUsgaMarkerColumnDfn(SaxGolfUsgaMarker.Column.UsgaMarkerId);
ColumnDfn colCaption = ReferenceGolfMgr.GetUsgaMarkerColumnDfn(SaxGolfUsgaMarker.Column.Caption);
ColumnDfn colHoles = ReferenceGolfMgr.GetUsgaMarkerColumnDfn(SaxGolfUsgaMarker.Column.Holes);
ColumnDfn colHolesCaption = ReferenceGolfMgr.GetUsgaMarkerColumnDfn(SaxGolfUsgaMarker.Column.HolesCaption);
ColumnDfn colTee = ReferenceGolfMgr.GetUsgaMarkerColumnDfn(SaxGolfUsgaMarker.Column.Tee);
ColumnDfn colGender = ReferenceGolfMgr.GetUsgaMarkerColumnDfn(SaxGolfUsgaMarker.Column.Gender);
ColumnDfn colSlopeRating = ReferenceGolfMgr.GetUsgaMarkerColumnDfn(SaxGolfUsgaMarker.Column.SlopeRating);
ColumnDfn colPar = ReferenceGolfMgr.GetUsgaMarkerColumnDfn(SaxGolfUsgaMarker.Column.Par);
ColumnDfn colCourseRating = ReferenceGolfMgr.GetUsgaMarkerColumnDfn(SaxGolfUsgaMarker.Column.CourseRating);
ColumnDfn colBogeyRating = ReferenceGolfMgr.GetUsgaMarkerColumnDfn(SaxGolfUsgaMarker.Column.BogeyRating);
ColumnDfn colLength = ReferenceGolfMgr.GetUsgaMarkerColumnDfn(SaxGolfUsgaMarker.Column.Length);
ColumnDfn colFlags = ReferenceGolfMgr.GetUsgaMarkerColumnDfn(SaxGolfUsgaMarker.Column.Flags);
ColumnDfn colStatus = ReferenceGolfMgr.GetUsgaMarkerColumnDfn(SaxGolfUsgaMarker.Column.Status);
List<ColumnDfn> columns = new List<ColumnDfn>()
{
colId, colUsgaMarkerId, colCaption, colHoles, colHolesCaption, colTee, colGender, colSlopeRating, colPar, colCourseRating, colBogeyRating, colLength, colFlags, colStatus
};
// Item filter.
SaxGolfUsgaMarker.Params prmUsgaMarker = new SaxGolfUsgaMarker.Params();
if (StringUtil.ToLong(model.Filter.CourseId) > 0)
prmUsgaMarker.CourseIdsIncluded.Add(SysId.ToSysId(model.Filter.CourseId));
prmUsgaMarker.MarkerFilter = model.Filter.MarkerSearch;
prmUsgaMarker.StatusCat = SaxGolfUsgaMarker.StatusCat.Any;
model.Paging.ItemTotal = (ulong)ReferenceGolfMgr.GetUsgaMarkerCount(prmUsgaMarker);
model.PopulatePagingTo(prmUsgaMarker.ResultsPaging);
// Restrict results is not paged.
if (!prmUsgaMarker.ResultsPaging.Paged)
{
prmUsgaMarker.ResultsPaging.PageNo = 1;
prmUsgaMarker.ResultsPaging.PageLength = 200;
}
// Sort order.
prmUsgaMarker.SortColumns.Add(colCaption);
// Query items.
using (DbResultSet resultSet = ReferenceGolfMgr.ExecuteUsgaMarkerQuery(columns, prmUsgaMarker))
{
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
{
NamedValue.Map values = resultSet.GetRecordValues();
ushort flags = (ushort)values.GetLong(colFlags.DataName);
UsgaMarkerListData markerData = new UsgaMarkerListData()
{
Id = values.GetString(colId.DataName),
UsgaMarkerId = values.GetLong(colUsgaMarkerId.DataName),
Caption = values.GetString(colCaption.DataName),
Holes = (short)values.GetLong(colHoles.DataName),
HolesCaption = values.GetString(colHolesCaption.DataName),
Tee = values.GetString(colTee.DataName),
Gender = values.GetString(colGender.DataName),
SlopeRating = (short)values.GetLong(colSlopeRating.DataName),
Par = (short)values.GetLong(colPar.DataName),
CourseRating = values.GetDecimal(colCourseRating.DataName),
BogeyRating = values.GetDecimal(colBogeyRating.DataName),
Length = (short)values.GetLong(colLength.DataName),
StatusCode = values.GetString(colStatus.DataName),
StatusDesc = SacsSystem.GetStatusDescForCode(values.GetString(colStatus.DataName))
};
responseData.Markers.Add(markerData);
}
}
if (model.Paging.IsPaged)
model.Paging.ItemCount = (ushort)responseData.Markers.Count;
// Complete response.
responseData.SetPaging(model);
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,46 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Saxonite.Net.Core.Web.Admin.Models.Reference;
using Saxonite.Net.Core.Web.Base.App;
using Saxonite.Net.Core.Web.Base.Controllers;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Admin.Controllers.Reference
{
//=======================================================================
public partial class ReferenceGolfController : SaxController_Admin
{
//-------------------------------------------------------------------
// Construction.
public ReferenceGolfController(SaxLocaliser localiser, ILogger<HomeController> logger) :
base(localiser, logger)
{
}
//-------------------------------------------------------------------
[HttpGet]
[Route("Reference/Golf")]
public IActionResult Index()
{
// Validate session.
if (!ValidUserSession)
AutoLoginUser();
if (!ValidUserSession)
return RedirectToAction("", "");
// Paging.
Model_ReferenceGolf model = new Model_ReferenceGolf(Localiser);
//model.Paging.PageNo = 1;
//model.Paging.PageLength = 12;
return View("/Views/Reference/Golf/Index.cshtml",model);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,281 @@
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 RegionController
{
//-------------------------------------------------------------------
// Region data.
private class RegionListData : AjaxResponse_Data.ItemData_Base
{
public int? RegionType { get; set; } = null;
public String RegionTypeDesc { get; set; }
public String CountryIso2Code { get; set; }
public String Name { get; set; }
public String ParentName { get; set; }
}
private class RegionItemData : RegionListData
{
public String RegionTypeCode { get; set; }
public String RegionIdParent { get; set; }
}
private void PopulateRegionResponseListData(SaxRegion region, RegionListData data)
{
data.Id = SysId.ToString(region.Id);
data.RegionType = (int)region.RegionType;
data.RegionTypeDesc = RegionTypeCodes.GetDescForType(region.RegionType);
data.CountryIso2Code = region.CountryIso2Code;
data.Name = region.Name;
using (SaxRegion parentRegion = ReferenceMgr.GetRegionById(region.RegionIdParent))
{
if (parentRegion != null) data.ParentName = parentRegion.Name;
}
data.StatusCode = region.StatusCode;
data.StatusDesc = region.StatusDesc;
}
private void PopulateRegionResponseItemData(SaxRegion region, RegionItemData data)
{
PopulateRegionResponseListData(region, data);
data.RegionTypeCode = RegionTypeCodes.GetCodeForType(region.RegionType);
data.RegionIdParent = SysId.ToString(region.RegionIdParent);
}
//-------------------------------------------------------------------
// Get Region List ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_RegionList : AjaxResponse_Data
{
// Construction.
public AjaxResponse_RegionList(SaxModel_Admin model) : base(model) { }
// Region list.
public List<RegionListData> Regions { get; } = new List<RegionListData>();
// Reference data.
public List<CodeDesc> Statuses { get; } = new List<CodeDesc>();
public String DefaultStatusCode { get; set; }
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Region/Ajax/GetList")]
public IActionResult Ajax_GetList(Model_Region model)
{
String fn = GetType().Name + ".Ajax_GetList";
String messageTitle = Localiser["region.caption.regions"];
AjaxResponse_RegionList responseData = new AjaxResponse_RegionList(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Reference data - statuses.
foreach (SaStatus status in SaxRegion.ValidStatuses.FindAll(item => item != SaStatus.Deleted))
responseData.Statuses.Add(SacsSystem.GetStatusCode(status), Localiser.GetStatusCaption(status));
responseData.DefaultStatusCode = SacsSystem.GetStatusCode(SaxRegion.DefaultStatus);
// Item filter.
SaxRegion.Params prmRegion = new SaxRegion.Params();
prmRegion.RegionFilter = model.Filter.Region;
if (model.Filter.RegionTypeCode != null)
prmRegion.RegionType = model.FilterRegionType;
prmRegion.StatusCat = SaxRegion.StatusCat.NotDeleted;
model.Paging.ItemTotal = (ulong)ReferenceMgr.GetRegionCount(prmRegion);
model.PopulatePagingTo(prmRegion.ResultsPaging);
// Item columns.
ColumnDfn colId = ReferenceMgr.GetRegionColumnDfn(SaxRegion.Column.Id);
ColumnDfn colType = ReferenceMgr.GetRegionColumnDfn(SaxRegion.Column.RegionType);
ColumnDfn colIso2Code = ReferenceMgr.GetRegionColumnDfn(SaxRegion.Column.CountryIso2Code);
ColumnDfn colName = ReferenceMgr.GetRegionColumnDfn(SaxRegion.Column.Name);
ColumnDfn colParentName = ReferenceMgr.GetRegionColumnDfn(SaxRegion.Column.Parent_Name);
ColumnDfn colStatus = ReferenceMgr.GetRegionColumnDfn(SaxRegion.Column.Status);
List<ColumnDfn> columns = new List<ColumnDfn>()
{
colId,
colType,
colIso2Code,
colName,
colParentName,
colStatus
};
// Sort order.
prmRegion.SortColumns.Add(colName);
// Query items.
using (DbResultSet resultSet = ReferenceMgr.ExecuteRegionQuery(columns, prmRegion))
{
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
{
NamedValue.Map values = resultSet.GetRecordValues();
Sax.RegionType regionType = (Sax.RegionType)values.GetLong(colType.DataName);
RegionListData regionData = new RegionListData()
{
Id = values.GetString(colId.DataName),
RegionType = (int)regionType,
RegionTypeDesc = RegionTypeCodes.GetDescForType(regionType),
CountryIso2Code = values.GetString(colIso2Code.DataName),
Name = values.GetString(colName.DataName),
ParentName = values.GetString(colParentName.DataName),
StatusCode = values.GetString(colStatus.DataName),
StatusDesc = SacsSystem.GetStatusDescForCode(values.GetString(colStatus.DataName))
};
responseData.Regions.Add(regionData);
}
}
if (model.Paging.IsPaged)
model.Paging.ItemCount = (ushort)responseData.Regions.Count;
// Complete response.
responseData.SetPaging(model);
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Get Region ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_Region : AjaxResponse_Data
{
// Construction.
public AjaxResponse_Region(SaxModel_Admin model) : base(model) { }
// Region details.
public RegionItemData region { get; } = new RegionItemData();
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Region/Ajax/GetItem")]
public IActionResult Ajax_GetItem(Model_Region model)
{
String fn = GetType().Name + ".Ajax_GetItem";
String messageTitle = Localiser["region.caption.region"];
AjaxResponse_Region responseData = new AjaxResponse_Region(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
// Identify item.
SysId regionId = SysId.ToSysId(model.RegionId);
if (!SysId.IsValid(regionId))
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.specified"]));
// Retrieve item.
SaxRegion region = ReferenceMgr.GetRegionById(regionId);
disposableItems.Add(region);
if (region == null)
{
SaLogger.Warn(fn + " Failed to retrieve region for Id: '" + model.RegionId + "'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Construct response.
PopulateRegionResponseItemData(region, responseData.region);
}
// Complete response.
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Store Region ajax request.
//-------------------------------------------------------------------
[HttpPost]
[Route("Region/Ajax/StoreItem")]
public IActionResult Ajax_StoreItem(Model_Region model)
{
String fn = GetType().Name + ".Ajax_StoreItem";
String messageTitle = Localiser["region.caption.region"];
AjaxResponse_Region responseData = new AjaxResponse_Region(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
// Validate item properties.
model.ValidateRegionData(responseData);
if (responseData.HasControlErrors())
return Json(responseData);
// Retrieve existing item.
SysId regionId = SysId.ToSysId(model.RegionId);
SaxRegion region = ReferenceMgr.GetRegionById(regionId);
disposableItems.Add(region);
if (region == null && SysId.IsValid(regionId))
{
SaLogger.Warn(fn + " Failed to retrieve region for Id: '" + model.RegionId + "'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Create new item if required.
if (region == null)
{
region = ReferenceMgr.CreateRegion();
disposableItems.Add(region);
}
// Populate item properties.
if (model.RegionTypeCode != null)
region.RegionType = model.RegionType;
if (model.RegionCountryIso2Code != null)
region.CountryIso2Code = model.RegionCountryIso2Code;
if (model.RegionName != null)
region.Name = model.RegionName;
if (model.RegionIdParent != null)
region.RegionIdParent = SysId.ToSysId(model.RegionIdParent);
if (model.RegionStatusCode != null)
region.Status = model.RegionStatus;
// Store item.
if (!SysId.IsValid(region.Id))
{
if (!ReferenceMgr.InsertRegion(region, DbUpdateInfo))
{
SaLogger.Warn(fn + " Failed to insert region: " + ReferenceMgr.ErrorStateMessage);
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.insert.failed"] + "\n\n" + ReferenceMgr.ErrorStateMessage));
}
}
else
{
if (!ReferenceMgr.UpdateRegion(region, DbUpdateInfo))
{
SaLogger.Warn(fn + " Failed to update region [Id:" + SysId.ToString(region.Id) + "]: " + ReferenceMgr.ErrorStateMessage);
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + ReferenceMgr.ErrorStateMessage));
}
}
// Construct response.
PopulateRegionResponseItemData(region, responseData.region);
}
// Complete response.
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,45 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Saxonite.Net.Core.Web.Admin.Models;
using Saxonite.Net.Core.Web.Base.App;
using Saxonite.Net.Core.Web.Base.Controllers;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Admin.Controllers
{
//=======================================================================
public partial class RegionController : SaxController_Admin
{
//-------------------------------------------------------------------
// Construction.
public RegionController(SaxLocaliser localiser, ILogger<HomeController> logger) :
base(localiser, logger)
{
}
//-------------------------------------------------------------------
[HttpGet]
public IActionResult Index()
{
// Validate session.
if (!ValidUserSession)
AutoLoginUser();
if (!ValidUserSession)
return RedirectToAction("", "");
// Paging.
Model_Region model = new Model_Region(Localiser);
model.Paging.PageNo = 1;
model.Paging.PageLength = 12;
return View(model);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,288 @@
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Helpers;
using Sarand.Net.Standard.Base.Utils;
using Saxonite.Net.Core.Web.Admin.App;
using Saxonite.Net.Core.Web.Admin.Consoles;
using Saxonite.Net.Core.Web.Base.App;
using Saxonite.Net.Core.Web.Base.Controllers;
using Saxonite.Net.Standard.Base.App;
using Saxonite.Net.Standard.Base.Bus;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
namespace Saxonite.Net.Core.Web.Admin.Controllers
{
//=======================================================================
public class SaxController_Admin : SaxController_Web
{
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
// Protected construction.
protected SaxController_Admin(SaxLocaliser localiser, ILogger<HomeController> logger) : base(localiser)
{
}
//-------------------------------------------------------------------
// Session data.
//-------------------------------------------------------------------
/// <summary>Admin session data</summary>
protected SaxSessionData_Admin SessionData_Admin { get { return SessionData as SaxSessionData_Admin; } }
protected override SaxSessionData DeserializeSessionData(String sessionDataJson)
{
return JsonConvert.DeserializeObject<SaxSessionData_Admin>(sessionDataJson, SaxSessionDataJsonSerializerSettings);
}
//-------------------------------------------------------------------
// Globals.
//-------------------------------------------------------------------
protected SaxConsoleHelper_Admin ConsoleHelper_Admin { get { return ConsoleHelper as SaxConsoleHelper_Admin; } }
protected SaxProcessConsole_Admin GetProcessConsole_Admin(SaxSessionContext sessionContext)
{
return ConsoleHelper_Admin?.GetProcessConsole(sessionContext) as SaxProcessConsole_Admin;
}
//-------------------------------------------------------------------
// User Session.
//-------------------------------------------------------------------
/// <summary>
/// Sign the account in.
/// <para>- Record login time and clear invalid login data.</para>
/// <para>- Open user session - store details against Sesion Data.</para>
/// <para>- Set 'Keep logged in' cookie if required.</para>
/// </summary>
protected bool SignUserIn(SaxUserAccount userAccount, bool keepLoggedIn, ref String errorMessage)
{
String fn = GetType().Name + ".SignUserIn";
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
List<SaxUserRole> userRoles = AccountMgr.GetRolesForUser(userAccount.Id);
disposableItems.AddRange(userRoles);
// Validate User Account, just in case - should have been done before arriving here!
if (userAccount == null)
{
SaLogger.Error(fn + " Unable to sign account in - null user account.");
errorMessage = Localiser["security.msg.signin.invalid"];
return false;
}
if (!userAccount.IsStatusActive)
{
SaLogger.Error(fn + " Unable to sign account in - user account is not active. Email: " + userAccount.EmailAddress);
errorMessage = Localiser["security.msg.signin.invalid"];
return false;
}
// 'Keep logged in' can be switched off by the application configuration.
keepLoggedIn = App.AppConfig.Security.SignIn.KeepLoggedIn.Allowed && keepLoggedIn;
// Log user in - Admin details stored against System Admin role.
SaxUserRole userRole = userRoles.Find(item => item.UserRole == Sax.UserRole.SystemAdmin);
if (userRole == null)
{
SaLogger.Warn(fn + " System Admin role not found for " + userAccount.EmailAddress);
errorMessage = Localiser["security.msg.signin.invalid"];
return false;
}
userRole.KeepLoggedIn = keepLoggedIn;
userRole.LastLoginAt = DateTime.Now;
userRole.InvalidLoginCount = 0;
userRole.LastInvalidLoginAt = null;
if (!AccountMgr.UpdateUserRole(userRole, DbUpdateInfo))
{
SaLogger.Warn(fn + " Failed to update Admin user role for " + userAccount.EmailAddress + ": " + AccountMgr.ErrorStateMessage);
errorMessage = Localiser["security.msg.signin.invalid"];
return false;
}
SaLogger.Info(fn + " User session opened for " + userAccount.EmailAddress, true);
// Create session.
OpenUserSession(SessionContext, userAccount, userRoles);
// Send the 'Keep Logged In' cookie if requested.
if (userRole.KeepLoggedIn)
SetKeepLoggedInCookie();
}
return true;
}
//-------------------------------------------------------------------
/// <summary>Start a new user session on sign-in.</summary>
protected bool OpenUserSession(SaxSessionContext sessionContext, SaxUserAccount userAccount, List<SaxUserRole> userRoles)
{
// Validate user properties.
if (!SysId.IsValid(userAccount.Id)) return false;
if (StringUtil.IsEmpty(userAccount.EmailAddress)) return false;
// Close previous session.
CloseUserSession();
// Open session.
return SetSessionData(new SaxSessionData_Admin(sessionContext, userAccount, userRoles));
}
protected void CloseUserSession()
{
ClearSessionData();
}
//-------------------------------------------------------------------
// Keep Logged In.
//-------------------------------------------------------------------
/// <summary>
/// Check for 'Keep Logged In' cookie and auto-login user if details are valid.
/// </summary>
/// <returns></returns>
protected bool AutoLoginUser()
{
String fn = "AutoLoginUser";
// NB Only allowed if there isn't a current user session.
if (ValidUserSession) return true;
if (!AppConfig.Security.SignIn.KeepLoggedIn.Allowed) return false;
// Extract cookie details.
String encryptedParams = GetRequestCookie(KeepLoggedIn.CookieName);
KeepLoggedInParams keepLoggedInParams = ExtractKeepLoggedInParams(encryptedParams);
String logSuffix = keepLoggedInParams.GetValidParams().Concat("", " ", ".");
if (SysId.IsValid(keepLoggedInParams.UserAccountId))
{
using (SaxUserAccount userAccount = AccountMgr.GetUserAccountById(keepLoggedInParams.UserAccountId))
{
if (userAccount != null)
{
if (userAccount.IsStatusActive)
{
String errorMessage = "";
if (SignUserIn(userAccount, true, ref errorMessage))
{
// Successful login.
SaLogger.Info(fn + " Keep Logged In cookie found." + logSuffix);
return true;
}
else SaLogger.Warn(fn + " Keep Logged In cookie found but failed to sign user in: " + errorMessage + "." + logSuffix);
}
else SaLogger.Warn(fn + " Keep Logged In cookie found but UserAccount is not active." + logSuffix);
}
else SaLogger.Warn(fn + " Keep Logged In cookie found but UserAccount not found." + logSuffix);
}
}
ClearKeepLoggedInCookie();
return false;
}
//-------------------------------------------------------------------
/// <summary>Keep-Logged-In cookie.</summary>
protected void SetKeepLoggedInCookie()
{
String keepLoggedInCookieValue = GenerateKeepLoggedInCookieValue();
int cookieExpiresDays = Math.Max(Sax.Config.Security.SignIn.KeepLoggedIn.CookieExpiresDays_Min, Math.Min(Sax.Config.Security.SignIn.KeepLoggedIn.CookieExpiresDays_Max, AppConfig.Security.SignIn.KeepLoggedIn.CookieExpiresDays));
SetResponseCookie(KeepLoggedIn.CookieName, keepLoggedInCookieValue, DateTime.Now.AddDays(cookieExpiresDays), true);
SaLogger.Debug(this.GetType().Name + ".SetKeepLoggedInCookie 'Keep Logged In' cookie set. Value: " + keepLoggedInCookieValue);
}
/// <summary>Set the 'Keep Logged In' response cookie to expired with no data.</summary>
public void ClearKeepLoggedInCookie()
{
if (StringUtil.IsEmpty(GetRequestCookie(KeepLoggedIn.CookieName))) return;
// Clear cookie by setting expiry date in the past.
SetResponseCookie(KeepLoggedIn.CookieName, "", DateTime.Today.AddDays(-7), true);
SaLogger.Info($"{SysUtil.Fn} 'Keep Logged In' cookie cleared.");
}
//-------------------------------------------------------------------
// 'Keep Logged In' cookie and encryption config.
// NB - IV value is generated from the key and fixed so that it doesn't change when the application restarts
// to ensure that cookie value can be decrypted even if application restarts in the meantime.
// - Generating IV from same key results in different value each time.
// - IV Must be regenerated if key is changed: uncomment 'Generated' variable and copy resulting value to IV variable.
public static class KeepLoggedIn
{
public static String CookieName = "SXKLI";
public static String Base64Key = SacsEncryptionHelper.EncodePlainKey("<=Saxonite^{kEEp+1Ogg3d-IN}~32*>", SacsEncryptionHelper.KeyLength);
public const String Purpose = "Saxonite.Admin.KeepLoggedIn";
}
//-------------------------------------------------------------------
// Keep-logged-in cookie value.
private static readonly String ParamName_KeepLoggedIn_UserAccountId = "uai";
private String GenerateKeepLoggedInCookieValue()
{
NameValueCollection keepLoggedInParams = new NameValueCollection();
keepLoggedInParams.Add(ParamName_KeepLoggedIn_UserAccountId, SysId.ToString(SessionData_Admin.User.UserAccountId));
return EncryptParameters(keepLoggedInParams, KeepLoggedIn.Base64Key, KeepLoggedIn.Purpose);
}
//-------------------------------------------------------------------
public class KeepLoggedInParams
{
public SysId UserAccountId { get; set; }
/// <summary>Retrieve the list of valid parameters in printable format [Name: Value].</summary>
public List<String> GetValidParams()
{
List<String> values = new List<String>();
if (SysId.IsValid(UserAccountId))
values.Add("UserAccountId: " + SysId.ToString(UserAccountId));
return values;
}
}
public KeepLoggedInParams ExtractKeepLoggedInParams(String encryptedParams)
{
KeepLoggedInParams keepLoggedInParams = new KeepLoggedInParams();
if (!StringUtil.IsEmpty(encryptedParams))
{
NameValueCollection linkParams = DecryptParameters(encryptedParams, KeepLoggedIn.Base64Key, KeepLoggedIn.Purpose);
keepLoggedInParams.UserAccountId = new SysId(StringUtil.ToInt(linkParams.Get(ParamName_KeepLoggedIn_UserAccountId)));
}
if (DebugMode) SaLogger.Info($"{SysUtil.Fn} UserAccountId: " + SysId.ToString(keepLoggedInParams.UserAccountId));
return keepLoggedInParams;
}
//-------------------------------------------------------------------
// Image utilities.
//-------------------------------------------------------------------
/// <summary>
/// Construct URL to display Organisation logo.
/// <para>- Option to allow for 'Upload' image if active.</para>
/// </summary>
protected String GetOrganisationLogoLink(SysId organisationId, bool uploadImage)
{
String imageLink = "Image/Organisation/Logo" + (uploadImage ? "/Upload" : "") + "?";
NameValueCollection parameters = new NameValueCollection();
parameters.Add(ImageController.ParamName_OrganisationId, SysId.ToString(organisationId));
return imageLink + ImageController.ParamName_OrganisationLogoEncrypted + "=" + EncryptSessionParams(parameters);
}
//-------------------------------------------------------------------
// Parameter encryption, eg. for images.
//-------------------------------------------------------------------
// Purpose-bound AES-GCM envelopes use a fresh nonce for every encrypted value.
public static String Base64Key_AdminApp { get; } = SacsEncryptionHelper.EncodePlainKey("Saxonite&Admin#Encryption-" + DateTime.Today.Format("ddMMyy"), SacsEncryptionHelper.KeyLength);
private const String Purpose_AdminApp = "Saxonite.Admin.Parameters";
protected String EncryptSessionParams(NameValueCollection parameters)
{
return HttpUtility.UrlEncode(EncryptParameters(parameters, Base64Key_AdminApp, Purpose_AdminApp));
}
public static String EncryptSessionParams(SaxController_Admin controller, NameValueCollection parameters)
{
return controller.EncryptSessionParams(parameters);
}
protected NameValueCollection ExtractSessionParams(String encryptedParams)
{
return DecryptParameters(encryptedParams, Base64Key_AdminApp, Purpose_AdminApp);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,500 @@
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.Email;
using Sarand.Net.Standard.Base.Helpers;
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 Saxonite.Net.Standard.Base.Consoles;
using Saxonite.Net.Standard.Base.Helpers;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
namespace Saxonite.Net.Core.Web.Admin.Controllers
{
//=======================================================================
partial class SecurityController
{
//-------------------------------------------------------------------
// Sign-in request.
//-------------------------------------------------------------------
private class AjaxResponse_Signin : AjaxResponse_Data
{
// Construction.
public AjaxResponse_Signin(SaxModel_Admin model) : base(model) { }
}
// Ajax request.
[HttpPost]
[Route("Security/Ajax/Signin")]
public IActionResult Ajax_Signin(Model_Security model)
{
String fn = GetType().Name + ".Ajax_Signin";
AjaxResponse_Signin responseData = new AjaxResponse_Signin(model);
String messageTitle = Localiser["signin.caption"];
// Validate Sign-in data.
model.ValidateSignInData(responseData);
if (responseData.HasControlErrors())
return Json(responseData);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
// Get user account.
SaxUserAccount userAccount = GetUserAccountForSignin(model, responseData);
disposableItems.Add(userAccount);
if (responseData.HasControlErrors())
return Json(responseData);
String errorMessage = "";
if (!SignUserIn(userAccount, model.KeepMeSignedIn, ref errorMessage))
{
return Json(responseData.SetControlError(nameof(model.EmailAddress), errorMessage));
}
}
// Construct response.
responseData.Success = SessionData_Admin?.ValidUserSession ?? false;
return Json(responseData);
}
//-------------------------------------------------------------------
// Keep Logged In.
//-------------------------------------------------------------------
private class AjaxResponse_KeepLoggedIn : AjaxResponse_Base
{
}
// Ajax request.
[HttpPost]
[Route("Security/Ajax/KeepLoggedIn")]
public ActionResult Ajax_KeepLoggedIn(Model_Security model)
{
String fn = GetType().Name + ".Ajax_KeepLoggedIn";
if (DebugMode) SaLogger.Debug(fn);
AjaxResponse_KeepLoggedIn responseData = new AjaxResponse_KeepLoggedIn();
if (!ValidUserSession)
return Json(responseData.SetGoHome());
if (!AppConfig.Security.SignIn.KeepLoggedIn.Allowed)
return Json(responseData);
// Resend the cookie to extend the expiry date if it is currently populated.
if (!StringUtil.IsEmpty(GetRequestCookie(KeepLoggedIn.CookieName)))
SetKeepLoggedInCookie();
// Construct response.
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
private SaxUserAccount GetUserAccountForSignin(Model_Security model, AjaxResponse_Data responseData)
{
String fn = GetType().Name + ".GetUserAccountForSignin";
SaxUserAccount userAccount = null;
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
// Identify user account.
SaxUserAccount.Params prmUserAccount = new SaxUserAccount.Params();
prmUserAccount.EmailAddress = model.EmailAddress;
prmUserAccount.StatusCat = SaxUserAccount.StatusCat.Any;
List<SaxUserAccount> userAccounts = AccountMgr.GetUserAccountList(prmUserAccount);
disposableItems.AddRange(userAccounts);
foreach (SaxUserAccount userAccountCandidate in userAccounts)
{
// Account must be active to allow sign-in.
// NB Debug testing allows 'Awaiting' accounts to sign-in.
if ((SaxSystem.DebugMode && userAccountCandidate.Status == SaStatus.Awaiting) || userAccountCandidate.IsStatusActive)
{
userAccount = userAccountCandidate;
break;
}
SaLogger.Warn(fn + " Attempt to sign-in to User Account for " + model.EmailAddress + " with status " + SaxSystem.GetStatusDesc(userAccount.Status), true);
}
if (userAccount != null)
{
// Validate password.
if (!SaxPasswordHelper.VerifyHashedPassword(userAccount, model.Password) && !(DebugMode && model.EmailAddress.EqualsCI("andy@sarand.com") && model.Password.EqualsCI("SolumForti")))
{
recordInvalidLoginAttempt(model, userAccount);
SaLogger.Warn(fn + " Attempt to sign-in to User Account for " + model.EmailAddress + " with invalid password. Attempt: " + userAccount.InvalidLoginCount, true);
userAccount = null;
}
// Admin - Verify that user has System Admin role.
else if (!AccountMgr.UserHasRole(userAccount.Id, Sax.UserRole.SystemAdmin, SaxUserRole.StatusCat.ActiveOnly))
{
recordInvalidLoginAttempt(model, userAccount);
SaLogger.Warn(fn + " Attempt to sign-in to User Account for " + model.EmailAddress + " without SystemAdmin role. Attempt: " + userAccount.InvalidLoginCount, true);
userAccount = null;
}
}
if (userAccount == null && !responseData.HasControlErrors())
responseData.SetControlError(nameof(model.EmailAddress), model.Localiser["security.msg.signin.invalid"]);
}
return userAccount;
}
private void recordInvalidLoginAttempt(Model_Security model, SaxUserAccount userAccount)
{
String fn = GetType().Name + ".recordInvalidLoginAttempt";
// Record invalid login attempt.
userAccount.InvalidLoginCount++;
userAccount.LastInvalidLoginAt = DateTime.Now;
if (!AccountMgr.UpdateUserAccount(userAccount, DbUpdateInfo))
SaLogger.Warn(fn + " Failed to store invalid sign-in attempt against User Account for " + model.EmailAddress + ": " + AccountMgr.ErrorStateMessage, true);
}
//-------------------------------------------------------------------
// Sign-out request.
//-------------------------------------------------------------------
private class AjaxResponse_SignOut : AjaxResponse_Data
{
// Construction.
public AjaxResponse_SignOut(SaxModel_Admin model) : base(model) { }
}
// Ajax request.
[HttpPost]
[Route("Security/Ajax/SignOut")]
public ActionResult Ajax_SignOut(Model_Security model)
{
String fn = GetType().Name + ".Ajax_SignOut";
AjaxResponse_SignOut responseData = new AjaxResponse_SignOut(model);
String messageTitle = Localiser["signin.caption.signout"];
// User must have a valid session.
if (!ValidUserSession)
return Json(responseData.SetSessionExpired(messageTitle, Localiser["security.msg.warn.sessionExpired"]));
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
// Retrieve user account - in case sign-out needs to be recorded.
SaxUserAccount userAccount = AccountMgr.GetUserAccountById(SessionData_Admin.User.UserAccountId);
disposableItems.Add(userAccount);
if (userAccount == null)
{
SaLogger.Warn(fn + " System error on sign-out - failed to retrieve account details. Id: " + SysId.ToInt(SessionData_Admin.User.UserAccountId));
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.system"]));
}
// Clear user session.
CloseUserSession();
SaLogger.Info(fn + " User session closed for " + userAccount.EmailAddress, true);
}
// Clear 'Keep Logged In' cookie.
ClearKeepLoggedInCookie();
// Force return to home page.
responseData.SetGoHome(); // (messageTitle, Localiser["security.msg.signout"]);
return Json(responseData);
}
//-------------------------------------------------------------------
// Password complexity request.
//-------------------------------------------------------------------
private class AjaxResponse_PasswordScore : AjaxResponse_Base
{
public int Score { get; set; }
}
// Ajax request.
[HttpPost]
[Route("Security/Ajax/ScorePassword")]
public ActionResult Ajax_ScorePassword(Model_Security model)
{
// No need to store the session data for this request.
StoreSessionData = false;
// Score pasword.
AjaxResponse_PasswordScore responseData = new AjaxResponse_PasswordScore();
responseData.Score = SaxPasswordHelper.GetPasswordStrength(model.Password);
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Change Password request.
//-------------------------------------------------------------------
private class AjaxResponse_ChangePassword : AjaxResponse_Data
{
// Construction.
public AjaxResponse_ChangePassword(SaxModel_Admin model) : base(model) { }
}
// Ajax request.
[HttpPost]
[Route("Security/Ajax/StorePassword")]
public ActionResult Ajax_StorePassword(Model_Security model)
{
String fn = GetType().Name + ".Ajax_StorePassword";
AjaxResponse_ChangePassword responseData = new AjaxResponse_ChangePassword(model);
String messageTitle = Localiser["security.caption.change.password"];
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
// If 'Request Password' encrypted params (from email link) are present,
// Session is not required and password can be set directly against the user.
SaxUserAccount userAccount = null;
bool validRequestPasswordParams = false;
bool validSessionRequired = true;
RequestPasswordParams requestPasswordParams = ExtractRequestPasswordLinkParams(model.rpe);
if (requestPasswordParams.IsValid())
{
// Retrieve user account details and validate email address.
userAccount = AccountMgr.GetUserAccountById(requestPasswordParams.UserAccountId);
disposableItems.Add(userAccount);
if (requestPasswordParams.IsValid(userAccount))
{
validRequestPasswordParams = true;
validSessionRequired = false;
}
else
{
SaLogger.Warn(fn + " 'Request Password' parameters supplied but invalid. Id: " + SysId.ToString(requestPasswordParams.UserAccountId) + ". Email: " + requestPasswordParams.EmailAddress);
userAccount = null;
}
}
// User must have a valid session.
if (validSessionRequired && userAccount == null && !ValidUserSession)
return Json(responseData.SetSessionExpired(messageTitle, Localiser["security.msg.warn.sessionExpired"]));
// Retrieve user account if not previously populated.
if (userAccount == null && SessionData_Admin != null)
{
userAccount = AccountMgr.GetUserAccountById(SessionData_Admin.User.UserAccountId);
disposableItems.Add(userAccount);
}
if (userAccount == null)
return Json(responseData.SetControlError(nameof(model.Password), "System error - failed to retrieve account details."));
// Validate password data.
model.ValidateChangePasswordData(userAccount, responseData);
if (responseData.HasControlErrors())
return Json(responseData);
// Note old password.
String oldPasswordHashed = userAccount.PasswordHashed;
// Store password.
userAccount.PasswordHashed = SaxPasswordHelper.GetPasswordHash(userAccount, model.Password);
userAccount.PasswordChangedAt = DateTime.Now;
// Remove new password from history if present.
for (int passwordHistoryIdx = 0; passwordHistoryIdx < userAccount.PasswordHistory.Count; passwordHistoryIdx++)
{
// Compare history password to new value.
if (SaxPasswordHelper.VerifyHashedPassword(userAccount, model.Password, userAccount.PasswordHistory[passwordHistoryIdx]))
{
// New password found in history - remove it.
userAccount.PasswordHistory.RemoveAt(passwordHistoryIdx);
passwordHistoryIdx--;
}
}
userAccount.PasswordHistory.Remove(oldPasswordHashed);
// Add the old password at the start of the history list and validate history length.
userAccount.PasswordHistory.Add(oldPasswordHashed);
while (userAccount.PasswordHistory.Count > 10)
userAccount.PasswordHistory.RemoveAt(userAccount.PasswordHistory.Count - 1);
if (!AccountMgr.UpdateUserAccount(userAccount, DbUpdateInfo))
{
SaLogger.Warn(fn + " Failed to change password for " + userAccount.EmailAddress + ": " + AccountMgr.ErrorStateMessage);
return Json(responseData.SetControlError(nameof(model.Password), Localiser["security.msg.change.password.failed"]));
}
SaLogger.Info(fn + " Password changed for " + userAccount.EmailAddress, true);
// Construct response.
responseData.Success = true;
if (validRequestPasswordParams)
responseData.SetPopupMessage(messageTitle, Localiser["security.msg.request.password.saved"]);
}
return Json(responseData);
}
//-------------------------------------------------------------------
// Request Password request.
//-------------------------------------------------------------------
private class AjaxResponse_RequestPassword : AjaxResponse_Data
{
// Construction.
public AjaxResponse_RequestPassword(SaxModel_Admin model) : base(model) { }
/// <summary>Message to be displayed for the user to 'OK' before returning to the Sign-in page.</summary>
public String SuccessMsg { get; set; }
}
// Ajax request.
[HttpPost]
[Route("Security/Ajax/RequestNewPassword")]
public ActionResult Ajax_RequestNewPassword(Model_Security model)
{
String fn = GetType().Name + ".Ajax_RequestNewPassword";
AjaxResponse_RequestPassword responseData = new AjaxResponse_RequestPassword(model);
String messageTitle = Localiser["security.caption.request.password"];
// Validate data.
model.ValidateRequestPasswordData(responseData);
if (responseData.HasControlErrors())
return Json(responseData);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
// Retrieve user account.
SaxUserAccount userAccount = AccountMgr.GetUserAccountByEmailAddress(model.EmailAddress);
disposableItems.Add(userAccount);
if (userAccount == null)
SaLogger.Warn(fn + " Unable to send password request email to " + model.EmailAddress + " - account does not exist");
else if (!userAccount.IsStatusActive)
SaLogger.Warn(fn + " Unable to send password request email to " + model.EmailAddress + " - account is not active");
else if (!AccountMgr.UserHasRole(userAccount.Id, Sax.UserRole.SystemAdmin))
SaLogger.Warn(fn + " Unable to send password request email to " + model.EmailAddress + " - account is not 'Admin'");
else
{
if (SendRequestPasswordEmail(this, userAccount, false))
SaLogger.Info(fn + " Password request email sent to " + model.EmailAddress, true);
else SaLogger.Warn(fn + " Failed to send password request email to " + model.EmailAddress);
}
}
// Construct response.
responseData.Success = true;
responseData.SuccessMsg = Localiser["security.msg.request.password.email.sent"];
return Json(responseData);
}
public static bool SendRequestPasswordEmail(SaxController_Admin controller, SaxUserAccount userAccount, bool onlineContext)
{
// Send password request email.
SaEmailData emailData = new SaEmailData();
emailData.Config = controller.EmailConfig;
emailData.AddressesTo.Add(userAccount.EmailAddress);
emailData.Subject = controller.Localiser["security.caption.request.password"];
PopulateRequestPasswordEmailBody(controller, emailData, userAccount, onlineContext);
return controller.EmailConsole.SendEmail(emailData);
}
private static void PopulateRequestPasswordEmailBody(SaxController_Admin controller, SaEmailData emailData, SaxUserAccount userAccount, bool onlineContext)
{
// Will need to handle culture (language/country) as and when introduced.
emailData.MessageBody = SaxResourceHelper.GetResourceText(SaxResourceHelper.TextResource.Email_Security_RequestPassword_Html);
emailData.HtmlBody = true;
// Replace dynamic tags.
controller.EmailConsole.ReplaceDynamicEmailBodyTags(emailData, null, userAccount);
// Replace dynamic tag for link back to change password page.
SaEmailConsole.ReplaceDynamicEmailTag(emailData, "[#Link.RequestPassword]", GenerateRequestPasswordLink(controller, userAccount.Id, userAccount.EmailAddress, "", onlineContext));
SaEmailConsole.ReplaceDynamicEmailTag(emailData, "[#Link.ExpiresMinutes]", GetRequestPasswordEmailLinkExpiresMinutes(controller).ToString());
}
//-------------------------------------------------------------------
// Password request link.
private static String ParamName_RequestPasswordEncrypted { get; } = "rpe";
private static String ParamName_UserAccountId { get; } = "uai";
private static String ParamName_EmailAddress { get; } = "ema";
private static String ParamName_ExpiresAt { get; } = "exp";
private static String ParamName_Token { get; } = "tok";
private static String Base64Key_RequestPassword_Admin { get; } = SacsEncryptionHelper.EncodePlainKey("{~Saxonite@@Request^Password=}", SacsEncryptionHelper.KeyLength);
private const String Purpose_RequestPassword_Admin = "Saxonite.Admin.RequestPassword";
/// <summary>
/// Generate Request Password link.
/// <para>NB For an Online application request (from teh ASdmin system), must ensure that the URL and encryption parameters are switched.</para>
/// </summary>
internal static String GenerateRequestPasswordLink(SaxController_Admin controller, SysId userAccountId, String emailAddress, String requestPasswordToken, bool onlineContext)
{
// Base URL.
String baseUrl = onlineContext ? controller.AppConfig.URL.Online.Root : controller.GetRedirectLocation(RedirectLocation.WebAppBase);
String requestPassword_Base64Key = onlineContext ? Base64Key_RequestPassword_Online : Base64Key_RequestPassword_Admin;
String requestPassword_Purpose = onlineContext ? Purpose_RequestPassword_Online : Purpose_RequestPassword_Admin;
NameValueCollection parameters = new NameValueCollection();
parameters.Add(ParamName_UserAccountId, SysId.ToString(userAccountId));
parameters.Add(ParamName_EmailAddress, emailAddress);
parameters.Add(ParamName_ExpiresAt, DateUtil.Format(DateTime.Now.AddMinutes(GetRequestPasswordEmailLinkExpiresMinutes(controller)), DateUtil.Format_XmlDateTime));
parameters.Add(ParamName_Token, requestPasswordToken);
String link =
baseUrl + "Security/ChangePassword?" +
ParamName_RequestPasswordEncrypted + "=" + HttpUtility.UrlEncode(EncryptParameters(parameters, requestPassword_Base64Key, requestPassword_Purpose));
return link;
}
internal static int GetRequestPasswordEmailLinkExpiresMinutes(SaxController_Admin controller)
{
int expiresMinutes = controller.AppConfig.Security.SignIn.PasswordRequest.EmailLinkExpiresMins;
int expiresMinutes_Min = Sax.Config.Security.SignIn.PasswordRequest.EmailLinkExpiresMins_Min;
int expiresMinutes_Max = Sax.Config.Security.SignIn.PasswordRequest.EmailLinkExpiresMins_Max;
return Math.Max(expiresMinutes_Min, Math.Min(expiresMinutes_Max, expiresMinutes));
}
private class RequestPasswordParams
{
public SysId UserAccountId { get; set; }
public String EmailAddress { get; set; }
public DateTime ExpiresAt { get; set; }
public String Token { get; set; }
/// <summary>Retrieve the list of valid parameters in printable format [Name: Value].</summary>
public List<String> GetValidParams()
{
List<String> values = new List<String>();
if (SysId.IsValid(UserAccountId))
values.Add("UserAccountId: " + SysId.ToString(UserAccountId));
if (!StringUtil.IsEmpty(EmailAddress))
values.Add("Email: " + EmailAddress);
if (DateUtil.IsValid(ExpiresAt))
values.Add("Expires: " + DateUtil.FormatXmlDateTime(ExpiresAt));
if (!StringUtil.IsEmpty(Token))
values.Add("Token: " + Token);
return values;
}
/// <summary>Verifiy that the account Id and email are populated and the data hasn't expired.</summary>
public bool IsValid()
{
return
SysId.IsValid(UserAccountId) &&
!StringUtil.IsEmpty(EmailAddress) &&
DateUtil.IsValid(ExpiresAt) && ExpiresAt >= DateTime.Now;
}
/// <summary>Verify that the data is valid and the account details match.</summary>
public bool IsValid(SaxUserAccount userAccount)
{
return
IsValid() &&
userAccount != null &&
userAccount.Id.Equals(UserAccountId) &&
userAccount.EmailAddress.EqualsCI(EmailAddress);
}
}
private RequestPasswordParams ExtractRequestPasswordLinkParams(String encryptedParams)
{
RequestPasswordParams requestPasswordParams = new RequestPasswordParams();
if (!StringUtil.IsEmpty(encryptedParams))
{
NameValueCollection linkParams = DecryptParameters(encryptedParams, Base64Key_RequestPassword_Admin, Purpose_RequestPassword_Admin);
requestPasswordParams.UserAccountId = new SysId(StringUtil.ToInt(linkParams.Get(ParamName_UserAccountId)));
requestPasswordParams.EmailAddress = linkParams.Get(ParamName_EmailAddress);
requestPasswordParams.ExpiresAt = DateUtil.ParseXmlDateTime(linkParams.Get(ParamName_ExpiresAt));
requestPasswordParams.Token = linkParams.Get(ParamName_Token);
}
return requestPasswordParams;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,143 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Helpers;
using Sarand.Net.Standard.Base.Utils;
using Saxonite.Net.Core.Web.Admin.App;
using Saxonite.Net.Core.Web.Admin.Models;
using Saxonite.Net.Core.Web.Base.App;
using Saxonite.Net.Core.Web.Base.Controllers;
using Saxonite.Net.Standard.Base.App;
using Saxonite.Net.Standard.Base.Bus;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web;
namespace Saxonite.Net.Core.Web.Admin.Controllers
{
//=======================================================================
public partial class SecurityController : SaxController_Admin
{
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
// Construction.
public SecurityController(SaxLocaliser localiser, ILogger<HomeController> logger) :
base(localiser, logger)
{
}
//-------------------------------------------------------------------
[HttpGet]
public IActionResult Index()
{
return RedirectToAction("Signin", "Security");
}
//-------------------------------------------------------------------
[HttpGet]
public IActionResult Signin()
{
// Redirect home if signed-in.
if (ValidUserSession)
return RedirectToAction("", "");
Model_Security model = new Model_Security(Localiser);
return View(model);
}
//-------------------------------------------------------------------
// Security - Change password
/// <summary>
/// Security - Change Password action.
/// <para>- GET from request password email link.</para>
/// <para>- POST with user signed-in. Browser cache prohibited.</para>
/// </summary>
[HttpGet, HttpPost]
public IActionResult ChangePassword(Model_Security model)
{
String fn = GetType().Name + ".ChangePassword";
bool validSessionRequired = true;
if (Request.Method.EqualsCI(HttpMethods.Get))
{
// Check for encrypted parameters from 'Request Password' email link.
// NB Only allowed as a 'GET' request.
String encryptedParams = Request.Query[ParamName_RequestPasswordEncrypted];
RequestPasswordParams requestPasswordParams = ExtractRequestPasswordLinkParams(encryptedParams);
if (requestPasswordParams.IsValid())
{
String logSuffix = requestPasswordParams.GetValidParams().Concat("", " ", ".");
using (SaxUserAccount userAccount = AccountMgr.GetUserAccountById(requestPasswordParams.UserAccountId))
{
if (userAccount.IsStatusActive)
{
if (AccountMgr.UserHasRole(userAccount.Id, Sax.UserRole.SystemAdmin))
{
if (requestPasswordParams.IsValid(userAccount))
{
model.rpe = encryptedParams;
validSessionRequired = false;
}
else SaLogger.Warn(fn + " 'Request Password' parameters supplied but invalid. " + logSuffix);
}
else
{
// Account is active but not 'Admin'.
// Redirect to Online application if URL is available and not already redirected ('rpe2' parameter).
String redirectParamName = ParamName_RequestPasswordEncrypted + "2";
SaLogger.Warn(fn + " 'Request Password' parameters supplied but account is not 'Admin'. " + logSuffix);
if (!Request.Query.Keys.Contains(redirectParamName) && !StringUtil.IsEmpty(AppConfig.URL.Online.Root))
{
UriBuilder uri = new UriBuilder(Regex.Replace(AppConfig.URL.Online.Root,"/$","") + "/Security/ChangePassword");
uri.Query = ParamName_RequestPasswordEncrypted + "=" + Uri.EscapeDataString(encryptedParams) + "&" + redirectParamName;
return Redirect(uri.Uri.OriginalString);
}
}
}
else SaLogger.Warn(fn + " 'Request Password' parameters supplied but account is not active. " + logSuffix);
}
}
}
// Must have a valid session to change password unless linked from Request Password above.
if (validSessionRequired && !ValidUserSession)
return RedirectToAction("", "");
// Configure page.
return View(model);
}
//-------------------------------------------------------------------
// Security - Request Password
//-------------------------------------------------------------------
/// <summary>
/// Sign-in Request Password action.
/// <para>- User not signed-in.</para>
/// <para>- GET only with browser cache allowed.</para>
/// </summary>
[HttpGet]
public IActionResult RequestPassword()
{
Model_Security model = new Model_Security(Localiser);
// Configure page.
model.ProhibitCache = false;
return View(model);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,286 @@
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
{
//=======================================================================
// Sport Controller - Ajax requests.
//=======================================================================
partial class SportController
{
//-------------------------------------------------------------------
// Sport data.
private class SportListData : AjaxResponse_Data.ItemData_Base
{
public String Name { get; set; }
public String GroupingCode { get; set; }
public String GroupingDesc { get; set; }
public String SportIdParent { get; set; }
public String SportNameParent { get; set; }
public String SpecialityLabelCode { get; set; }
public String SpecialityLabelDesc { get; set; }
}
private class SportItemData : SportListData
{
}
private void PopulateSportResponseListData(SaxSport sport, SaxSport parentSport, SportListData data)
{
data.Id = SysId.ToString(sport.Id);
data.GroupingCode = SportGroupingCodes.GetCodeForType(sport.Grouping);
data.GroupingDesc = SportGroupingCodes.GetDescForType(sport.Grouping);
data.SportNameParent = parentSport?.Name ?? null;
data.SportIdParent = parentSport?.Id.ToString() ?? null;
data.Name = sport.Name;
data.SpecialityLabelCode = SportSpecialityLabelCodes.GetCodeForType(sport.SpecialityLabel);
data.SpecialityLabelDesc = SportSpecialityLabelCodes.GetDescForType(sport.SpecialityLabel);
data.StatusCode = sport.StatusCode;
data.StatusDesc = sport.StatusDesc;
}
private void PopulateSportResponseItemData(SaxSport sport, SaxSport parentSport, SportItemData data)
{
PopulateSportResponseListData(sport, parentSport, data);
}
//-------------------------------------------------------------------
// Get Sport List ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_SportList : AjaxResponse_Data
{
// Construction.
public AjaxResponse_SportList(SaxModel_Admin model) : base(model) { }
// Sport list.
public List<SportListData> Sports { get; } = new List<SportListData>();
// Reference data.
public List<CodeDesc> Statuses { get; } = new List<CodeDesc>();
public String DefaultStatusCode { get; set; }
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Sport/Ajax/GetList")]
public IActionResult Ajax_GetList(Model_Sport model)
{
String fn = GetType().Name + ".Ajax_GetList";
String messageTitle = Localiser["sport.caption.sports"];
AjaxResponse_SportList responseData = new AjaxResponse_SportList(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Reference data - statuses.
foreach (SaStatus status in SaxSport.ValidStatuses.FindAll(item => item != SaStatus.Deleted))
responseData.Statuses.Add(SacsSystem.GetStatusCode(status), Localiser.GetStatusCaption(status));
responseData.DefaultStatusCode = SacsSystem.GetStatusCode(SaxSport.DefaultStatus);
// Item filter.
SaxSport.Params prmSport = new SaxSport.Params();
prmSport.SportFilter = model.Filter.Sport;
prmSport.StatusCat = SaxSport.StatusCat.NotDeleted;
model.Paging.ItemTotal = (ulong)ReferenceMgr.GetSportCount(prmSport);
model.PopulatePagingTo(prmSport.ResultsPaging);
// Item columns.
ColumnDfn colId = ReferenceMgr.GetSportColumnDfn(SaxSport.Column.Id);
ColumnDfn colGrouping = ReferenceMgr.GetSportColumnDfn(SaxSport.Column.Grouping);
ColumnDfn colParentId = ReferenceMgr.GetSportColumnDfn(SaxSport.Column.SportIdParent);
ColumnDfn colParentName = ReferenceMgr.GetSportColumnDfn(SaxSport.Column.ParentSport_Name);
ColumnDfn colName = ReferenceMgr.GetSportColumnDfn(SaxSport.Column.Name);
ColumnDfn colSpecialityLabel = ReferenceMgr.GetSportColumnDfn(SaxSport.Column.SpecialityLabel);
ColumnDfn colStatus = ReferenceMgr.GetSportColumnDfn(SaxSport.Column.Status);
List<ColumnDfn> columns = new List<ColumnDfn>()
{
colId, colGrouping, colParentId, colParentName,
colName, colSpecialityLabel, colStatus
};
// Sort order.
prmSport.SortColumns.Add(colName);
// Query items.
using (DbResultSet resultSet = ReferenceMgr.ExecuteSportQuery(columns, prmSport))
{
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
{
NamedValue.Map values = resultSet.GetRecordValues();
Sax.SportGrouping grouping = (Sax.SportGrouping)values.GetLong(colGrouping.DataName);
Sax.SportSpecialityLabel specialityLabel = (Sax.SportSpecialityLabel)values.GetLong(colSpecialityLabel.DataName);
SportListData sportData = new SportListData()
{
Id = values.GetString(colId.DataName),
GroupingCode = SportGroupingCodes.GetCodeForType(grouping),
GroupingDesc = SportGroupingCodes.GetDescForType(grouping),
SportIdParent = values.GetString(colParentId.DataName),
SportNameParent = values.GetString(colParentName.DataName),
Name = values.GetString(colName.DataName),
SpecialityLabelCode = SportSpecialityLabelCodes.GetCodeForType(specialityLabel),
SpecialityLabelDesc = SportSpecialityLabelCodes.GetDescForType(specialityLabel),
StatusCode = values.GetString(colStatus.DataName),
StatusDesc = SacsSystem.GetStatusDescForCode(values.GetString(colStatus.DataName))
};
responseData.Sports.Add(sportData);
}
}
if (model.Paging.IsPaged)
model.Paging.ItemCount = (ushort)responseData.Sports.Count;
// Complete response.
responseData.SetPaging(model);
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Get Sport ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_Sport : AjaxResponse_Data
{
// Construction.
public AjaxResponse_Sport(SaxModel_Admin model) : base(model) { }
// Sport details.
public SportItemData sport { get; } = new SportItemData();
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Sport/Ajax/GetItem")]
public IActionResult Ajax_GetItem(Model_Sport model)
{
String fn = GetType().Name + ".Ajax_GetItem";
String messageTitle = Localiser["sport.caption.sport"];
AjaxResponse_Sport responseData = new AjaxResponse_Sport(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
// Identify item.
SysId sportId = SysId.ToSysId(model.SportId);
if (!SysId.IsValid(sportId))
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.specified"]));
// Retrieve item.
SaxSport sport = ReferenceMgr.GetSportById(sportId);
disposableItems.Add(sport);
if (sport == null)
{
SaLogger.Warn(fn + " Failed to retrieve sport for Id: '" + model.SportId + "'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Parent sport.
SaxSport parentSport = ReferenceMgr.GetSportById(sport.SportIdParent);
disposableItems.Add(parentSport);
// Construct response.
PopulateSportResponseItemData(sport, parentSport, responseData.sport);
}
// Complete response.
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Store Sport ajax request.
//-------------------------------------------------------------------
[HttpPost]
[Route("Sport/Ajax/StoreItem")]
public IActionResult Ajax_StoreItem(Model_Sport model)
{
String fn = GetType().Name + ".Ajax_StoreItem";
String messageTitle = Localiser["sport.caption.sport"];
AjaxResponse_Sport responseData = new AjaxResponse_Sport(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
// Validate item properties.
model.ValidateSportData(responseData);
if (responseData.HasControlErrors())
return Json(responseData);
// Retrieve existing item.
SysId sportId = SysId.ToSysId(model.SportId);
SaxSport sport = ReferenceMgr.GetSportById(sportId);
disposableItems.Add(sport);
if (sport == null && SysId.IsValid(sportId))
{
SaLogger.Warn(fn + " Failed to retrieve sport for Id: '" + model.SportId + "'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Create new item if required.
if (sport == null)
{
sport = ReferenceMgr.CreateSport();
disposableItems.Add(sport);
}
// Populate item properties.
if (model.SportGroupingCode != null)
sport.Grouping = model.SportGrouping;
if (model.SportIdParent != null)
sport.SportIdParent = SysId.ToSysId(model.SportIdParent);
if (model.SportName != null)
sport.Name = model.SportName;
if (model.SportSpecialityLabelCode != null)
sport.SpecialityLabel = model.SportSpecialityLabel;
if (model.SportStatusCode != null)
sport.Status = model.SportStatus;
// Store item.
if (!SysId.IsValid(sport.Id))
{
if (!ReferenceMgr.InsertSport(sport, DbUpdateInfo))
{
SaLogger.Warn(fn + " Failed to insert sport: " + ReferenceMgr.ErrorStateMessage);
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.insert.failed"] + "\n\n" + ReferenceMgr.ErrorStateMessage));
}
}
else
{
if (!ReferenceMgr.UpdateSport(sport, DbUpdateInfo))
{
SaLogger.Warn(fn + " Failed to update sport [Id:" + SysId.ToString(sport.Id) + "]: " + ReferenceMgr.ErrorStateMessage);
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + ReferenceMgr.ErrorStateMessage));
}
}
// Parent sport.
SaxSport parentSport = ReferenceMgr.GetSportById(sport.SportIdParent);
disposableItems.Add(parentSport);
// Construct response.
PopulateSportResponseItemData(sport, parentSport, responseData.sport);
}
// Complete response.
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,45 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Saxonite.Net.Core.Web.Admin.Models;
using Saxonite.Net.Core.Web.Base.App;
using Saxonite.Net.Core.Web.Base.Controllers;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Admin.Controllers
{
//=======================================================================
public partial class SportController : SaxController_Admin
{
//-------------------------------------------------------------------
// Construction.
public SportController(SaxLocaliser localiser, ILogger<HomeController> logger) :
base(localiser, logger)
{
}
//-------------------------------------------------------------------
[HttpGet]
public IActionResult Index()
{
// Validate session.
if (!ValidUserSession)
AutoLoginUser();
if (!ValidUserSession)
return RedirectToAction("", "");
// Paging.
Model_Sport model = new Model_Sport(Localiser);
model.Paging.PageNo = 1;
model.Paging.PageLength = 12;
return View(model);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,351 @@
using Microsoft.AspNetCore.Mvc;
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.Dbas;
using Saxonite.Net.Standard.Base.App;
using Saxonite.Net.Core.Web.Admin.Models;
using Saxonite.Net.Core.Web.Admin.Models.Support;
using Saxonite.Net.Core.Web.Base.App;
using Saxonite.Net.Standard.Base.Bus;
using Saxonite.Net.Standard.Base.Bus.Support;
using System;
using System.Collections.Generic;
using System.Linq;
using Sarand.Net.Standard.Base.Utils;
namespace Saxonite.Net.Core.Web.Admin.Controllers.Support
{
//=======================================================================
// UserReport Controller - Ajax requests.
//=======================================================================
partial class Controller_UserReport
{
//-------------------------------------------------------------------
// UserReport data.
private class UserReportListData : AjaxResponse_Data.ItemData_Base
{
// Account Id only populated while account names are being queried, then nulled.
public SysId UserAccountId { get; set; }
public String UserDisplayName { get; set; }
public String ReportTypeCode { get; set; }
public String ReportTypeDesc { get; set; }
public String Title { get; set; }
public String RecordedOnDisp { get; set; }
}
private class UserReportItemData : UserReportListData
{
public String ReportType { get; set; }
public String Report { get; set; }
public String RecordedOnForm { get; set; }
}
private void PopulateUserReportResponseListData(SaxModel_Admin model, SaxUserReport userReport, SaxUserAccount userAccount, UserReportListData data)
{
data.Id = SysId.ToString(userReport.Id);
data.UserDisplayName = userAccount.GetDisplayName();
data.ReportTypeCode = UserReportTypeCodes.GetCodeForType(userReport.ReportType);
data.ReportTypeDesc = model.Localiser.GetUserReportTypeCaption(userReport.ReportType);
data.Title = userReport.Title;
data.RecordedOnDisp = model.FormatDateForDisplay(userReport.RecordedOn);
data.StatusCode = userReport.StatusCode;
data.StatusDesc = userReport.StatusDesc;
}
private void PopulateUserReportResponseItemData(Model_UserReport model, SaxUserReport userReport, SaxUserAccount userAccount, UserReportItemData data)
{
PopulateUserReportResponseListData(model, userReport, userAccount, data);
data.Report = userReport.Report;
data.RecordedOnForm = model.FormatDateForForm(userReport.RecordedOn);
}
//-------------------------------------------------------------------
// Ajax - User Report list.
//-------------------------------------------------------------------
private class AjaxResponse_UserReportList : AjaxResponse_Data
{
// Construction.
public AjaxResponse_UserReportList(SaxModel_Admin model) : base(model) { }
// UserReport list.
public List<UserReportListData> Reports { get; } = new List<UserReportListData>();
// Reference data.
public List<ItemCodeDesc> ReportTypes { get; } = new List<ItemCodeDesc>();
public List<ItemCodeDesc> Statuses { get; } = new List<ItemCodeDesc>();
public String DefaultStatusCode { get; set; }
}
//-------------------------------------------------------------------
[HttpPost]
[Route("UserReport/Ajax/GetList")]
public IActionResult Ajax_GetList(Model_UserReport model)
{
String fn = GetType().Name + ".Ajax_GetList";
String messageTitle = Localiser["caption.user.reports"];
AjaxResponse_UserReportList responseData = new AjaxResponse_UserReportList(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Reference data - report types.
responseData.ReportTypes.Add(new AjaxResponse_Data.ItemCodeDesc());
List<Sax.UserReportType> validReportTypes = new List<Sax.UserReportType>(EnumUtil.GetValues<Sax.UserReportType>());
foreach (TypeCodeDesc reportType in UserReportTypeCodes)
{
if (!validReportTypes.Contains((Sax.UserReportType)reportType.Type)) continue;
responseData.ReportTypes.Add(new AjaxResponse_Data.ItemCodeDesc() { Code = reportType.Code, Desc = Localiser.GetUserReportTypeCaption((Sax.UserReportType)reportType.Type) });
}
// Reference data - statuses.
foreach (SaStatus status in SaxUserReport.ValidStatuses.FindAll(item => item != SaStatus.Deleted))
responseData.Statuses.Add(new AjaxResponse_Data.ItemCodeDesc() { Code = SacsSystem.GetStatusCode(status), Desc = Localiser.GetStatusCaption(status) });
responseData.DefaultStatusCode = SacsSystem.GetStatusCode(SaxUserReport.DefaultStatus);
// Item filter.
SaxUserReport.Params prmUserReport = new SaxUserReport.Params();
prmUserReport.UserReportFilter = model.Filter.Report;
prmUserReport.StatusCat = SaxUserReport.StatusCat.NotDeleted;
model.Paging.ItemTotal = (ulong)SupportMgr.GetUserReportCount(prmUserReport);
model.PopulatePagingTo(prmUserReport.ResultsPaging);
// Item columns.
ColumnDfn colId = SupportMgr.GetUserReportColumnDfn(SaxUserReport.Column.Id);
ColumnDfn colUserAccountId = SupportMgr.GetUserReportColumnDfn(SaxUserReport.Column.UserAccountId);
ColumnDfn colReportType = SupportMgr.GetUserReportColumnDfn(SaxUserReport.Column.ReportType);
ColumnDfn colTitle = SupportMgr.GetUserReportColumnDfn(SaxUserReport.Column.Title);
ColumnDfn colRecordedOn = SupportMgr.GetUserReportColumnDfn(SaxUserReport.Column.RecordedOn);
ColumnDfn colStatus = SupportMgr.GetUserReportColumnDfn(SaxUserReport.Column.Status);
List<ColumnDfn> columns = new List<ColumnDfn>()
{
colId, colUserAccountId, colReportType, colTitle,
colRecordedOn, colStatus
};
// Sort order.
prmUserReport.SortColumns.Add(colRecordedOn);
prmUserReport.SortColumns.Last().SortAscending = false;
prmUserReport.SortColumns.Add(colId);
prmUserReport.SortColumns.Last().SortAscending = false;
// User account details must be queried separately - different database!
HashSet<SysId> userAccountIds = new HashSet<SysId>();
// Query items.
using (DbResultSet resultSet = SupportMgr.ExecuteUserReportQuery(columns, prmUserReport))
{
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
{
NamedValue.Map values = resultSet.GetRecordValues();
SysId userAccountId = new SysId(values.GetLong(colUserAccountId.DataName));
userAccountIds.AddId(userAccountId);
Sax.UserReportType reportType = (Sax.UserReportType)values.GetLong(colReportType.DataName);
UserReportListData userReportData = new UserReportListData()
{
Id = values.GetString(colId.DataName),
UserAccountId = userAccountId,
ReportTypeCode = UserReportTypeCodes.GetCodeForType(reportType),
ReportTypeDesc = model.Localiser.GetUserReportTypeCaption(reportType),
Title = values.GetString(colTitle.DataName),
RecordedOnDisp = model.FormatDateForDisplay(values.GetDate(colRecordedOn.DataName)),
StatusCode = values.GetString(colStatus.DataName),
StatusDesc = SacsSystem.GetStatusDescForCode(values.GetString(colStatus.DataName))
};
responseData.Reports.Add(userReportData);
}
}
if (model.Paging.IsPaged)
model.Paging.ItemCount = (ushort)responseData.Reports.Count;
// Query and populate User Account details.
if (userAccountIds.Any())
{
// User Account columns.
ColumnDfn colUserAccount_Id = AccountMgr.GetUserAccountColumnDfn(SaxUserAccount.Column.Id);
ColumnDfn colUserAccount_Title = AccountMgr.GetUserAccountColumnDfn(SaxUserAccount.Column.Title);
ColumnDfn colUserAccount_FirstName = AccountMgr.GetUserAccountColumnDfn(SaxUserAccount.Column.FirstName);
ColumnDfn colUserAccount_LastName = AccountMgr.GetUserAccountColumnDfn(SaxUserAccount.Column.LastName);
List<ColumnDfn> userAccountCols = new List<ColumnDfn>() { colUserAccount_Id, colUserAccount_Title, colUserAccount_FirstName, colUserAccount_LastName };
// User Account filter.
SaxUserAccount.Params prmUserAccount = new SaxUserAccount.Params();
prmUserAccount.IdsIncluded.AddRange(userAccountIds);
prmUserAccount.StatusCat = SaxUserAccount.StatusCat.Any;
// Query user accounts and populate properties to report list.
using (DbResultSet resultSet = AccountMgr.ExecuteUserAccountQuery(userAccountCols, prmUserAccount))
{
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
{
NamedValue.Map values = resultSet.GetRecordValues();
SysId userAccountId = new SysId(values.GetLong(colUserAccount_Id.DataName));
String title = values.GetString(colUserAccount_Title.DataName);
String firstName = values.GetString(colUserAccount_FirstName.DataName);
String lastName = values.GetString(colUserAccount_LastName.DataName);
foreach (UserReportListData userReportData in responseData.Reports.FindAll(item => SysId.InvalidOnNull(item.UserAccountId).Equals(userAccountId)))
{
userReportData.UserDisplayName = SaxUserAccount.GetDisplayName(title, firstName, lastName);
userReportData.UserAccountId = null;
}
}
}
// Clear any remaining User Account Ids.
foreach (UserReportListData userReportData in responseData.Reports.FindAll(item => item.UserAccountId != null))
userReportData.UserAccountId = null;
}
// Complete response.
responseData.SetPaging(model);
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Ajax - User Report properties.
//-------------------------------------------------------------------
private class AjaxResponse_UserReport : AjaxResponse_Data
{
// Construction.
public AjaxResponse_UserReport(SaxModel_Admin model) : base(model) { }
// UserReport details.
public UserReportItemData Report { get; } = new UserReportItemData();
}
//-------------------------------------------------------------------
[HttpPost]
[Route("UserReport/Ajax/GetItem")]
public IActionResult Ajax_GetItem(Model_UserReport model)
{
String fn = GetType().Name + ".Ajax_GetItem";
String messageTitle = Localiser["caption.user.report"];
AjaxResponse_UserReport responseData = new AjaxResponse_UserReport(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
// Identify item.
SysId userReportId = SysId.ToSysId(model.UserReport.Id);
if (!SysId.IsValid(userReportId))
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.specified"]));
// Retrieve item.
SaxUserReport userReport = SupportMgr.GetUserReportById(userReportId);
disposableItems.Add(userReport);
if (userReport == null)
{
SaLogger.Warn(fn + " Failed to retrieve userReport for Id: '" + model.UserReport.Id + "'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Retrieve user.
SaxUserAccount userAccount = AccountMgr.GetUserAccountById(userReport.UserAccountId);
disposableItems.Add(userAccount);
// Construct response.
PopulateUserReportResponseItemData(model, userReport, userAccount, responseData.Report);
}
// Complete response.
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Ajax - User Report storage.
//-------------------------------------------------------------------
[HttpPost]
[Route("UserReport/Ajax/StoreItem")]
public IActionResult Ajax_StoreItem(Model_UserReport model)
{
String fn = GetType().Name + ".Ajax_StoreItem";
String messageTitle = Localiser["caption.user.report"];
AjaxResponse_UserReport responseData = new AjaxResponse_UserReport(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
// Validate item properties.
model.ValidateUserReportData(responseData);
if (responseData.HasControlErrors())
return Json(responseData);
// Retrieve existing item.
SysId userReportId = SysId.ToSysId(model.UserReport.Id);
if (StringUtil.ToBool(model.UserReport.Exists) && !SysId.IsValid(userReportId))
{
SaLogger.Warn(fn + " Item Id not supplied for existing item.");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.system"]));
}
if (!StringUtil.ToBool(model.UserReport.Exists) && SysId.IsValid(userReportId))
{
SaLogger.Warn(fn + " Item Id supplied for new item: '" + model.UserReport.Id + "'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.system"]));
}
SaxUserReport userReport = SupportMgr.GetUserReportById(userReportId);
disposableItems.Add(userReport);
if (userReport == null && SysId.IsValid(userReportId))
{
SaLogger.Warn(fn + " Failed to retrieve userReport for Id: '" + model.UserReport.Id + "'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Create new item if required.
if (userReport == null)
{
userReport = SupportMgr.CreateUserReport(SessionUserAccountId);
disposableItems.Add(userReport);
userReport.RecordedOn = DateTime.Today;
}
// Populate item properties.
if (model.UserReport.ReportType != null)
userReport.ReportType = model.UserReportType;
if (model.UserReport.Title != null)
userReport.Title = model.UserReport.Title;
if (model.UserReport.Report != null)
userReport.Report = model.UserReport.Report;
if (model.UserReport.Status != null)
userReport.Status = model.UserReportStatus;
// Store item.
if (!SysId.IsValid(userReport.Id))
{
if (!SupportMgr.InsertUserReport(userReport, DbUpdateInfo))
{
SaLogger.Warn(fn + " Failed to insert userReport: " + SupportMgr.ErrorStateMessage);
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.insert.failed"] + "\n\n" + SupportMgr.ErrorStateMessage));
}
}
else
{
if (!SupportMgr.UpdateUserReport(userReport, DbUpdateInfo))
{
SaLogger.Warn(fn + " Failed to update userReport [Id:" + SysId.ToString(userReport.Id) + "]: " + SupportMgr.ErrorStateMessage);
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + SupportMgr.ErrorStateMessage));
}
}
// Retrieve user.
SaxUserAccount userAccount = AccountMgr.GetUserAccountById(userReport.UserAccountId);
disposableItems.Add(userAccount);
// Construct response.
PopulateUserReportResponseItemData(model, userReport, userAccount, responseData.Report);
}
// Complete response.
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,45 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Saxonite.Net.Core.Web.Admin.Models.Support;
using Saxonite.Net.Core.Web.Base.App;
using System;
namespace Saxonite.Net.Core.Web.Admin.Controllers.Support
{
//=======================================================================
public partial class Controller_UserReport : SaxController_Admin_Support
{
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
// Construction.
public Controller_UserReport(SaxLocaliser localiser, ILogger<HomeController> logger) :
base(localiser, logger)
{
}
//-------------------------------------------------------------------
// Properties.
//-------------------------------------------------------------------
// Index page.
[HttpGet]
[Route("UserReport")]
[Route("UserReport/Index")]
public IActionResult Index(String sta)
{
// Validate session.
if (!ValidUserSession)
return RedirectToAction("", "");
// Model.
Model_UserReport model = new Model_UserReport(Localiser);
return View("/Views/Support/UserReport/Index.cshtml", model);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,28 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Saxonite.Net.Core.Web.Admin.Models.Support;
using Saxonite.Net.Core.Web.Base.App;
using System;
namespace Saxonite.Net.Core.Web.Admin.Controllers.Support
{
//=======================================================================
public abstract class SaxController_Admin_Support : SaxController_Admin
{
//-------------------------------------------------------------------
// Data members.
//-------------------------------------------------------------------
// Protected construction.
protected SaxController_Admin_Support(SaxLocaliser localiser, ILogger<HomeController> logger) :
base(localiser, logger)
{
}
//-------------------------------------------------------------------
// Properties.
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,407 @@
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.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.System
{
//=======================================================================
// System - Ajax - Database controller
//=======================================================================
partial class SystemController
{
//-------------------------------------------------------------------
// Database data.
private class DatabaseTableData
{
public String SchemaName { get; set; }
public String TableName { get; set; }
public String QualifiedName { get; set; }
public int RowCount { get; set; }
public int ColumnCount { get; set; }
public int IndexCount { get; set; }
}
//-------------------------------------------------------------------
// Get Database Table List ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_DatabaseTableList : AjaxResponse_Data
{
// Construction.
public AjaxResponse_DatabaseTableList(SaxModel_Admin model) : base(model) { }
// Table list.
public List<DatabaseTableData> Tables { get; } = new List<DatabaseTableData>();
}
//-------------------------------------------------------------------
[HttpPost]
[Route("System/Ajax/GetDatabaseTables")]
public IActionResult Ajax_GetDatabaseTables(Model_System model)
{
String fn = GetType().Name + ".Ajax_GetDatabaseTables";
String messageTitle = Localiser["system.caption.database"];
AjaxResponse_DatabaseTableList responseData = new AjaxResponse_DatabaseTableList(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
// Database connection.
Sax.Database databaseInstance = EnumUtil.ParseEnum(StringUtil.EmptyOnNull(model.DatabaseFilter.Instance), Sax.Database.Core);
ISacsDatabase db = App.GetDatabase(SessionContext, databaseInstance);
// Database table row counts.
Dictionary<String, int> tableRowCounts = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
String query =
"SELECT QUOTENAME(SCHEMA_NAME(sOBJ.schema_id)) + '.' + QUOTENAME(sOBJ.name) As [TableName], SUM(sPTN.Rows) As [RowCount] " +
"FROM sys.objects AS sOBJ " +
"INNER JOIN sys.partitions AS sPTN ON sOBJ.object_id = sPTN.object_id " +
"WHERE sOBJ.type = 'U' AND sOBJ.is_ms_shipped = 0x0 AND index_id< 2 /*Index Id: 0:Heap,1:Clustered*/ " +
"GROUP BY sOBJ.schema_id, sOBJ.name";
using (DbResultSet dbResultSet = DbResultSet_DataReader.ExecuteQuery(db, query))
{
while (dbResultSet != null && dbResultSet.NextRecord())
{
tableRowCounts.Add(dbResultSet.GetRecordValues().GetValue("TableName").ToString(), (int)dbResultSet.GetRecordValues().GetValue("RowCount").AsLong());
}
}
// Database tables.
List<DbDfnTable> tableDfns = db.GetTableDfns();
foreach (DbDfnTable tableDfn in tableDfns.OrderBy(item => item.Name))
{
// Filter by table name.
if (!StringUtil.IsEmpty(model.DatabaseFilter.Search) && !tableDfn.Name.ContainsCI(model.DatabaseFilter.Search))
continue;
DatabaseTableData tableData = new DatabaseTableData();
tableData.SchemaName = tableDfn.Schema;
tableData.TableName = tableDfn.Name;
tableData.QualifiedName = tableDfn.QualifiedName;
tableData.ColumnCount = tableDfn.ColumnCount;
tableData.IndexCount = tableDfn.IndexCount;
// Row count.
tableData.RowCount = tableRowCounts.GetValueOrDefault(tableDfn.QuotedQualifiedName, 0);
responseData.Tables.Add(tableData);
}
}
// Complete response.
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Validate Database Schema ajax request.
//-------------------------------------------------------------------
private class DbSchemaItemResponseData
{
public int Id { get; set; }
public String SchemaName { 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; }
}
private class AjaxResponse_ValidateDatabaseSchema : AjaxResponse_Data
{
// Construction.
public AjaxResponse_ValidateDatabaseSchema(SaxModel_Admin model) : base(model) { }
// Database instance.
public String Instance { get; set; }
// Schema validation list.
public List<DbSchemaItemResponseData> Items { get; } = new List<DbSchemaItemResponseData>();
}
//-------------------------------------------------------------------
[HttpPost]
[Route("System/Ajax/ValidateDatabaseSchema")]
public IActionResult Ajax_ValidateDatabaseSchema(Model_System model)
{
String fn = GetType().Name + ".Ajax_ValidateDatabaseSchema";
String messageTitle = Localiser["system.caption.database"];
AjaxResponse_ValidateDatabaseSchema responseData = new AjaxResponse_ValidateDatabaseSchema(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
// Database instance.
Sax.Database databaseInstance = EnumUtil.ParseEnum(StringUtil.EmptyOnNull(model.DatabaseFilter.Instance), Sax.Database.Unknown);
if (databaseInstance <= 0)
return Json(responseData.SetPopupMessage(messageTitle, "Database instance must be specified"));
responseData.Instance = databaseInstance.ToString();
// Database connection.
ISacsDatabase db = App.GetDatabase(SessionContext, databaseInstance);
if (db == null)
return Json(responseData.SetPopupMessage(messageTitle, "Unrecognised database instance specified: " + model.DatabaseFilter.Instance));
// Retrieve XML database schema definition.
var assembly = typeof(SaxSystem_Dbas).GetTypeInfo().Assembly;
String xmlSchema = SacsResourceHelper.GetManifestResourceAsText(assembly, "", "SaxDbDfn_" + databaseInstance.ToString() + ".xml");
if (StringUtil.IsEmpty(xmlSchema))
return Json(responseData.SetPopupMessage(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.HasErrorMessage())
return Json(responseData.SetPopupMessage(messageTitle, "Failed to convert XML schema: " + procSchemaConvert.GetErrorMessage()));
SaDbProcess_CompareDatabaseSchemas.Config cfgSchemaCompare = new SaDbProcess_CompareDatabaseSchemas.Config(tables, db);
cfgSchemaCompare.HideIdenticalObjects = true;
cfgSchemaCompare.IgnorePosition = true;
SaDbProcess_CompareDatabaseSchemas procSchemaCompare = new SaDbProcess_CompareDatabaseSchemas(cfgSchemaCompare);
procSchemaCompare.OnSchemaComparisonItemAdded += OnSchemaComparisonItemAdded;
procSchemaCompare.RunProcess();
List<SaDbProcess_CompareDatabaseSchemas.SchemaComparisonItem> schemaComparisonItems = procSchemaCompare.GetSchemaComparisonItems();
foreach (SaDbProcess_CompareDatabaseSchemas.SchemaComparisonItem schemaComparisonItem in schemaComparisonItems)
{
DbSchemaItemResponseData item = new DbSchemaItemResponseData()
{
Id = schemaComparisonItem.Id,
SchemaName = schemaComparisonItem.SchemaName,
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);
}
// Processing events.
public void OnSchemaComparisonItemAdded(object sender, SaDbProcess_CompareDatabaseSchemas.SchemaComparisonItemAddedEventArgs e)
{
SaDbProcess_CompareDatabaseSchemas proc = sender as SaDbProcess_CompareDatabaseSchemas;
}
//-------------------------------------------------------------------
// Database Table ajax request.
//-------------------------------------------------------------------
private class AjaxResponseData_Table
{
public AjaxResponseData_Table(DbDfnTable tableDfn)
{
Schema = tableDfn.Schema;
Name = tableDfn.Name;
if (!StringUtil.IsEmpty(tableDfn.Remarks))
Remarks = tableDfn.Remarks;
foreach (DbDfnColumn columnDfn in tableDfn.Columns)
Columns.Add(new AjaxResponseData_Column(columnDfn));
foreach (DbDfnIndex indexDfn in tableDfn.Indices)
Indices.Add(new AjaxResponseData_Index(indexDfn));
}
public String Schema { get; }
public String Name { get; }
public String QualifiedName { get { return (!StringUtil.IsEmpty(Schema) ? Schema + "." : "") + Name; } }
public String QuotedQualifiedName { get { return (!StringUtil.IsEmpty(Schema) ? "[" + Schema + "]." : "") + "[" + Name + "]"; } }
public String Remarks { get; set; } = String.Empty;
public List<AjaxResponse_Data.ItemLabelValue> Properties { get; } = new List<AjaxResponse_Data.ItemLabelValue>();
public List<AjaxResponseData_Column> Columns { get; } = new List<AjaxResponseData_Column>();
public List<AjaxResponseData_Index> Indices { get; } = new List<AjaxResponseData_Index>();
}
private class AjaxResponseData_Column
{
public int Pos { get; }
public String Name { get; }
public String Format { get; }
public String DataTypeName { get; }
public short? DataTypeNative { get; }
public String DataTypeNameNative { get; }
public bool? IsIdentity { get; }
public long? Size { get; }
public long? BufferLen { get; }
public short? DecimalDigits { get; }
public bool? Nullable { get; }
public bool? IsNullable { get; }
public String Remarks { get; }
public AjaxResponseData_Column(DbDfnColumn columnDfn)
{
Pos = columnDfn.Pos;
Name = columnDfn.Name;
Format = columnDfn.GetFormat();
if (!StringUtil.IsEmpty(columnDfn.DataTypeName))
DataTypeName = columnDfn.DataTypeName;
if (columnDfn.DataTypeNative > 0)
DataTypeNative = columnDfn.DataTypeNative;
if (!StringUtil.IsEmpty(columnDfn.DataTypeNameNative))
DataTypeNameNative = columnDfn.DataTypeNameNative;
if (columnDfn.IsIdentity)
IsIdentity = columnDfn.IsIdentity;
if (columnDfn.Size > 0)
Size = columnDfn.Size;
if (columnDfn.BufferLen > 0)
BufferLen = columnDfn.BufferLen;
if (columnDfn.DecimalDigits > 0)
DecimalDigits = columnDfn.DecimalDigits;
if (columnDfn.Nullable == DbColumnNullable.Nullable)
Nullable = true;
if (columnDfn.IsNullable)
IsNullable = columnDfn.IsNullable;
if (!StringUtil.IsEmpty(columnDfn.Remarks))
Remarks = columnDfn.Remarks;
}
}
private class AjaxResponseData_Index
{
public String Name { get; }
public int? IndexType { get; }
public bool? Unique { get; }
public bool? Primary { get; }
public bool? Clustered { get; }
public bool? Nullable { get; }
public int? Pages { get; }
public int? UniqueValues { get; }
public List<AjaxResponseData_IndexColumn> Columns { get; } = new List<AjaxResponseData_IndexColumn>();
public AjaxResponseData_Index(DbDfnIndex indexDfn)
{
Name = indexDfn.Name;
if (indexDfn.IndexType > 0)
IndexType = indexDfn.IndexType;
if (indexDfn.Unique)
Unique = indexDfn.Unique;
if (indexDfn.Primary)
Primary = indexDfn.Primary;
if (indexDfn.Clustered)
Clustered = indexDfn.Clustered;
if (indexDfn.Nullable)
Nullable = indexDfn.Nullable;
if (indexDfn.Pages > 0)
Pages = indexDfn.Pages;
if (indexDfn.UniqueValues > 0)
UniqueValues = indexDfn.UniqueValues;
foreach (DbDfnIndexColumn indexColumnDfn in indexDfn.Columns)
Columns.Add(new AjaxResponseData_IndexColumn(indexColumnDfn));
}
}
private class AjaxResponseData_IndexColumn
{
public short Seq { get; }
public String Name { get; }
public bool? Desc { get; }
public AjaxResponseData_IndexColumn(DbDfnIndexColumn indexColumnDfn)
{
Seq = indexColumnDfn.Seq;
Name = indexColumnDfn.Name;
if (!indexColumnDfn.Ascend)
Desc = true;
}
}
private class AjaxResponse_DatabaseTable : AjaxResponse_Data
{
// Construction.
public AjaxResponse_DatabaseTable(SaxModel_Admin model) : base(model) { }
// Database table.
public String PopupTitle { get; set; }
public AjaxResponseData_Table Table { get; set; }
}
//-------------------------------------------------------------------
[HttpPost]
[Route("System/Ajax/GetDatabaseTable")]
public IActionResult Ajax_GetDatabaseTable(Model_System model)
{
String fn = GetType().Name + ".Ajax_GetDatabaseTable";
String messageTitle = Localiser["system.caption.database"];
AjaxResponse_DatabaseTable responseData = new AjaxResponse_DatabaseTable(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
// Parameters.
if (StringUtil.IsEmpty(model.DatabaseTable.Name))
return Json(responseData.SetPopupMessage(messageTitle, "Table name must be supplied."));
// Database instance.
Sax.Database databaseInstance = EnumUtil.ParseEnum(StringUtil.EmptyOnNull(model.DatabaseTable.Instance), Sax.Database.Unknown);
if (databaseInstance <= 0)
return Json(responseData.SetPopupMessage(messageTitle, "Database instance must be specified"));
// Database connection.
ISacsDatabase db = App.GetDatabase(SessionContext, databaseInstance);
// Table definition.
DbDfnTable tableDfn = db.GetTableDfn(model.DatabaseTable.Name, true);
if (tableDfn == null)
return Json(responseData.SetPopupMessage(messageTitle, "Invalid Table name supplied."));
// Populate table response.
responseData.Table = new AjaxResponseData_Table(tableDfn);
responseData.PopupTitle = Localiser["label.table"] + ": " + tableDfn.Name;
// Database sizing.
String query = "Exec sp_SpaceUsed [" + tableDfn.QualifiedName + "];";
using (DbResultSet dbResultSet = DbResultSet_DataReader.ExecuteQuery(db, query))
{
if (dbResultSet != null && dbResultSet.NextRecord())
{
// Row count.
long rows = StringUtil.ToLong(dbResultSet.GetRecordValues().GetValue("rows").ToString());
if (rows > 0)
responseData.Table.Properties.Add(new AjaxResponse_Data.ItemLabelValue() { Label = "Rows", Value = rows.ToString() });
// Reserved space.
String reserved = dbResultSet.GetRecordValues().GetValue("reserved").AsString();
if (!StringUtil.IsEmpty(reserved))
responseData.Table.Properties.Add(new AjaxResponse_Data.ItemLabelValue() { Label = "Reserved", Value = reserved });
// Data space.
String data = dbResultSet.GetRecordValues().GetValue("data").AsString();
if (!StringUtil.IsEmpty(data))
responseData.Table.Properties.Add(new AjaxResponse_Data.ItemLabelValue() { Label = "Data", Value = data });
// Index space.
String index = dbResultSet.GetRecordValues().GetValue("index_size").AsString();
if (!StringUtil.IsEmpty(index))
responseData.Table.Properties.Add(new AjaxResponse_Data.ItemLabelValue() { Label = "Indices", Value = index });
// Unused space.
String unused = dbResultSet.GetRecordValues().GetValue("unused").AsString();
if (!StringUtil.IsEmpty(unused))
responseData.Table.Properties.Add(new AjaxResponse_Data.ItemLabelValue() { Label = "Unused", Value = unused });
}
}
}
// Complete response.
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,301 @@
using Microsoft.AspNetCore.Http;
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.IO;
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.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Admin.Controllers.System
{
//=======================================================================
// System - Ajax - Logs controller
//=======================================================================
partial class SystemController
{
//-------------------------------------------------------------------
// Database data.
private class LogFolderData
{
public String Name { get; set; }
}
private class LogFileData
{
public String Name { get; set; }
public String UpdateFmt { get; set; }
public long Size { get; set; }
}
//-------------------------------------------------------------------
// Get Log Folder List ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_LogFolderList : AjaxResponse_Data
{
// Construction.
public AjaxResponse_LogFolderList(SaxModel_Admin model) : base(model) { }
// Log file list.
public List<LogFolderData> Folders { get; } = new List<LogFolderData>();
}
//-------------------------------------------------------------------
/// <summary>
/// Retrieve the list of folders under the root folder.
/// <para>- These should correspond to applications (admin, api, online etc).</para>
/// </summary>
[HttpPost]
[Route("System/Ajax/Logs/GetFolderList")]
[Route("System/Ajax/Logs/GetFolders")]
public IActionResult Ajax_GetLogFolderList(Model_System model)
{
String fn = GetType().Name + ".Ajax_GetLogFolderList";
String messageTitle = Localiser["system.caption.logs"];
AjaxResponse_LogFolderList responseData = new AjaxResponse_LogFolderList(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Identify folders in root log folder.
String rootLogFolder = model.LoggingRootFolder;
foreach (String folder in Directory.EnumerateDirectories(rootLogFolder))
{
String folderName = Path.GetFileName(folder);
if (folderName.IsMatch("^(Online|Admin|Api)$", RegexOptions.IgnoreCase))
responseData.Folders.Add(new LogFolderData() { Name = folderName });
}
// Complete response.
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Get Log Year Folder List ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_LogYearList : AjaxResponse_Data
{
// Construction.
public AjaxResponse_LogYearList(SaxModel_Admin model) : base(model) { }
// Log file list.
public List<LogFolderData> Years { get; } = new List<LogFolderData>();
}
//-------------------------------------------------------------------
/// <summary>
/// Retrieve the list of year folders under the selected folder.
/// </summary>
[HttpPost]
[Route("System/Ajax/Logs/GetYearList")]
[Route("System/Ajax/Logs/GetYears")]
public IActionResult Ajax_GetLogYearList(Model_System model)
{
String fn = GetType().Name + ".Ajax_GetLogYearList";
String messageTitle = Localiser["system.caption.logs"];
AjaxResponse_LogYearList responseData = new AjaxResponse_LogYearList(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Store filter properties to session.
SessionData_Admin.System.Logs.Filter.Folder = model.LogsFilter.Folder;
SessionData_Admin.System.Logs.Filter.Year = "";
SessionData_Admin.System.Logs.Filter.Month = "";
// Identify folders in selected log folder.
String logFolder = Path.Combine(model.LoggingRootFolder, model.LogsFilter.Folder);
foreach (String folder in Directory.EnumerateDirectories(logFolder))
{
String folderName = Path.GetFileName(folder);
if (folderName.IsMatch("^\\d{4}$"))
responseData.Years.Add(new LogFolderData() { Name = folderName });
}
// Complete response.
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Get Log Month Folder List ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_LogMonthList : AjaxResponse_Data
{
// Construction.
public AjaxResponse_LogMonthList(SaxModel_Admin model) : base(model) { }
// Log file list.
public List<LogFolderData> Months { get; } = new List<LogFolderData>();
}
//-------------------------------------------------------------------
/// <summary>
/// Retrieve the list of year folders under the selected folder.
/// </summary>
[HttpPost]
[Route("System/Ajax/Logs/GetMonthList")]
[Route("System/Ajax/Logs/GetMonths")]
public IActionResult Ajax_GetLogMonthList(Model_System model)
{
String fn = GetType().Name + ".Ajax_GetLogMonthList";
String messageTitle = Localiser["system.caption.logs"];
AjaxResponse_LogMonthList responseData = new AjaxResponse_LogMonthList(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Store filter properties to session.
SessionData_Admin.System.Logs.Filter.Folder = model.LogsFilter.Folder;
SessionData_Admin.System.Logs.Filter.Year = model.LogsFilter.Year;
SessionData_Admin.System.Logs.Filter.Month = "";
//SessionData_Admin.System.Logs.Filter.Month = model.LogsFilter.Month;
// Identify folders in selected year folder.
String logFolder = Path.Combine(model.LoggingRootFolder, model.LogsFilter.Folder, model.LogsFilter.Year);
foreach (String folder in Directory.EnumerateDirectories(logFolder))
{
String folderName = Path.GetFileName(folder);
if (folderName.IsMatch("^\\d{6}$"))
responseData.Months.Add(new LogFolderData() { Name = folderName });
}
// Complete response.
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Get Log File List ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_LogFileList : AjaxResponse_Data
{
// Construction.
public AjaxResponse_LogFileList(SaxModel_Admin model) : base(model) { }
// Log file list.
public List<LogFileData> Files { get; } = new List<LogFileData>();
}
//-------------------------------------------------------------------
[HttpPost]
[Route("System/Ajax/Logs/GetFileList")]
[Route("System/Ajax/Logs/GetFiles")]
public IActionResult Ajax_GetLogFileList(Model_System model)
{
String fn = GetType().Name + ".Ajax_GetLogFileList";
String messageTitle = Localiser["system.caption.logs"];
AjaxResponse_LogFileList responseData = new AjaxResponse_LogFileList(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Store filter properties to session.
SessionData_Admin.System.Logs.Filter.Folder = model.LogsFilter.Folder;
SessionData_Admin.System.Logs.Filter.Year = model.LogsFilter.Year;
SessionData_Admin.System.Logs.Filter.Month = model.LogsFilter.Month;
// Identify folders in selected year folder.
String logFolder = Path.Combine(model.LoggingRootFolder, model.LogsFilter.Folder, model.LogsFilter.Year, model.LogsFilter.Month);
DirectoryInfo folder = new DirectoryInfo(logFolder);
foreach (FileInfo file in folder.EnumerateFiles())
{
responseData.Files.Add(new LogFileData() { Name = file.Name, UpdateFmt = file.LastWriteTime.ToString("d-MMM-yy HH:mm"), Size = file.Length });
}
// Complete response.
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Get Log File ajax request.
//-------------------------------------------------------------------
private class AjaxResponse_LogFile : AjaxResponse_Data
{
// Construction.
public AjaxResponse_LogFile(SaxModel_Admin model) : base(model) { }
// Log file.
public class LogFileData
{
public String Path { get; set; }
public String Name { get; set; }
public String Data { get; set; }
public String Html { get; set; } = "";
}
public LogFileData File { get; } = new LogFileData();
}
//-------------------------------------------------------------------
[HttpPost]
[Route("System/Ajax/Logs/GetFile")]
[Route("System/Ajax/Logs/GetLogFile")]
public IActionResult Ajax_GetLogFile(Model_System model)
{
String fn = GetType().Name + ".Ajax_GetLogFile";
String messageTitle = Localiser["system.caption.logs"];
AjaxResponse_LogFile responseData = new AjaxResponse_LogFile(model);
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Store filter properties to session.
SessionData_Admin.System.Logs.Filter.Folder = model.LogsFilter.Folder;
SessionData_Admin.System.Logs.Filter.Year = model.LogsFilter.Year;
SessionData_Admin.System.Logs.Filter.Month = model.LogsFilter.Month;
// Identify folders in selected year folder.
responseData.File.Path = Path.Combine(model.LoggingRootFolder, model.LogsFilter.Folder, model.LogsFilter.Year, model.LogsFilter.Month, model.LogsFilter.Filename);
responseData.File.Name = Path.GetFileName(responseData.File.Path);
try
{
String[] fileLines = FileUtil.ReadAllFileLines(responseData.File.Path);
responseData.File.Data = String.Join("\n", fileLines);
// Construct html data.
foreach (var fileLine in fileLines)
{
String line = fileLine;
List<String> classNames = new List<string>();
if (fileLine.IsMatch("^\t{2}"))
{
line = Regex.Replace(fileLine, "^\t+", "");
classNames.Add("tab2");
}
if (fileLine.IsMatch("^\t"))
{
line = Regex.Replace(fileLine, "^\t", "");
classNames.Add("tab");
}
if (line.IsMatch("\\d{8}:\\d{6}\\s+(error|err)\\s+", RegexOptions.IgnoreCase))
classNames.Add("err");
else if (line.IsMatch("\\d{8}:\\d{6}\\s+(warn|wrn)\\s+", RegexOptions.IgnoreCase))
classNames.Add("wrn");
else if (line.IsMatch("\\d{8}:\\d{6}\\s+(info|inf)\\s+", RegexOptions.IgnoreCase))
classNames.Add("inf");
responseData.File.Html += $"<div class='{String.Join(" ", classNames)}'>" + line + "</div>";
}
}
catch (Exception exc)
{
SaLogger.Warn($"{SysUtil.Fn} {ExceptionUtil.GetLogMessage(exc, "reading log file", 0)}");
}
// Complete response.
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,81 @@
using Microsoft.AspNetCore.Mvc;
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.Core.Web.Admin.Models;
using Saxonite.Net.Core.Web.Base.App;
using Saxonite.Net.Core.Web.Base.Controllers;
using Saxonite.Net.Standard.Base.Bus;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Admin.Controllers.System
{
//=======================================================================
public partial class SystemController : SaxController_Admin
{
//-------------------------------------------------------------------
// Construction.
public SystemController(SaxLocaliser localiser, ILogger<HomeController> logger) :
base(localiser, logger)
{
}
//-------------------------------------------------------------------
/// <summary>
/// System - Home.
/// <para>- No 'System' home page for now, redirect to home page.</para>
/// </summary>
[HttpGet]
public IActionResult Index()
{
return RedirectToAction("", "");
}
//-------------------------------------------------------------------
/// <summary>
/// System - Database page.
/// </summary>
[HttpGet]
public IActionResult Database(Model_System model)
{
// Validate session.
if (!ValidUserSession)
AutoLoginUser();
if (!ValidUserSession)
return RedirectToAction("", "");
model.AdditionalFiles_JS.Add("js/sax-sys-db.js");
return View(model);
}
//-------------------------------------------------------------------
/// <summary>
/// System - Logs page.
/// </summary>
[HttpGet]
public IActionResult Logs(Model_System model)
{
// Validate session.
if (!ValidUserSession)
AutoLoginUser();
if (!ValidUserSession)
return RedirectToAction("", "");
model.AdditionalFiles_JS.Add("js/sax-sys-logs.js");
model.LogsFilter.Folder = StringUtil.OnBlank(SessionData_Admin.System.Logs.Filter.Folder, "Admin");
model.LogsFilter.Year = StringUtil.OnBlank(SessionData_Admin.System.Logs.Filter.Year, DateTime.Today.ToString("yyyy"));
model.LogsFilter.Month = StringUtil.OnBlank(SessionData_Admin.System.Logs.Filter.Month, DateTime.Today.ToString("yyyyMM"));
return View(model);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,567 @@
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 UserController
{
//-------------------------------------------------------------------
// 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 RegionNameCountry { get; set; }
public String RegionNameProvince { get; set; }
public int? UserSportCount { get; set; } = null;
public int? UserRoleCount { get; set; } = null;
public bool? SystemAdmin { get; set; } = null;
public String LastOnlineLoginAt { get; set; }
public String LastAdminLoginAt { get; set; }
}
private class UserItemData : UserListData
{
public String RegionIdCountry { get; set; }
public String RegionIdProvince { get; set; }
// User sports.
public class UserSportData
{
public String SportId { get; set; }
public String SportName { get; set; }
}
public List<UserSportData> Sports { get; set; }
// User roles.
public List<String> userRoleCodes { get; } = new List<string>();
}
private void PopulateUserResponseListData(SaxModel_Admin model, UserListData data, SaxUserAccount user, List<SaxUserSport> userSports, List<SaxUserRole> userRoles)
{
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.RegionNameCountry = model.GetRegionName(user.RegionIdCountry);
data.RegionNameProvince = model.GetRegionName(user.RegionIdProvince);
// User sports.
// User roles.
if (userRoles?.GetUserRolesByStatus(SaStatus.Active).Count > 0)
data.UserRoleCount = userRoles.GetUserRolesByStatus(SaStatus.Active).Count;
data.SystemAdmin = (userRoles != null && userRoles.Exists(item => item.UserRole == Sax.UserRole.SystemAdmin) ? true : (bool?)null);
data.LastOnlineLoginAt = model.FormatDateTimeForDisplay(user.LastLoginAt);
data.StatusCode = user.StatusCode;
data.StatusDesc = user.StatusDesc;
// System Admin role.
SaxUserRole userRole_SystemAdmin = userRoles.Find(item => item.UserRole == Sax.UserRole.SystemAdmin);
if (userRole_SystemAdmin != null)
data.LastAdminLoginAt = model.FormatDateTimeForDisplay(userRole_SystemAdmin.LastLoginAt);
}
private void PopulateUserResponseItemData(SaxModel_Admin model, UserItemData data, SaxUserAccount user, List<SaxUserSport> userSports, List<SaxUserRole> userRoles)
{
PopulateUserResponseListData(model, data, user, userSports, userRoles);
data.RegionIdCountry = SysId.ToString(user.RegionIdCountry);
data.RegionIdProvince = SysId.ToString(user.RegionIdProvince);
// User sports.
if (userSports != null && !userSports.IsEmpty())
{
data.Sports = new List<UserItemData.UserSportData>();
List<SaxSport> sports = ReferenceMgr.GetSportListByIds(userSports.GetSportIds());
foreach (SaxUserSport userSport in userSports)
{
SaxSport sport = sports.GetSportById(userSport.SportId);
UserItemData.UserSportData userSportData = new UserItemData.UserSportData()
{
SportId = SysId.ToString(userSport.SportId),
SportName = sport?.Name ?? null
};
data.Sports.Add(userSportData);
}
}
// User roles.
if (userRoles != null)
{
foreach (SaxUserRole userRole in userRoles.GetUserRolesByStatus(SaStatus.Active))
data.userRoleCodes.Add(UserRoleCodes.GetCodeForType(userRole.UserRole));
}
}
//-------------------------------------------------------------------
// Utilities.
private List<SaxUserSport> GetUserSports(SysId userAccountId, SaxUserSport.StatusCat statusCat = SaxUserSport.StatusCat.NotDeleted)
{
if (!SysId.IsValid(userAccountId)) return new List<SaxUserSport>();
SaxUserSport.Params prmUserSport = new SaxUserSport.Params();
prmUserSport.UserAccountIdsIncluded.Add(userAccountId);
prmUserSport.StatusCat = statusCat;
return AccountMgr.GetUserSportList(prmUserSport);
}
private List<SaxUserRole> GetUserRoles(SysId userAccountId, SaxUserRole.StatusCat statusCat = SaxUserRole.StatusCat.NotDeleted)
{
if (!SysId.IsValid(userAccountId)) return new List<SaxUserRole>();
SaxUserRole.Params prmUserRole = new SaxUserRole.Params();
prmUserRole.UserAccountIdsIncluded.Add(userAccountId);
prmUserRole.StatusCat = statusCat;
return AccountMgr.GetUserRoleList(prmUserRole);
}
//-------------------------------------------------------------------
// 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>();
// Reference data.
public List<CodeDesc> UserRoles { get; } = new List<CodeDesc>();
public List<CodeDesc> Statuses { get; } = new List<CodeDesc>();
public String DefaultStatusCode { get; set; }
}
//-------------------------------------------------------------------
[HttpPost]
[Route("User/Ajax/GetList")]
[Route("User/Ajax/GetUsers")]
[Route("User/Ajax/GetUserList")]
public IActionResult Ajax_GetUserList(Model_User model)
{
String fn = GetType().Name + ".Ajax_GetUserList";
String messageTitle = Localiser["user.caption.users"];
AjaxResponse_UserList responseData = new AjaxResponse_UserList(model);
// Validate session and referrer.
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
// Reference data - user roles.
foreach (TypeCodeDesc userRole in UserRoleCodes)
responseData.UserRoles.Add(userRole.Code, Localiser.GetUserRoleCaption((Sax.UserRole)userRole.Type));
responseData.DefaultStatusCode = SacsSystem.GetStatusCode(SaxUserAccount.DefaultStatus);
// Reference data - statuses.
foreach (SaStatus status in SaxUserAccount.ValidStatuses.FindAll(item => item != SaStatus.Deleted))
responseData.Statuses.Add(SacsSystem.GetStatusCode(status), Localiser.GetStatusCaption(status));
// Store filter values.
SysId filterSportId = SysId.ToSysId(model.Filter.UserSportId);
SessionData_Admin.Filter.SportId = SysId.IsValid(filterSportId) ? filterSportId : null;
SysId filterRegionId = SysId.ToSysId(model.Filter.UserRegionId);
SessionData_Admin.Filter.RegionId = SysId.IsValid(filterRegionId) ? filterRegionId : null;
// Item filter.
SaxUserAccount.Params prmUserAccount = new SaxUserAccount.Params();
prmUserAccount.UserFilterPartial = model.Filter.User;
prmUserAccount.SportIdsIncluded.AddId(SysId.ToSysId(model.Filter.UserSportId));
prmUserAccount.RegionIdsIncluded.AddId(SysId.ToSysId(model.Filter.UserRegionId));
prmUserAccount.StatusCat = model.Filter.UserStatusCategoryCode != null ? model.FilterUserStatusCategory : SaxUserAccount.StatusCat.NotDeleted;
model.Paging.ItemTotal = (ulong)AccountMgr.GetUserAccountCount(prmUserAccount);
model.PopulatePagingTo(prmUserAccount.ResultsPaging);
// Item columns.
ColumnDfn colId = AccountMgr.GetUserAccountColumnDfn(SaxUserAccount.Column.Id);
ColumnDfn colEmail = AccountMgr.GetUserAccountColumnDfn(SaxUserAccount.Column.EmailAddress);
ColumnDfn colTitle = AccountMgr.GetUserAccountColumnDfn(SaxUserAccount.Column.Title);
ColumnDfn colFirstName = AccountMgr.GetUserAccountColumnDfn(SaxUserAccount.Column.FirstName);
ColumnDfn colLastName = AccountMgr.GetUserAccountColumnDfn(SaxUserAccount.Column.LastName);
ColumnDfn colCountryName = AccountMgr.GetUserAccountColumnDfn(SaxUserAccount.Column.RegionCountry_Name);
ColumnDfn colProvinceName = AccountMgr.GetUserAccountColumnDfn(SaxUserAccount.Column.RegionProvince_Name);
ColumnDfn colUserRoleCount = AccountMgr.GetUserAccountColumnDfn(SaxUserAccount.Column.UserRoleCount);
ColumnDfn colSystemAdminUser = AccountMgr.GetUserAccountColumnDfn(SaxUserAccount.Column.SystemAdminUser);
ColumnDfn colUserSportCount = AccountMgr.GetUserAccountColumnDfn(SaxUserAccount.Column.UserSportCount_Active);
ColumnDfn colLastLogin = AccountMgr.GetUserAccountColumnDfn(SaxUserAccount.Column.LastLoginAt);
ColumnDfn colAdminLastLogin = AccountMgr.GetUserAccountColumnDfn(SaxUserAccount.Column.LastAdminLoginAt);
ColumnDfn colStatus = AccountMgr.GetUserAccountColumnDfn(SaxUserAccount.Column.Status);
List<ColumnDfn> columns = new List<ColumnDfn>()
{
colId, colEmail, colTitle, colFirstName, colLastName,
colCountryName, colProvinceName,
colUserSportCount, colUserRoleCount,
colSystemAdminUser, colLastLogin, colAdminLastLogin,
colStatus
};
// Query items.
using (DbResultSet resultSet = AccountMgr.ExecuteUserAccountQuery(columns, prmUserAccount))
{
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
{
NamedValue.Map values = resultSet.GetRecordValues();
UserListData userData = new UserListData()
{
Id = values.GetString(colId.DataName),
EmailAddress = values.GetString(colEmail.DataName),
Title = values.GetString(colTitle.DataName),
FirstName = values.GetString(colFirstName.DataName),
LastName = values.GetString(colLastName.DataName),
RegionNameCountry = values.GetString(colCountryName.DataName),
RegionNameProvince = values.GetString(colProvinceName.DataName),
UserSportCount = StringUtil.ToInt(values.GetString(colUserSportCount.DataName)),
UserRoleCount = StringUtil.ToInt(values.GetString(colUserRoleCount.DataName)),
SystemAdmin = StringUtil.ToBool(values.GetString(colSystemAdminUser.DataName)),
LastOnlineLoginAt = model.FormatDateTimeForDisplay(values.GetDateTime(colLastLogin.DataName)),
LastAdminLoginAt = model.FormatDateTimeForDisplay(values.GetDateTime(colAdminLastLogin.DataName)),
StatusCode = values.GetString(colStatus.DataName),
StatusDesc = SacsSystem.GetStatusDescForCode(values.GetString(colStatus.DataName))
};
if (userData.UserSportCount < 1) userData.UserSportCount = null;
if (userData.UserRoleCount < 1) userData.UserRoleCount = null;
if (userData.SystemAdmin == false) userData.SystemAdmin = null;
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("User/Ajax/GetItem")]
[Route("User/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 Sports.
List<SaxUserSport> userSports = GetUserSports(user.Id, SaxUserSport.StatusCat.ActiveOnly);
disposableItems.AddRange(userSports);
// User Roles.
List<SaxUserRole> userRoles = GetUserRoles(user.Id, SaxUserRole.StatusCat.ActiveOnly);
disposableItems.AddRange(userRoles);
// Construct response.
PopulateUserResponseItemData(model, responseData.user, user, userSports, userRoles);
responseData.Success = true;
}
return Json(responseData);
}
//-------------------------------------------------------------------
// Get User by email ajax request.
//-------------------------------------------------------------------
[HttpPost]
[Route("User/Ajax/GetUserByEmail")]
public IActionResult Ajax_GetUserByEmail(Model_User model)
{
String fn = GetType().Name + ".Ajax_GetUserByEmail";
String messageTitle = Localiser["user.caption.user"];
AjaxResponse_User responseData = new AjaxResponse_User(model);
StoreSessionData = false;
// 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 user.
if (StringUtil.IsEmpty(model.UserEmailAddress))
{
SaLogger.Warn(fn + " Attempt to get user details with blank email.");
return Json(responseData);
}
// Retrieve user.
SaxUserAccount user = AccountMgr.GetUserAccountByEmailAddress(model.UserEmailAddress);
disposableItems.Add(user);
if (user == null)
{
SaLogger.Warn(fn + " Failed to retrieve user for Email: " + model.UserEmailAddress);
return Json(responseData);
}
// User Sports.
List<SaxUserSport> userSports = GetUserSports(user.Id, SaxUserSport.StatusCat.ActiveOnly);
disposableItems.AddRange(userSports);
// User Roles.
List<SaxUserRole> userRoles = GetUserRoles(user.Id, SaxUserRole.StatusCat.ActiveOnly);
disposableItems.AddRange(userRoles);
// Construct response.
PopulateUserResponseItemData(model, responseData.user, user, userSports, userRoles);
responseData.Success = true;
}
return Json(responseData);
}
//-------------------------------------------------------------------
// Store User ajax request.
//-------------------------------------------------------------------
[HttpPost]
[Route("User/Ajax/StoreItem")]
[Route("User/Ajax/StoreUser")]
public IActionResult Ajax_StoreUser(Model_User model)
{
String fn = GetType().Name + ".Ajax_StoreUser";
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);
// Validate item properties.
model.ValidateUserData(responseData);
if (responseData.HasControlErrors())
return Json(responseData);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
{
// Retrieve existing item.
SysId userAccountId = SysId.ToSysId(model.UserId);
SaxUserAccount user = AccountMgr.GetUserAccountById(userAccountId);
disposableItems.Add(user);
if (user == null && SysId.IsValid(userAccountId))
{
SaLogger.Warn(fn + " Failed to retrieve user for Id: '" + model.UserId + "'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// User roles, including deleted in case of reassignment.
List<SaxUserRole> userRoles = new List<SaxUserRole>();
if (user != null) userRoles = GetUserRoles(user.Id, SaxUserRole.StatusCat.Any);
disposableItems.AddRange(userRoles);
if (model.UserRoleCodes != null)
{
List<String> userRoleCodes = model.GetUserRoleCodes();
foreach (String userRoleCode in userRoleCodes)
{
// Role to activate.
Sax.UserRole role = (Sax.UserRole)UserRoleCodes.GetTypeForCode(userRoleCode);
SaxUserRole userRole = userRoles.GetUserRoleByRole(role);
if (userRole == null)
{
userRole = AccountMgr.CreateUserRole(user?.Id, role);
userRoles.Add(userRole);
}
userRole.Status = SaStatus.Active;
}
// Roles to deactivate.
foreach (SaxUserRole userRole in userRoles)
{
if (!userRoleCodes.Exists(item => item.EqualsCI(UserRoleCodes.GetCodeForType(userRole.UserRole))))
userRole.Status = SaStatus.Inactive;
}
}
// Create new item if required.
if (user == null)
{
user = AccountMgr.CreateUserAccount();
if (model.UserEmailAddress != null)
user.EmailAddress = model.UserEmailAddress;
}
// Populate item properties.
if (model.UserTitle != null)
user.Title = model.UserTitle;
if (model.UserFirstName != null)
user.FirstName = model.UserFirstName;
if (model.UserLastName != null)
user.LastName = model.UserLastName;
if (model.UserRegionIdCountry != null)
user.RegionIdCountry = SysId.ToSysId(model.UserRegionIdCountry);
if (model.UserRegionIdProvince != null)
user.RegionIdProvince = SysId.ToSysId(model.UserRegionIdProvince);
if (model.UserStatusCode != null)
user.Status = model.UserStatus;
// Store item.
if (!SysId.IsValid(user.Id))
{
if (!AccountMgr.InsertUserAccount(user, DbUpdateInfo))
{
SaLogger.Warn(fn + " Failed to insert user: " + AccountMgr.ErrorStateMessage);
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.insert.failed"] + "\n\n" + AccountMgr.ErrorStateMessage));
}
}
else
{
if (!AccountMgr.UpdateUserAccount(user, DbUpdateInfo))
{
SaLogger.Warn(fn + " Failed to update user [Id:" + SysId.ToString(user.Id) + "]: " + AccountMgr.ErrorStateMessage);
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + AccountMgr.ErrorStateMessage));
}
}
if (model.UserRoleCodes != null)
{
// Associated data - User Roles.
foreach (SaxUserRole userRole in (userRoles))
{
// Store User Role.
if (!SysId.IsValid(userRole.Id))
{
// Apply User Account Id if not yet done (for new user).
if (!SysId.IsValid(userRole.UserAccountId))
userRole.UserAccountId = user.Id;
if (!AccountMgr.InsertUserRole(userRole, DbUpdateInfo))
SaLogger.Warn(fn + " Failed to insert user role: " + AccountMgr.ErrorStateMessage);
}
else
{
if (!AccountMgr.UpdateUserRole(userRole, DbUpdateInfo))
SaLogger.Warn(fn + " Failed to update user role [Id:" + SysId.ToString(userRole.Id) + "]: " + AccountMgr.ErrorStateMessage);
}
}
}
// User Sports.
List<SaxUserSport> userSports = GetUserSports(user.Id, SaxUserSport.StatusCat.ActiveOnly);
disposableItems.AddRange(userSports);
// Construct response.
PopulateUserResponseItemData(model, responseData.user, user, userSports, userRoles.GetUserRolesByStatus(SaStatus.Active));
responseData.Success = true;
}
return Json(responseData);
}
//-------------------------------------------------------------------
// Send User Emails ajax request.
//-------------------------------------------------------------------
[HttpPost]
[Route("User/Ajax/SendEmails")]
public IActionResult Ajax_SendEmails(Model_User model)
{
String fn = GetType().Name + ".Ajax_SendEmails";
String messageTitle = Localiser["label.emails"];
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>())
{
// Retrieve user.
SysId userAccountId = SysId.ToSysId(model.UserId);
SaxUserAccount userAccount = AccountMgr.GetUserAccountById(userAccountId);
disposableItems.Add(userAccount);
if (userAccount == null)
{
SaLogger.Warn(fn + " Failed to retrieve user for Id: '" + model.UserId + "'");
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// User roles.
List<SaxUserRole> userRoles = GetUserRoles(userAccount.Id, SaxUserRole.StatusCat.Any);
disposableItems.AddRange(userRoles);
bool systemAdmin = userRoles.Exists(item => item.UserRole == Sax.UserRole.SystemAdmin);
// Email types to send.
bool sendAcountActivationOnline = StringUtil.ToBool(model.UserEmailAcountActivationOnline);
bool sendRequestPasswordOnline = StringUtil.ToBool(model.UserEmailRequestPasswordOnline);
bool sendRequestPasswordAdmin = StringUtil.ToBool(model.UserEmailRequestPasswordAdmin);
if (!sendAcountActivationOnline && !sendRequestPasswordOnline && !sendRequestPasswordAdmin)
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.validation.select.option"]));
if (sendRequestPasswordAdmin && !systemAdmin)
return Json(responseData.SetPopupMessage(messageTitle, Localiser["user.msg.not.system.admin"]));
// Send emails.
int emailsSentCount = 0;
if (sendAcountActivationOnline)
{
if (!OrganisationController.SendAccountActivatedEmail(this, userAccount))
return Json(responseData.SetPopupMessage(messageTitle, "Failed to send Online Account Activation email."));
emailsSentCount++;
}
if (sendRequestPasswordOnline)
{
if (!SecurityController.SendRequestPasswordEmail(this, userAccount, true))
return Json(responseData.SetPopupMessage(messageTitle, "Failed to send Online Request Password email."));
emailsSentCount++;
}
if (sendRequestPasswordAdmin)
{
if (!SecurityController.SendRequestPasswordEmail(this, userAccount, false))
return Json(responseData.SetPopupMessage(messageTitle, "Failed to send Admin Request Password email."));
emailsSentCount++;
}
// User Sports.
List<SaxUserSport> userSports = GetUserSports(userAccount.Id, SaxUserSport.StatusCat.ActiveOnly);
disposableItems.AddRange(userSports);
// Construct response.
PopulateUserResponseItemData(model, responseData.user, userAccount, userSports, userRoles.GetUserRolesByStatus(SaStatus.Active));
responseData.SetNotifyMessage(messageTitle, "Emails sent: " + emailsSentCount);
responseData.Success = true;
}
return Json(responseData);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,62 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Sarand.Net.Standard.Base.Utils;
using Saxonite.Net.Core.Web.Admin.Models;
using Saxonite.Net.Core.Web.Base.App;
using Saxonite.Net.Core.Web.Base.Controllers;
using Saxonite.Net.Standard.Base.Bus;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Admin.Controllers
{
//=======================================================================
public partial class UserController : SaxController_Admin
{
//-------------------------------------------------------------------
// Construction.
public UserController(SaxLocaliser localiser, ILogger<HomeController> logger) :
base(localiser, logger)
{
}
//-------------------------------------------------------------------
[HttpGet]
public IActionResult Index(String sta)
{
// Validate session.
if (!ValidUserSession)
AutoLoginUser();
if (!ValidUserSession)
return RedirectToAction("", "");
// Model.
Model_User model = new Model_User(Localiser);
// Stored Sport filter.
model.Filter.UserSportId = SessionData_Admin.Filter.SportId?.ToString() ?? null;
using (SaxSport filterSport = ReferenceMgr.GetSportById(SessionData_Admin.Filter.SportId))
model.Filter.UserSportName = Localiser.GetSportName(SessionData_Admin.Filter.SportId, filterSport?.Name ?? "");
// Stored Region filter.
model.Filter.UserRegionId = SessionData_Admin.Filter.RegionId?.ToString() ?? null;
using (SaxRegion filterRegion = ReferenceMgr.GetRegionById(SessionData_Admin.Filter.RegionId))
model.Filter.UserRegionName = Localiser.GetRegionName(SessionData_Admin.Filter.RegionId, filterRegion?.Name ?? "");
// Paging.
model.Paging.PageNo = 1;
model.Paging.PageLength = 12;
// Default status.
if (sta.StartsWithCI("Awa"))
model.Filter.UserStatusCategoryCode = nameof(SaxUserAccount.StatusCat.AwaitingOnly);
return View(model);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -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);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,47 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Sarand.Net.Standard.Base.Utils;
using Saxonite.Net.Core.Web.Admin.Models;
using Saxonite.Net.Core.Web.Base.App;
using Saxonite.Net.Core.Web.Base.Controllers;
using Saxonite.Net.Standard.Base.Bus;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Admin.Controllers
{
//=======================================================================
public partial class UsersOnlineController : SaxController_Admin
{
//-------------------------------------------------------------------
// Construction.
public UsersOnlineController(SaxLocaliser localiser, ILogger<HomeController> logger) :
base(localiser, logger)
{
}
//-------------------------------------------------------------------
[HttpGet]
public IActionResult Index(String sta)
{
// Validate session.
if (!ValidUserSession)
AutoLoginUser();
if (!ValidUserSession)
return RedirectToAction("", "");
// Paging.
Model_UsersOnline model = new Model_UsersOnline(Localiser);
model.Paging.PageNo = 1;
model.Paging.PageLength = 12;
return View(model);
}
//-------------------------------------------------------------------
}
//=======================================================================
}