Add project files.

This commit is contained in:
2021-05-11 19:38:43 +01:00
parent d316c1898d
commit ae64e14da4
1401 changed files with 283421 additions and 0 deletions
+32
View File
@@ -0,0 +1,32 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Admin.App
{
//=======================================================================
public class Program
{
//-------------------------------------------------------------------
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
//-------------------------------------------------------------------
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<SaxStartup_Admin>();
});
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,232 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
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.IO;
using Sarand.Net.Standard.Base.Utils;
using Sarand.Net.Standard.Dbas.Base;
using Saxonite.Net.Core.Web.Admin.Consoles;
using Saxonite.Net.Core.Web.Base.App;
using Saxonite.Net.Core.Web.Base.Helpers;
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 Saxonite.Net.Standard.Base.Mgr;
using Saxonite.Net.Standard.Dbas.App;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Admin.App
{
//=======================================================================
public class SaxApplication_Admin : SaxApplication_Web
{
//-------------------------------------------------------------------
// Data members.
private bool generateDatabaseDefinitions = false;
private bool populateRegions = false;
private Dictionary<String, SaxSessionConfig> sessionConfigs = new Dictionary<string, SaxSessionConfig>();
private Dictionary<String, SaxEmailConfig> emailConfigs = new Dictionary<string, SaxEmailConfig>();
// Session contexts.
private class SessionContextData
{
public SessionContextData(SaxSessionContext_Web sessionContext)
{
SessionContext = sessionContext;
}
public SaxSessionContext_Web SessionContext { get; }
public ISacsDatabase CoreDatabase { get; set; }
}
private readonly Dictionary<int, SessionContextData> sessionContexts = new Dictionary<int, SessionContextData>();
//-------------------------------------------------------------------
// Construction.
public SaxApplication_Admin(IConfiguration configuration)
{
InitialiseTemporaryLoggingFolder();
try
{
AppInstance = this;
SaxGlobals.App = this;
SaxGlobals.System = (System_Web = new SaxSystem_Web());
Configuration = configuration;
ConsoleHelper_Web = new SaxConsoleHelper_WebAdmin(this);
}
catch (Exception exc)
{
SaLogger.Error(GetType().Name + ".CTR " + ExceptionUtil.GetErrorStateMessage(exc));
throw exc;
}
}
//-------------------------------------------------------------------
// Properties.
public static SaxApplication_Admin AppInstance { get; private set; }
public override ISacsDatabase GetCoreDatabase(SaxSessionContext sessionContext)
{
return sessionContexts.GetValue(sessionContext.Index, null)?.CoreDatabase;
}
public override String SessionCookieName { get { return Sax.Config.Admin.Session.Cookie.Name; } }
// Configuration.
public override SaxSessionConfig SessionConfig { get { return sessionConfigs.Values.ToList().getConfigForApplication(); } }
public override SaxEmailConfig EmailConfig { get { return emailConfigs.Values.ToList().getConfigForSession(SessionConfig); } }
//-------------------------------------------------------------------
// Initialisation.
/// <summary>Initialise environment - from Startup.</summary>
internal void InitWebHostEnvironment(IWebHostEnvironment env)
{
InitEnvironment(env);
}
/// <summary>Initialise environment - from internal (above) or unit tests.</summary>
protected void InitEnvironment(IWebHostEnvironment env)
{
String fn = GetType().Name + ".InitEnvironment";
Development = env.IsDevelopment();
environmentName = env.EnvironmentName;
SaLogger.LogFilenamePrefix = "Sax-Web-Adm";
SaLogger.IncludeHostInFileMessage = false;
SaLogger.IncludeUserInFileMessage = false;
try
{
// Load server configuration.
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.machine.{SaxSystem.ComputerName}.json", optional: false, reloadOnChange: false)
.AddEnvironmentVariables();
SaxConfiguration = builder.Build();
// Application configuration.
sessionConfigs = SaxSessionConfigHelper.ExtractSessionConfigs(SaxConfiguration.GetSection("SessionConfigs"));
emailConfigs = SaxEmailConfigHelper.ExtractEmailConfigs(SaxConfiguration.GetSection("EmailConfigs"));
LoggingRootFolder = SessionConfig.Logging.RootFolder;
// Application instance.
if (SessionConfig.Instance.EqualsCI("Dev")) instanceType = Sax.InstanceType.Dev;
else if (SessionConfig.Instance.ContainsCI("Q")) instanceType = Sax.InstanceType.QC;
else if (SessionConfig.Instance.ContainsCI("UAT")) instanceType = Sax.InstanceType.UAT;
else if (SessionConfig.Instance.EqualsCI("PreProd")) instanceType = Sax.InstanceType.UAT;
// Initialise hard-coded logging path.
if (StringUtil.IsEmpty(LoggingRootFolder))
{
if (SaxSystem.ComputerName.EqualsCI("WIN-BPCLM1QUOMT"))
LoggingRootFolder = @"D:\Saxonite\Logs\" + (Development ? "Dev" : "Test");
}
if (DirectoryUtil.DirectoryExists(LoggingRootFolder))
SaLogger.LogFolder = LoggingRootFolder;
LogApplicationStart(fn);
// Configure database connection.
SacsDatabaseConsole.OdbcConfig config = new SacsDatabaseConsole.OdbcConfig();
config.location = SessionConfig.CoreDatabase.Location;
config.user = SessionConfig.CoreDatabase.User;
config.pword = SessionConfig.CoreDatabase.Password;
SacsDatabase coreDatabase = SacsDatabaseConsole.OpenOdbcDatabase(config);
if (coreDatabase.HasErrorState())
SaLogger.Warn(fn + " Database initialisation error: " + coreDatabase.ErrorStateMessage);
// Session context - only one context available/necessary (for now).
SessionContextData sessionContextData = new SessionContextData(new SaxSessionContext_Web(100, Sax.Application.Admin));
sessionContextData.CoreDatabase = coreDatabase;
sessionContexts.Add(sessionContextData.SessionContext.Index, sessionContextData);
SessionContext = sessionContextData.SessionContext;
}
catch (Exception exc)
{
SaLogger.Error(fn + " " + ExceptionUtil.GetLogMessage(exc));
}
// Occasional startup processing.
if (generateDatabaseDefinitions)
GenerateDatabaseDefinition(SessionContext);
if (populateRegions)
populateRegionsFromDataFile(SessionContext);
}
//-------------------------------------------------------------------
private void GenerateDatabaseDefinition(SaxSessionContext sessionContext)
{
if (!generateDatabaseDefinitions || !SaxSystem.DebugMode || SessionContext == null) return;
if (!StringUtil.EmptyOnNull(EnvironmentName).EqualsCI("Development")) return;
ISacsDatabase database = GetCoreDatabase(sessionContext);
if (database == null || StringUtil.IsEmpty(database.ConnectionString)) return;
// Generate database definitions.
SaxDbDfnGenerator.Generate(database.ConnectionString);
//SaUpdateLogDbDfnGenerator.Generate(database.ConnectionString);
}
//-------------------------------------------------------------------
private void populateRegionsFromDataFile(SaxSessionContext sessionContext)
{
using (SaxInternals internals = new SaxInternals(sessionContext))
{
SaxReferenceMgr referenceMgr = internals.ReferenceMgr;
String regionsData = SaxResourceHelper.GetTextResource(SaxResourceHelper.TextResource.Data_CountriesWithRegions_Txt);
String[] regionDataValues = regionsData.Split("\n");
SysId parentRegionId = null;
for (int idx = 0; idx < regionDataValues.Length; idx++)
{
String regionData = regionDataValues[idx].ReplaceAll("\r", "");
if (StringUtil.IsEmpty(regionData)) continue;
if (regionData.Length > 3 && regionData[2] == ':')
{
String iso2Code = regionData.Substring(0, 2);
String name = regionData.Substring(3);
SaxRegion region = getRegionByName(referenceMgr, name);
if (region == null)
{
region = referenceMgr.CreateRegion();
region.RegionType = Sax.RegionType.Country;
region.CountryIso2Code = iso2Code;
region.Name = name;
region.RegionIdParent = parentRegionId;
if (!referenceMgr.InsertRegion(region, new DbUpdateInfo(new SysId(), "", "")))
{
SaLogger.Error("Failed to insert region - " + referenceMgr.ErrorStateMessage);
return;
}
}
}
else
{
// Sub continent.
SaxRegion region = getRegionByName(referenceMgr, regionData);
if (region == null)
{
region = referenceMgr.CreateRegion();
region.RegionType = Sax.RegionType.SubContinent;
region.Name = regionData;
if (!referenceMgr.InsertRegion(region, new DbUpdateInfo(new SysId(), "", "")))
{
SaLogger.Error("Failed to insert region - " + referenceMgr.ErrorStateMessage);
return;
}
}
parentRegionId = region.Id;
}
}
}
}
private SaxRegion getRegionByName(SaxReferenceMgr referenceMgr, String name)
{
SaxRegion.Params prmRegion = new SaxRegion.Params();
prmRegion.NameFull = name;
return referenceMgr.GetRegion(prmRegion);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,47 @@
using Microsoft.Extensions.Localization;
using Saxonite.Net.Core.Web.Base.App;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web
{
// <summary>
/// Dummy classes to group shared resources - named as per '.resx' file in root namespace.
/// <para>Required to create the IStringLocalizer instance using the type from the class.</para>
/// <para>Name of the class is used as the prefix for localisation filenames.</para>
/// <para>Resource files are in the Localization ResourcesPath in Startup.cs.</para>
/// </summary>
public class SaxAdminProperties { }
// Class must be in Root/Default Namespace to reference properties with same name.
// NB 'Assembly name' must be the same as 'Default Namespace' until 'Root Namespace' can be set (.Net Core 2.1?).
// Ref. Project Properties - Application.
}
namespace Saxonite.Net.Core.Web.Admin.App
{
//=======================================================================
public class SaxLocaliser_Admin : SaxLocaliser
{
//-------------------------------------------------------------------
// Data members.
private readonly IStringLocalizer<SaxAdminProperties> adminProperties;
//-------------------------------------------------------------------
/// <summary>
/// Localisers are supplied using construction injection.
/// </summary>
public SaxLocaliser_Admin(IStringLocalizer<SaxAdminProperties> webAppProperties)
{
this.adminProperties = webAppProperties;
}
//-------------------------------------------------------------------
// Properties.
public override IStringLocalizer MainProperties { get { return adminProperties; } }
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,37 @@
using Newtonsoft.Json;
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.App
{
//=======================================================================
public class SaxSessionData_Admin : SaxSessionData_Web
{
//-------------------------------------------------------------------
// Construction.
[JsonConstructor]
private SaxSessionData_Admin(SaxSessionContext sessionContext) : this(sessionContext, null, null) { }
/// <summary>
/// SessionData construction.
/// <para>SessionContext and UserAccount must be supplied.</para>
/// <para>Organisation is optional.</para>
/// </summary>
public SaxSessionData_Admin(SaxSessionContext sessionContext, SaxUserAccount userAccount, List<SaxUserRole> userRoles) :
base(sessionContext, userAccount, userRoles)
{
}
//-------------------------------------------------------------------
// Properties.
/// <summary>User - JSON deserialisation route.</summary>
public new UserData User { get { return base.User; } set { SetUser(value); } }
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,71 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Saxonite.Net.Core.Web.Base.App;
using Saxonite.Net.Standard.Base.App;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Admin.App
{
//=======================================================================
public class SaxStartup_Admin : SaxStartup
{
//-------------------------------------------------------------------
// Data members.
private SaxApplication_Admin application = null;
//-------------------------------------------------------------------
// Construction.
public SaxStartup_Admin(IConfiguration configuration) : base(configuration)
{
// Initialise application.
application = new SaxApplication_Admin(Configuration);
}
//-------------------------------------------------------------------
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Common configuration.
ConfigureServicesConfig configureServicesConfig = new ConfigureServicesConfig();
configureServicesConfig.services = services;
configureServicesConfig.sessionCookieName = Sax.Config.Admin.Session.Cookie.Name;
configureServicesConfig.sessionCookieHttpOnly = Sax.Config.Admin.Session.Cookie.HttpOnly;
configureServicesConfig.sessionIdleTimeout = Sax.Config.Admin.Session.Cookie.IdleTimeout;
ConfigureServices(configureServicesConfig);
// Application localisation.
services.AddSingleton<SaxLocaliser, SaxLocaliser_Admin>();
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = Microsoft.AspNetCore.Http.SameSiteMode.None;
});
}
//-------------------------------------------------------------------
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostApplicationLifetime hostApplicationLifetime, IWebHostEnvironment env)
{
application.InitWebHostEnvironment(env);
// Common configuration.
ConfigureConfig configureConfig = new ConfigureConfig();
configureConfig.app = app;
configureConfig.hostApplicationLifetime = hostApplicationLifetime;
configureConfig.env = env;
Configure(configureConfig);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,45 @@
using Saxonite.Net.Core.Web.Admin.App;
using Saxonite.Net.Core.Web.Base.Consoles;
using Saxonite.Net.Standard.Base.App;
using Saxonite.Net.Standard.Base.Consoles;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Admin.Consoles
{
//=======================================================================
public class SaxConsoleHelper_WebAdmin : SaxConsoleHelper_Web
{
//-------------------------------------------------------------------
// Data members:
private readonly SaxApplication_Admin app;
//-------------------------------------------------------------------
// Construction.
public SaxConsoleHelper_WebAdmin(SaxApplication_Admin app)
{
this.app = app;
}
//-------------------------------------------------------------------
// Data cache console.
public override SaxDataCacheConsole GetDataCacheConsole(SaxSessionContext sessionContext)
{
String fn = GetType().Name + ".GetDataCacheConsole";
throw new NotSupportedException(fn + " Data Cache Console not supported in Admin app.");
}
//-------------------------------------------------------------------
// Email console.
public override SaxEmailConsole CreateEmailConsole(SaxSessionContext sessionContext)
{
SaxEmailConsole emailConsole = new SaxEmailConsole_Web(app.EmailConfig);
return emailConsole;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,87 @@
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 class HomeController : SaxController_Admin
{
//-------------------------------------------------------------------
// Construction.
public HomeController(SaxLocaliser localiser, ILogger<HomeController> logger) :
base(localiser, logger)
{
}
//-------------------------------------------------------------------
/// <summary>
///
/// </summary>
/// <returns></returns>
[HttpGet]
public IActionResult Index()
{
String fn = GetType().Name + ".Index";
// Check for 'Keep Logged In' cookie.
// NB Only allowed if there isn't a current user session.
if (!ValidUserSession && SessionConfig.Security.SignIn.KeepLoggedIn.Allowed)
{
String encryptedParams = GetRequestCookie(CookieName_KeepLoggedIn);
KeepLoggedInParams keepLoggedInParams = ExtractKeepLoggedInParams(encryptedParams);
String logSuffix = keepLoggedInParams.GetValidParams().Concat("", " ", ".");
if (SysId.IsValid(keepLoggedInParams.UserAccountId))
{
SaxUserAccount userAccount = AccountMgr.GetUserAccountById(keepLoggedInParams.UserAccountId);
if (userAccount != null)
{
if (userAccount.IsStatusActive)
{
String errorMessage = "";
if (SignUserIn(userAccount, AccountMgr.GetRolesForUser(userAccount.Id), true, ref errorMessage))
SaLogger.Info(fn + " Keep Logged In cookie found." + logSuffix);
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);
}
}
// 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 { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,252 @@
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
//=======================================================================
partial class OrganisationController
{
//-------------------------------------------------------------------
// Organisation data.
public class OrganisationData_List
{
public String Id { get; set; }
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; }
public String StatusCode { get; set; }
public String StatusDesc { get; set; }
}
public class OrganisationData_Full : OrganisationData_List
{
}
private void PopulateOrganisationResponseData(SaxOrganisation organisation, OrganisationData_List data)
{
data.Id = SysId.ToString(organisation.Id);
data.Name = organisation.Name;
data.TypeCode = OrganisationTypes.GetCodeForType(organisation.OrganisationType);
data.TypeDesc = OrganisationTypes.GetDescForType(organisation.OrganisationType);
data.CategoryCode = OrganisationCategories.GetCodeForType(organisation.OrganisationCategory);
data.CategoryDesc = OrganisationCategories.GetDescForType(organisation.OrganisationCategory);
data.MemberCount = AccountMgr.GetOrganisationMemberCount(organisation.Id);
data.StatusCode = organisation.StatusCode;
data.StatusDesc = organisation.StatusDesc;
}
private void PopulateOrganisationResponseData(SaxOrganisation organisation, OrganisationData_Full data)
{
PopulateOrganisationResponseData(organisation, data as OrganisationData_List);
}
//-------------------------------------------------------------------
// Get Organisation List ajax request.
//-------------------------------------------------------------------
public class AjaxResponse_OrganisationList : AjaxResponse_Data
{
public List<OrganisationData_List> Organisations { get; } = new List<OrganisationData_List>();
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Organisation/Ajax/GetList")]
public IActionResult Ajax_GetList(Model_Organisation model)
{
String fn = GetType().Name + ".Ajax_GetList";
String messageTitle = Localiser["organisation.caption.organisations"];
AjaxResponse_OrganisationList responseData = new AjaxResponse_OrganisationList();
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Item filter.
SaxOrganisation.Params prmOrganisation = new SaxOrganisation.Params();
prmOrganisation.OrganisationFilter = model.Filter.Organisation;
prmOrganisation.StatusCat = SaxOrganisation.StatusCat.NotDeleted;
model.DataItemTotal = (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 colPhoneMain = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.PhoneMain);
ColumnDfn colPhoneAlt = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.PhoneAlt);
ColumnDfn colUserCount = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.User_Count);
ColumnDfn colMemberCount = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.Member_Count);
ColumnDfn colStatus = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.Status);
List<ColumnDfn> columns = new List<ColumnDfn>()
{
colId,
colName,
colOrganisationType,
colOrganisationCategory,
colPhoneMain,
colPhoneAlt,
colUserCount,
colMemberCount,
colStatus
};
// Query items.
model.DataItemCount = 0;
using (DbResultSet resultSet = AccountMgr.ExecuteOrganisationQuery(columns, prmOrganisation))
{
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
{
model.DataItemCount++;
NamedValue.Map values = resultSet.GetRecordValues();
OrganisationData_List organisationData = new OrganisationData_List()
{
Id = values.GetString(colId.DataName),
Name = values.GetString(colName.DataName),
TypeCode = values.GetString(colOrganisationType.DataName),
TypeDesc = OrganisationTypes.GetDescForCode(values.GetString(colOrganisationType.DataName)),
CategoryCode = values.GetString(colOrganisationCategory.DataName),
CategoryDesc = OrganisationCategories.GetDescForCode(values.GetString(colOrganisationCategory.DataName)),
MemberCount = StringUtil.ToLong(values.GetString(colMemberCount.DataName)),
StatusCode = values.GetString(colStatus.DataName),
StatusDesc = SacsSystem.GetStatusDescForCode(values.GetString(colStatus.DataName))
};
responseData.Organisations.Add(organisationData);
}
}
// Complete response.
responseData.SetPaging(model);
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Get Organisation ajax request.
//-------------------------------------------------------------------
public class AjaxResponse_Organisation : AjaxResponse_Data
{
public OrganisationData_Full organisation { get; } = new OrganisationData_Full();
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Organisation/Ajax/GetItem")]
public IActionResult Ajax_GetItem(Model_Organisation model)
{
String fn = GetType().Name + ".Ajax_GetItem";
String messageTitle = Localiser["organisation.caption.organisation"];
AjaxResponse_Organisation responseData = new AjaxResponse_Organisation();
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Identify item.
SysId organisationId = new SysId(StringUtil.ToLong(model.OrganisationId));
if (!SysId.IsValid(organisationId))
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.specified"]));
// Retrieve item.
SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationId);
if (organisation == null)
{
SaLogger.Warn(fn + " Failed to retrieve organisation for Id: '" + model.OrganisationId + "'");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Construct response.
PopulateOrganisationResponseData(organisation, 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["organisation.caption.organisation"];
AjaxResponse_Organisation responseData = new AjaxResponse_Organisation();
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Validate item properties.
model.ValidateOrganisationData(responseData);
if (responseData.HasControlErrors())
return Json(responseData);
// Retrieve existing item.
SysId organisationId = new SysId(StringUtil.ToLong(model.OrganisationId));
SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationId);
if (organisation == null && SysId.IsValid(organisationId))
{
SaLogger.Warn(fn + " Failed to retrieve organisation for Id: '" + model.OrganisationId + "'");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Create new item if required.
if (organisation == null)
organisation = AccountMgr.CreateOrganisation();
// 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.OrganisationStatusCode != null)
organisation.Status = model.OrganisationStatus;
// Store item.
if (!SysId.IsValid(organisation.Id))
{
if (!AccountMgr.InsertOrganisation(organisation, DbUpdateInfo))
{
SaLogger.Warn(fn + " Failed to insert organisation: " + AccountMgr.ErrorStateMessage);
return Json(responseData.SetMessage(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.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + AccountMgr.ErrorStateMessage));
}
}
// Construct response.
PopulateOrganisationResponseData(organisation, responseData.organisation);
// Complete response.
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,326 @@
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 - Members - Ajax
//=======================================================================
partial class OrganisationController
{
//-------------------------------------------------------------------
// Member data.
public class MemberData_List
{
public String Id { get; set; }
public String UserAccountId { get; set; }
public String EmailAddress { get; set; }
public String DisplayName { get; set; }
public String OrganisationId { get; set; }
public String OrganisationName { get; set; }
public List<String> RoleCodes { get; } = new List<string>();
public int RoleCount { get; set; }
public String StatusCode { get; set; }
public String StatusDesc { get; set; }
}
public class MemberData_Full : MemberData_List
{
}
private void PopulateMemberResponseData(MemberData_List data, SaxUserOrganisation userOrganisation, SaxUserAccount userAccount, SaxOrganisation organisation)
{
data.Id = SysId.ToString(userOrganisation.Id);
data.UserAccountId = SysId.ToString(userAccount.Id);
data.EmailAddress = userAccount.EmailAddress;
data.DisplayName = userAccount.GetDisplayName();
data.OrganisationId = SysId.ToString(organisation.Id);
data.OrganisationName = organisation.Name;
// Exclude 'Member' role.
foreach (Sax.OrganisationRole organisationRole in userOrganisation.OrganisationRoles.FindAll(item => item != Sax.OrganisationRole.Member))
data.RoleCodes.Add(OrganisationRoles.GetCodeForType(organisationRole));
data.RoleCount = userOrganisation.OrganisationRoles.Count - 1;
data.StatusCode = organisation.StatusCode;
data.StatusDesc = organisation.StatusDesc;
}
private void PopulateMemberResponseData(MemberData_Full data, SaxUserOrganisation userOrganisation, SaxUserAccount userAccount, SaxOrganisation organisation)
{
PopulateMemberResponseData(data as MemberData_List, userOrganisation, userAccount, organisation);
}
//-------------------------------------------------------------------
// Get Organisation Member List ajax request.
//-------------------------------------------------------------------
public class AjaxResponse_MemberList : AjaxResponse_Data
{
public List<MemberData_List> Members { get; } = new List<MemberData_List>();
public List<CodeDesc> MemberRoles { get; } = new List<CodeDesc>();
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Organisation/Ajax/GetMemberList")]
public IActionResult Ajax_GetMemberList(Model_OrganisationMembers model)
{
String fn = GetType().Name + ".Ajax_GetMemberList";
String messageTitle = Localiser["organisation.caption.members"];
AjaxResponse_MemberList responseData = new AjaxResponse_MemberList();
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Organisation Id filter is mandatory.
SysId filterOrganisationId = new SysId(StringUtil.ToLong(model.Filter.OrganisationId));
if (!SysId.IsValid(filterOrganisationId))
{
SaLogger.Warn(fn + " Request to retrieve members without valid organisation Id.");
return Json(responseData.SetMessage(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 OrganisationRoles.FindAll(item => item.Type != (int)Sax.OrganisationRole.Member))
responseData.MemberRoles.Add(organisationRole.Code, Localiser.GetOrganisationRoleCaption((Sax.OrganisationRole)organisationRole.Type));
// Item filter.
SaxUserOrganisation.Params prmUserOrganisation = new SaxUserOrganisation.Params();
prmUserOrganisation.OrganisationIdsIncluded.Add(filterOrganisationId);
prmUserOrganisation.OrganisationMembersOnly = true;
prmUserOrganisation.StatusCat = SaxUserOrganisation.StatusCat.NotDeleted;
model.DataItemTotal = (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 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,
colOrganisationId,
colOrganisationName,
colRoleCodes,
colRoleCount,
colStatus
};
// Query items.
model.DataItemCount = 0;
using (DbResultSet resultSet = AccountMgr.ExecuteUserOrganisationQuery(columns, prmUserOrganisation))
{
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
{
model.DataItemCount++;
NamedValue.Map values = resultSet.GetRecordValues();
MemberData_List memberData = new MemberData_List()
{
Id = values.GetString(colId.DataName),
UserAccountId = values.GetString(colUserAccountId.DataName),
EmailAddress = values.GetString(colUserEmailAddress.DataName),
DisplayName = AccountMgr.GetUserOrganisationDataValue(colUserDisplayName, values).ToString(),
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))
};
responseData.Members.Add(memberData);
}
}
// Complete response.
responseData.SetPaging(model);
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Get Organisation Member ajax request.
//-------------------------------------------------------------------
public class AjaxResponse_Member : AjaxResponse_Data
{
public MemberData_Full Member { get; } = new MemberData_Full();
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Organisation/Ajax/GetMember")]
public IActionResult Ajax_GetMember(Model_OrganisationMembers model)
{
String fn = GetType().Name + ".Ajax_GetItem";
String messageTitle = Localiser["organisation.caption.member"];
AjaxResponse_Member responseData = new AjaxResponse_Member();
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Identify item.
SysId userOrganisationId = new SysId(StringUtil.ToLong(model.UserOrganisationId));
if (!SysId.IsValid(userOrganisationId))
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.specified"]));
// Retrieve item.
SaxUserOrganisation userOrganisation = AccountMgr.GetUserOrganisationById(userOrganisationId);
if (userOrganisation == null)
{
SaLogger.Warn(fn + " Failed to retrieve User Organisation for Id: '" + model.UserOrganisationId + "'");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Retrieve User Account.
SaxUserAccount userAccount = AccountMgr.GetUserAccountById(userOrganisation.UserAccountId);
if (userAccount == null)
{
SaLogger.Warn(fn + " Failed to retrieve user account for Id: '" + userOrganisation.UserAccountId + "'");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Retrieve Organisation.
SaxOrganisation organisation = AccountMgr.GetOrganisationById(userOrganisation.OrganisationId);
if (organisation == null)
{
SaLogger.Warn(fn + " Failed to retrieve organisation for Id: '" + userOrganisation.OrganisationId + "'");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Construct response.
PopulateMemberResponseData(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 fn = GetType().Name + ".Ajax_StoreMember";
String messageTitle = Localiser["organisation.caption.member"];
AjaxResponse_Member responseData = new AjaxResponse_Member();
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Validate item properties.
model.ValidateOrganisationMemberData(responseData);
if (responseData.HasControlErrors())
return Json(responseData);
// User account.
SysId userAccountId = new SysId(StringUtil.ToLong(model.MemberUserAccountId));
SaxUserAccount userAccount = null;
if (!SysId.IsValid(userAccountId) && !StringUtil.IsEmpty(model.MemberUserEmailAddress))
{
userAccount = AccountMgr.GetUserAccountByEmailAddress(model.MemberUserEmailAddress);
userAccountId = userAccount?.Id ?? new SysId();
}
if (!SysId.IsValid(userAccountId))
{
SaLogger.Warn(fn + " User Account must be identified");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.data.incomplete"]));
}
// Organisation.
SysId organisationId = new SysId(StringUtil.ToLong(model.MemberOrganisationId));
SaxOrganisation organisation = null;
if (!SysId.IsValid(organisationId))
{
SaLogger.Warn(fn + " Organisation Id must be specified");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.data.incomplete"]));
}
// Retrieve existing item.
SysId userOrganisationId = new SysId(StringUtil.ToLong(model.UserOrganisationId));
SaxUserOrganisation userOrganisation = AccountMgr.GetUserOrganisationById(userOrganisationId);
if (userOrganisation == null && SysId.IsValid(userOrganisationId))
{
SaLogger.Warn(fn + " Failed to retrieve user organisation for Id: '" + userOrganisationId + "'");
return Json(responseData.SetMessage(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(fn + " Inconsistent user organisation properties. UserAccountId: " + userOrganisation.UserAccountId + " vs " + userAccountId + ". OrganisationId: " + userOrganisation.OrganisationId + " vs " + organisationId + ".");
return Json(responseData.SetMessage(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)
// Organisation not found for user.
userOrganisation = AccountMgr.CreateUserOrganisation(userAccountId, organisationId);
}
// Retrieve associated items.
if (userAccount == null)
userAccount = AccountMgr.GetUserAccountById(userAccountId);
if (organisation == null)
organisation = AccountMgr.GetOrganisationById(organisationId);
// Populate item properties.
if (model.MemberRoleCodes != null)
userOrganisation.OrganisationRoles = SaxUserOrganisation.GetOrganisationRolesFromCodes(model.GetMemberRoleCodes());
if (!userOrganisation.OrganisationRoles.Contains(Sax.OrganisationRole.Member))
userOrganisation.OrganisationRoles.Add(Sax.OrganisationRole.Member);
userOrganisation.Status = SaStatus.Active;
// Store item.
if (!SysId.IsValid(userOrganisation.Id))
{
if (!AccountMgr.InsertUserOrganisation(userOrganisation, DbUpdateInfo))
{
SaLogger.Warn(fn + " Failed to insert user organisation: " + AccountMgr.ErrorStateMessage);
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.insert.failed"] + "\n\n" + AccountMgr.ErrorStateMessage));
}
}
else
{
if (!AccountMgr.UpdateUserOrganisation(userOrganisation, DbUpdateInfo))
{
SaLogger.Warn(fn + " Failed to update user organisation [Id:" + SysId.ToString(userOrganisation.Id) + "]: " + AccountMgr.ErrorStateMessage);
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + AccountMgr.ErrorStateMessage));
}
}
// Construct response.
PopulateMemberResponseData(responseData.Member, userOrganisation, userAccount, organisation);
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,63 @@
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()
{
// Validate session.
if (!ValidUserSession)
return RedirectToAction("", "");
Model_Organisation model = new Model_Organisation(Localiser);
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)
return RedirectToAction("", "");
// Retrieve organisation.
SaxOrganisation organisation = AccountMgr.GetOrganisationById(new SysId(StringUtil.ToLong(model.Filter.OrganisationId)));
if (organisation == null)
RedirectToAction("", "Organisation");
model.MemberOrganisationName = organisation?.Name;
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,241 @@
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 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.
public class RegionData_List
{
public String Id { get; set; }
public String RegionTypeCode { get; set; }
public String RegionTypeDesc { get; set; }
public String CountryIso2Code { get; set; }
public String Name { get; set; }
public String ParentName { get; set; }
public String StatusCode { get; set; }
public String StatusDesc { get; set; }
}
public class RegionData_Full : RegionData_List
{
}
private void PopulateRegionResponseData(SaxRegion region, RegionData_List data)
{
data.Id = SysId.ToString(region.Id);
data.RegionTypeCode = RegionTypes.GetCodeForType(region.RegionType);
data.RegionTypeDesc = RegionTypes.GetDescForType(region.RegionType);
data.CountryIso2Code = region.CountryIso2Code;
data.Name = region.Name;
data.StatusCode = region.StatusCode;
data.StatusDesc = region.StatusDesc;
}
private void PopulateRegionResponseData(SaxRegion region, RegionData_Full data)
{
PopulateRegionResponseData(region, data as RegionData_List);
}
//-------------------------------------------------------------------
// Get Region List ajax request.
//-------------------------------------------------------------------
public class AjaxResponse_RegionList : AjaxResponse_Data
{
public List<RegionData_List> Regions { get; } = new List<RegionData_List>();
}
//-------------------------------------------------------------------
[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();
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// 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.DataItemTotal = (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.
model.DataItemCount = 0;
using (DbResultSet resultSet = ReferenceMgr.ExecuteRegionQuery(columns, prmRegion))
{
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
{
model.DataItemCount++;
NamedValue.Map values = resultSet.GetRecordValues();
Sax.RegionType regionType = (Sax.RegionType)values.GetLong(colType.DataName);
RegionData_List regionData = new RegionData_List()
{
Id = values.GetString(colId.DataName),
RegionTypeCode = RegionTypes.GetCodeForType(regionType),
RegionTypeDesc = RegionTypes.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);
}
}
// Complete response.
responseData.SetPaging(model);
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Get Region ajax request.
//-------------------------------------------------------------------
public class AjaxResponse_Region : AjaxResponse_Data
{
public RegionData_Full region { get; } = new RegionData_Full();
}
//-------------------------------------------------------------------
[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();
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Identify item.
if (!SysId.IsValid(model.RegionId))
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.specified"]));
// Retrieve item.
SaxRegion region = ReferenceMgr.GetRegionById(model.RegionId);
if (region == null)
{
SaLogger.Warn(fn + " Failed to retrieve region for Id: '" + model.RegionIdValue + "'");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Construct response.
PopulateRegionResponseData(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();
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Validate item properties.
model.ValidateRegionData(responseData);
if (responseData.HasControlErrors())
return Json(responseData);
// Retrieve existing item.
SaxRegion region = ReferenceMgr.GetRegionById(model.RegionId);
if (region == null && SysId.IsValid(model.RegionId))
{
SaLogger.Warn(fn + " Failed to retrieve region for Id: '" + model.RegionIdValue + "'");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Create new item if required.
if (region == null)
region = ReferenceMgr.CreateRegion();
// Populate item properties.
if (model.RegionCountryIso2Code != null)
region.CountryIso2Code = model.RegionCountryIso2Code;
if (model.RegionName != null)
region.Name = model.RegionName;
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.SetMessage(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.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + ReferenceMgr.ErrorStateMessage));
}
}
// Construct response.
PopulateRegionResponseData(region, responseData.region);
// Complete response.
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,39 @@
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)
return RedirectToAction("", "");
Model_Region model = new Model_Region(Localiser);
return View(model);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,186 @@
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.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;
namespace Saxonite.Net.Core.Web.Admin.Controllers
{
//=======================================================================
public class SaxController_Admin : SaxController_Base
{
//-------------------------------------------------------------------
// 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);
}
//-------------------------------------------------------------------
// 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, List<SaxUserRole> userRoles, bool keepLoggedIn, ref String errorMessage)
{
String fn = GetType().Name + ".SignUserIn";
// 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.failed"];
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.failed"];
return false;
}
// 'Keep logged in' can be switched off by the application configuration.
keepLoggedIn = App.SessionConfig.Security.SignIn.KeepLoggedIn.Allowed && keepLoggedIn;
// Log user in.
userAccount.KeepLoggedIn_Admin = keepLoggedIn;
userAccount.LastLoginAt = DateTime.Now;
userAccount.InvalidLoginCount = 0;
userAccount.LastInvalidLoginAt = DateTime.MinValue;
if (!AccountMgr.UpdateUserAccount(userAccount, DbUpdateInfo))
{
SaLogger.Warn(fn + " Failed to update account for " + userAccount.EmailAddress + ": " + AccountMgr.ErrorStateMessage);
errorMessage = Localiser["security.msg.signin.failed"];
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 (userAccount.KeepLoggedIn_Admin)
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>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, SessionConfig.Security.SignIn.KeepLoggedIn.CookieExpiresDays));
SetResponseCookie(CookieName_KeepLoggedIn, keepLoggedInCookieValue, DateTime.Now.AddDays(cookieExpiresDays), true);
SaLogger.Info(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(CookieName_KeepLoggedIn))) return;
// Clear cookie by setting expiry date in the past.
SetResponseCookie(CookieName_KeepLoggedIn, "", DateTime.Today.AddDays(-7), true);
SaLogger.Info(GetType().Name + ".ClearKeepLoggedInCookie '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.
// NB
public static String CookieName_KeepLoggedIn = "SXKLI";
public static String Base64Key_KeepLoggedIn = SacsEncryptionHelper.EncodePlainKey("<=Saxonite^{kEEp+1Ogg3d-IN}~32*>", SacsEncryptionHelper.KeyLength);
public static String Base64IV_KeepLoggedIn_Generated = SacsEncryptionHelper.InitSymmetricEncryptionIV(Base64Key_KeepLoggedIn);
public static String Base64IV_KeepLoggedIn = "YOX10FIPp1zM1pg5U6fW7A==";
//-------------------------------------------------------------------
// 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, Base64IV_KeepLoggedIn, Base64Key_KeepLoggedIn);
}
//-------------------------------------------------------------------
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, Base64IV_KeepLoggedIn, Base64Key_KeepLoggedIn);
keepLoggedInParams.UserAccountId = new SysId(StringUtil.ToInt(linkParams.Get(ParamName_KeepLoggedIn_UserAccountId)));
}
if (DebugMode) SaLogger.Info(GetType().Name + ".ExtractKeepLoggedInParams UserAccountId: " + SysId.ToString(keepLoggedInParams.UserAccountId));
return keepLoggedInParams;
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,448 @@
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.
//-------------------------------------------------------------------
public class AjaxResponse_Signin : AjaxResponse_Data
{
}
// 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();
String messageTitle = Localiser["signin.caption"];
// Validate Sign-in data.
model.ValidateSignInData(responseData);
if (responseData.HasControlErrors())
return Json(responseData);
// Get user account.
SaxUserAccount userAccount = GetUserAccountForSignin(model, responseData);
if (responseData.HasControlErrors())
return Json(responseData);
String errorMessage = "";
if (!SignUserIn(userAccount, AccountMgr.GetRolesForUser(userAccount.Id), model.KeepMeSignedIn, ref errorMessage))
{
return Json(responseData.SetControlError(nameof(model.EmailAddress), errorMessage));
}
// Construct response.
responseData.Success = SessionData_Admin?.ValidUserSession ?? false;
return Json(responseData);
}
//-------------------------------------------------------------------
// Keep Logged In.
//-------------------------------------------------------------------
public 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";
AjaxResponse_KeepLoggedIn responseData = new AjaxResponse_KeepLoggedIn();
if (!ValidUserSession)
return Json(responseData.SetGoHome());
if (!SessionConfig.Security.SignIn.KeepLoggedIn.Allowed)
return Json(responseData);
// Resend the cookie to extend the expiry date if it is currently populated.
if (!StringUtil.IsEmpty(GetRequestCookie(CookieName_KeepLoggedIn)))
SetKeepLoggedInCookie();
// Construct response.
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
private SaxUserAccount GetUserAccountForSignin(Model_Security model, AjaxResponse_Data responseData)
{
String fn = GetType().Name + ".GetUserAccountForSignin";
// Identify user account.
SaxUserAccount userAccount = null;
SaxUserAccount.Params prmUserAccount = new SaxUserAccount.Params();
prmUserAccount.EmailAddress = model.EmailAddress;
prmUserAccount.StatusCat = SaxUserAccount.StatusCat.Any;
foreach (SaxUserAccount userAccountDuplicate in AccountMgr.GetUserAccountList(prmUserAccount))
{
// Account must be active to allow sign-in.
// NB Debug testing allows 'Awaiting' accounts to sign-in.
if ((SaxSystem.DebugMode && userAccountDuplicate.Status == SaStatus.Awaiting) || userAccountDuplicate.IsStatusActive)
{
userAccount = userAccountDuplicate;
break;
}
SaLogger.Warn(fn + " Attempt to sign-in to User Account for " + model.EmailAddress + " with status " + SaxSystem.GetStatusDesc(userAccount.Status), true);
}
if (userAccount != null)
{
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;
}
}
if (userAccount == null && !responseData.HasControlErrors())
responseData.SetControlError(nameof(model.EmailAddress), model.Localiser["security.msg.signin.invalid"]);
// Admin - Verify that user has System Admin role.
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;
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.
//-------------------------------------------------------------------
public class AjaxResponse_SignOut : AjaxResponse_Data
{
}
// 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();
String messageTitle = Localiser["signin.caption.signout"];
// User must have a valid session.
if (!ValidUserSession)
return Json(responseData.SetSessionExpired(messageTitle, Localiser["security.msg.warn.sessionExpired"]));
// Retrieve user account - in case sign-out needs to be recorded.
SaxUserAccount userAccount = AccountMgr.GetUserAccountById(SessionData_Admin.User.UserAccountId);
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.SetMessage(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.
//-------------------------------------------------------------------
public class AjaxResponse_PasswordScore : AjaxResponse_Data
{
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.
//-------------------------------------------------------------------
public class AjaxResponse_ChangePassword : AjaxResponse_Data
{
}
// 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();
String messageTitle = Localiser["security.caption.change.password"];
// 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);
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);
}
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.SetMessage(messageTitle, Localiser["security.msg.request.password.saved"]);
return Json(responseData);
}
//-------------------------------------------------------------------
// Request Password request.
//-------------------------------------------------------------------
public class AjaxResponse_RequestPassword : AjaxResponse_Data
{
/// <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();
String messageTitle = Localiser["security.caption.request.password"];
// Validate data.
model.ValidateRequestPasswordData(responseData);
if (responseData.HasControlErrors())
return Json(responseData);
// Retrieve user account.
SaxUserAccount userAccount = AccountMgr.GetUserAccountByEmailAddress(model.EmailAddress);
if (userAccount != null && (userAccount.IsStatusActive || DebugMode))
{
// Send password request email.
SaEmailData emailData = new SaEmailData();
emailData.Config = EmailConfig;
emailData.AddressesTo.Add(model.EmailAddress);
emailData.Subject = Localiser["security.caption.request.password"];
PopulateRequestPasswordEmailBody(emailData, userAccount);
if (EmailConsole.SendEmail(emailData))
SaLogger.Info(fn + " Password request email sent to " + model.EmailAddress, true);
else SaLogger.Warn(fn + " Failed to send password request email to " + model.EmailAddress);
}
else
{
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");
}
// Construct response.
responseData.Success = true;
responseData.SuccessMsg = Localiser["security.msg.request.password.email.sent"];
return Json(responseData);
}
private void PopulateRequestPasswordEmailBody(SaEmailData emailData, SaxUserAccount userAccount)
{
// Will need to handle culture (language/country) as and when introduced.
emailData.MessageBody = SaxResourceHelper.GetTextResource(SaxResourceHelper.TextResource.Email_RequestPassword_Html);
emailData.HtmlBody = true;
// Replace dynamic tags.
EmailConsole.ReplaceDynamicEmailBodyTags(emailData, null, userAccount);
// Replace dynamic tag for link back to change password page.
SaEmailConsole.ReplaceDynamicEmailTag(emailData, "[#Link.RequestPassword]", GenerateRequestPasswordLink(userAccount.Id, userAccount.EmailAddress, ""));
SaEmailConsole.ReplaceDynamicEmailTag(emailData, "[#Link.ExpiresMinutes]", GetRequestPasswordEmailLinkExpiresMinutes().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";
// NB IV value is generated from the key and fixed so that it doesn't change when the application restarts.
// IV Must be regenerated if key is changed.
private static String Base64Key_RequestPassword { get; } = SacsEncryptionHelper.EncodePlainKey("{~Saxonite@@Request^Password=}", SacsEncryptionHelper.KeyLength);
private static String Base64IV_RequestPassword_Generated { get; } = SacsEncryptionHelper.InitSymmetricEncryptionIV(Base64Key_RequestPassword);
private static String Base64IV_RequestPassword { get; } = "1VAWYTW6d72n/6HDNhbqow==";
private String GenerateRequestPasswordLink(SysId userAccountId, String emailAddress, String requestPasswordToken)
{
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()), DateUtil.Format_XmlDateTime));
parameters.Add(ParamName_Token, requestPasswordToken);
String link =
GetRedirectLocation(RedirectLocation.WebAppBase) + "Security/ChangePassword?" +
ParamName_RequestPasswordEncrypted + "=" + HttpUtility.UrlEncode(EncryptParameters(parameters, Base64IV_RequestPassword, Base64Key_RequestPassword));
return link;
}
private int GetRequestPasswordEmailLinkExpiresMinutes()
{
int expiresMinutes = SessionConfig.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));
}
public 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, Base64IV_RequestPassword, Base64Key_RequestPassword);
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,121 @@
using Microsoft.AspNetCore.Http;
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.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.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 (requestPasswordParams.IsValid(userAccount))
{
model.rpe = encryptedParams;
validSessionRequired = false;
}
else SaLogger.Warn(fn + " 'Request Password' parameters supplied but invalid. " + 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,219 @@
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 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 SportController
{
//-------------------------------------------------------------------
// Sport data.
public class SportData_List
{
public String Id { get; set; }
public String Name { get; set; }
public String StatusCode { get; set; }
public String StatusDesc { get; set; }
}
public class SportData_Full : SportData_List
{
}
private void PopulateSportResponseData(SaxSport sport, SportData_List data)
{
data.Id = SysId.ToString(sport.Id);
data.Name = sport.Name;
data.StatusCode = sport.StatusCode;
data.StatusDesc = sport.StatusDesc;
}
private void PopulateSportResponseData(SaxSport sport, SportData_Full data)
{
PopulateSportResponseData(sport, data as SportData_List);
}
//-------------------------------------------------------------------
// Get Sport List ajax request.
//-------------------------------------------------------------------
public class AjaxResponse_SportList : AjaxResponse_Data
{
public List<SportData_List> Sports { get; } = new List<SportData_List>();
}
//-------------------------------------------------------------------
[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();
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Item filter.
SaxSport.Params prmSport = new SaxSport.Params();
prmSport.SportFilter = model.Filter.Sport;
prmSport.StatusCat = SaxSport.StatusCat.NotDeleted;
model.DataItemTotal = (ulong)ReferenceMgr.GetSportCount(prmSport);
model.PopulatePagingTo(prmSport.ResultsPaging);
// Item columns.
ColumnDfn colId = ReferenceMgr.GetSportColumnDfn(SaxSport.Column.Id);
ColumnDfn colName = ReferenceMgr.GetSportColumnDfn(SaxSport.Column.Name);
ColumnDfn colStatus = ReferenceMgr.GetSportColumnDfn(SaxSport.Column.Status);
List<ColumnDfn> columns = new List<ColumnDfn>()
{
colId,
colName,
colStatus
};
// Sort order.
prmSport.SortColumns.Add(colName);
// Query items.
model.DataItemCount = 0;
using (DbResultSet resultSet = ReferenceMgr.ExecuteSportQuery(columns, prmSport))
{
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
{
model.DataItemCount++;
NamedValue.Map values = resultSet.GetRecordValues();
SportData_List sportData = new SportData_List()
{
Id = values.GetString(colId.DataName),
Name = values.GetString(colName.DataName),
StatusCode = values.GetString(colStatus.DataName),
StatusDesc = SacsSystem.GetStatusDescForCode(values.GetString(colStatus.DataName))
};
responseData.Sports.Add(sportData);
}
}
// Complete response.
responseData.SetPaging(model);
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Get Sport ajax request.
//-------------------------------------------------------------------
public class AjaxResponse_Sport : AjaxResponse_Data
{
public SportData_Full sport { get; } = new SportData_Full();
}
//-------------------------------------------------------------------
[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();
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Identify item.
if (!SysId.IsValid(model.SportId))
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.specified"]));
// Retrieve item.
SaxSport sport = ReferenceMgr.GetSportById(model.SportId);
if (sport == null)
{
SaLogger.Warn(fn + " Failed to retrieve sport for Id: '" + model.SportIdValue + "'");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Construct response.
PopulateSportResponseData(sport, 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();
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Validate item properties.
model.ValidateSportData(responseData);
if (responseData.HasControlErrors())
return Json(responseData);
// Retrieve existing item.
SaxSport sport = ReferenceMgr.GetSportById(model.SportId);
if (sport == null && SysId.IsValid(model.SportId))
{
SaLogger.Warn(fn + " Failed to retrieve sport for Id: '" + model.SportIdValue + "'");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// Create new item if required.
if (sport == null)
sport = ReferenceMgr.CreateSport();
// Populate item properties.
if (model.SportName != null)
sport.Name = model.SportName;
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.SetMessage(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.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + ReferenceMgr.ErrorStateMessage));
}
}
// Construct response.
PopulateSportResponseData(sport, responseData.sport);
// Complete response.
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,39 @@
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)
return RedirectToAction("", "");
Model_Sport model = new Model_Sport(Localiser);
return View(model);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,318 @@
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.
public class UserData_List
{
public String Id { get; set; }
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 int UserRoleCount { get; set; }
public bool SystemAdmin { get; set; }
public String StatusCode { get; set; }
public String StatusDesc { get; set; }
}
public class UserData_Full : UserData_List
{
public List<String> userRoleCodes { get; } = new List<string>();
}
private void PopulateUserResponseData(UserData_List data, SaxUserAccount user, 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.UserRoleCount = userRoles?.GetUserRolesByStatus(SaStatus.Active).Count ?? 0;
data.StatusCode = user.StatusCode;
data.StatusDesc = user.StatusDesc;
}
private void PopulateUserResponseData(UserData_Full data, SaxUserAccount user, List<SaxUserRole> userRoles)
{
PopulateUserResponseData(data as UserData_List, user, userRoles);
if (userRoles != null)
{
foreach (SaxUserRole userRole in userRoles.GetUserRolesByStatus(SaStatus.Active))
data.userRoleCodes.Add(UserRoles.GetCodeForType(userRole.UserRole));
}
}
//-------------------------------------------------------------------
// Get User List ajax request.
//-------------------------------------------------------------------
public class AjaxResponse_UserList : AjaxResponse_Data
{
public List<UserData_List> Users { get; } = new List<UserData_List>();
public List<CodeDesc> UserRoles { get; } = new List<CodeDesc>();
}
//-------------------------------------------------------------------
[HttpPost]
[Route("User/Ajax/GetList")]
public IActionResult Ajax_GetList(Model_User model)
{
String fn = GetType().Name + ".Ajax_GetList";
String messageTitle = Localiser["user.caption.users"];
AjaxResponse_UserList responseData = new AjaxResponse_UserList();
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Reference data - user roles.
foreach (TypeCodeDesc userRole in UserRoles)
responseData.UserRoles.Add(userRole.Code, Localiser.GetUserRoleCaption((Sax.UserRole)userRole.Type));
// Item filter.
SaxUserAccount.Params prmUserAccount = new SaxUserAccount.Params();
prmUserAccount.UserAccountFilter = model.Filter.User;
prmUserAccount.StatusCat = SaxUserAccount.StatusCat.NotDeleted;
model.DataItemTotal = (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 colUserRoleCount = AccountMgr.GetUserAccountColumnDfn(SaxUserAccount.Column.UserRoleCount);
ColumnDfn colSystemAdminUser = AccountMgr.GetUserAccountColumnDfn(SaxUserAccount.Column.SystemAdminUser);
ColumnDfn colStatus = AccountMgr.GetUserAccountColumnDfn(SaxUserAccount.Column.Status);
List<ColumnDfn> columns = new List<ColumnDfn>()
{
colId,
colEmail,
colTitle,
colFirstName,
colLastName,
colUserRoleCount,
colSystemAdminUser,
colStatus
};
// Query items.
model.DataItemCount = 0;
using (DbResultSet resultSet = AccountMgr.ExecuteUserAccountQuery(columns, prmUserAccount))
{
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
{
model.DataItemCount++;
NamedValue.Map values = resultSet.GetRecordValues();
UserData_List userData = new UserData_List()
{
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),
UserRoleCount = StringUtil.ToInt(values.GetString(colUserRoleCount.DataName)),
SystemAdmin = StringUtil.ToBool(values.GetString(colSystemAdminUser.DataName)),
StatusCode = values.GetString(colStatus.DataName),
StatusDesc = SacsSystem.GetStatusDescForCode(values.GetString(colStatus.DataName))
};
userData.DisplayName = SaxUserAccount.GetDisplayName(userData.Title, userData.FirstName, userData.LastName);
responseData.Users.Add(userData);
}
}
// Complete response.
responseData.SetPaging(model);
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
// Get User ajax request.
//-------------------------------------------------------------------
public class AjaxResponse_User : AjaxResponse_Data
{
public UserData_Full user { get; } = new UserData_Full();
}
//-------------------------------------------------------------------
[HttpPost]
[Route("User/Ajax/GetItem")]
public IActionResult Ajax_GetItem(Model_User model)
{
String fn = GetType().Name + ".Ajax_GetItem";
String messageTitle = Localiser["user.caption.user"];
AjaxResponse_User responseData = new AjaxResponse_User();
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Identify item.
if (!SysId.IsValid(model.UserAccountId))
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.specified"]));
// Retrieve item.
SaxUserAccount user = AccountMgr.GetUserAccountById(model.UserAccountId);
if (user == null)
{
SaLogger.Warn(fn + " Failed to retrieve user for Id: '" + model.UserAccountIdValue + "'");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// User's roles.
// Construct response.
PopulateUserResponseData(responseData.user, user, GetUserRoles(user.Id).GetUserRolesByStatus(SaStatus.Active));
// Complete response.
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
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);
}
//-------------------------------------------------------------------
// Store User ajax request.
//-------------------------------------------------------------------
[HttpPost]
[Route("User/Ajax/StoreItem")]
public IActionResult Ajax_StoreItem(Model_User model)
{
String fn = GetType().Name + ".Ajax_StoreItem";
String messageTitle = Localiser["user.caption.user"];
AjaxResponse_User responseData = new AjaxResponse_User();
// Validate session.
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
// Validate item properties.
model.ValidateUserData(responseData);
if (responseData.HasControlErrors())
return Json(responseData);
// Retrieve existing item.
SaxUserAccount user = AccountMgr.GetUserAccountById(model.UserAccountId);
if (user == null && SysId.IsValid(model.UserAccountId))
{
SaLogger.Warn(fn + " Failed to retrieve user for Id: '" + model.UserAccountIdValue + "'");
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
}
// User roles, including deleted.
List<String> userRoleCodes = model.GetUserRoleCodes();
List<SaxUserRole> userRoles = new List<SaxUserRole>();
if (user != null) userRoles = GetUserRoles(user.Id, SaxUserRole.StatusCat.Any);
foreach (String userRoleCode in userRoleCodes)
{
// Role to activate.
Sax.UserRole role = (Sax.UserRole)UserRoles.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(UserRoles.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.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.SetMessage(messageTitle, Localiser["msg.error.item.insert.failed"] + "\n\n" + AccountMgr.ErrorStateMessage));
}
// Apply User Account Id to dependent items.
foreach (SaxUserRole userRole in (userRoles))
userRole.UserAccountId = user.Id;
}
else
{
if (!AccountMgr.UpdateUserAccount(user, DbUpdateInfo))
{
SaLogger.Warn(fn + " Failed to update user [Id:" + SysId.ToString(user.Id) + "]: " + AccountMgr.ErrorStateMessage);
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + AccountMgr.ErrorStateMessage));
}
}
// Associated data - User Roles.
foreach (SaxUserRole userRole in (userRoles))
{
// Store User Role.
if (!SysId.IsValid(userRole.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);
}
}
// Construct response.
PopulateUserResponseData(responseData.user, user, userRoles.GetUserRolesByStatus(SaStatus.Active));
// Complete response.
responseData.Success = true;
return Json(responseData);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,39 @@
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 UserController : SaxController_Admin
{
//-------------------------------------------------------------------
// Construction.
public UserController(SaxLocaliser localiser, ILogger<HomeController> logger) :
base(localiser, logger)
{
}
//-------------------------------------------------------------------
[HttpGet]
public IActionResult Index()
{
// Validate session.
if (!ValidUserSession)
return RedirectToAction("", "");
Model_User model = new Model_User(Localiser);
return View(model);
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,16 @@
using System;
namespace Saxonite.Net.Core.Web.Admin.Models
{
//=======================================================================
public class ErrorViewModel
{
//-------------------------------------------------------------------
public string RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,23 @@
using Saxonite.Net.Core.Web.Base.App;
using Saxonite.Net.Core.Web.Base.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Admin.Models
{
//=======================================================================
public class Model_Home : SaxModel_Admin
{
//-------------------------------------------------------------------
// Construction.
public Model_Home(SaxLocaliser localiser) :
base(localiser)
{
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,103 @@
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.Base.App;
using Saxonite.Net.Core.Web.Base.App;
using Saxonite.Net.Core.Web.Base.Controllers;
using Saxonite.Net.Core.Web.Base.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.Models
{
//=======================================================================
public class Model_Organisation : SaxModel_Admin
{
//-------------------------------------------------------------------
// Construction.
public Model_Organisation(SaxLocaliser localiser) :
base(localiser)
{
AdditionalFiles_JS.Add("js/sax-organisation.js");
}
//-------------------------------------------------------------------
// List configuration.
public class FilterData
{
public String Organisation { get; set; }
public String OrganisationTypeCode { get; set; }
public String OrganisationCategoryCode { get; set; }
}
public FilterData Filter { get; } = new FilterData();
public Sax.OrganisationType FilterOrganisationType
{
get { return (Sax.OrganisationType)OrganisationTypes.GetTypeForCode(Filter.OrganisationTypeCode); }
}
public Sax.OrganisationCategory FilterOrganisationCategory
{
get { return (Sax.OrganisationCategory)OrganisationCategories.GetTypeForCode(Filter.OrganisationCategoryCode); }
}
//-------------------------------------------------------------------
// Organisation properties.
public String OrganisationId { get; set; }
public String OrganisationName { get; set; }
public String OrganisationTypeValue { get; set; }
public Sax.OrganisationType OrganisationType { get { return (Sax.OrganisationType)OrganisationTypes.GetTypeForCode(OrganisationTypeValue); } }
public String OrganisationCategoryValue { get; set; }
public Sax.OrganisationCategory OrganisationCategory { get { return (Sax.OrganisationCategory)OrganisationCategories.GetTypeForCode(OrganisationCategoryValue); } }
public String OrganisationStatusCode { get; set; }
public SaStatus OrganisationStatus { get { return SacsSystem.GetStatusForCode(OrganisationStatusCode); } }
//-------------------------------------------------------------------
// Validation.
//-------------------------------------------------------------------
// Organisation validation.
public bool ValidateOrganisationData(SaxController_Base.AjaxResponse_Data responseData)
{
// Organisation name.
if (OrganisationName != null)
{
responseData.SetControlValue(nameof(OrganisationName), OrganisationName);
if (StringUtil.IsEmpty(OrganisationName))
responseData.SetControlError(nameof(OrganisationName), Localiser["msg.validation.mandatory.value"]);
else if (StringUtil.GetLength(OrganisationName) > GetControlMaxLength(SaxDbDfn.ColumnIdx.Organisation_Name))
responseData.SetControlError(nameof(OrganisationName), Localiser["msg.validation.maximum.length", GetControlMaxLength(SaxDbDfn.ColumnIdx.Organisation_Name)]);
}
// Organisation type.
if (OrganisationTypeValue != null)
{
if (StringUtil.IsEmpty(OrganisationTypeValue))
responseData.SetControlError(nameof(OrganisationTypeValue), Localiser["msg.validation.mandatory.value"]);
}
// Organisation category.
if (OrganisationCategoryValue != null)
{
if (StringUtil.IsEmpty(OrganisationCategoryValue))
responseData.SetControlError(nameof(OrganisationCategoryValue), Localiser["msg.validation.mandatory.value"]);
}
// Organisation status.
if (OrganisationStatusCode != null)
{
if (StringUtil.IsEmpty(OrganisationStatusCode))
responseData.SetControlError(nameof(OrganisationStatusCode), Localiser["msg.validation.mandatory.value"]);
else if (!SaxOrganisation.ValidStatuses.Contains(OrganisationStatus))
responseData.SetControlError(nameof(OrganisationStatusCode), Localiser["msg.validation.invalid.value"]);
}
return !responseData.HasControlErrors();
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,100 @@
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.Base.App;
using Saxonite.Net.Core.Web.Base.App;
using Saxonite.Net.Core.Web.Base.Controllers;
using Saxonite.Net.Core.Web.Base.Models;
using Saxonite.Net.Standard.Base.App;
using Saxonite.Net.Standard.Base.Bus;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Admin.Models
{
//=======================================================================
public class Model_OrganisationMembers : SaxModel_Admin
{
//-------------------------------------------------------------------
// Construction.
public Model_OrganisationMembers(SaxLocaliser localiser) :
base(localiser)
{
AdditionalFiles_JS.Add("js/sax-organisationMembers.js");
}
//-------------------------------------------------------------------
// List configuration.
public class FilterData
{
public String OrganisationId { get; set; }
}
public FilterData Filter { get; } = new FilterData();
public String OrganisationId { get { return Filter.OrganisationId; } set { Filter.OrganisationId = value; } }
//-------------------------------------------------------------------
// User Organisation (member) properties.
public String UserOrganisationId { get; set; }
[EmailAddress]
public String MemberUserEmailAddress{ get; set; }
public String MemberUserAccountId { get; set; }
public String MemberOrganisationId { get; set; }
public String MemberOrganisationName { get; set; }
public String MemberRoleCodeDelim = ",";
public List<String> MemberRoleCodes { get; set; }
public List<String> GetMemberRoleCodes()
{
List<String> codes = new List<string>();
foreach (String code in MemberRoleCodes)
{
if (StringUtil.IsEmpty(code)) continue;
codes.AddRange(code.Split(MemberRoleCodeDelim));
}
return codes;
}
public String MemberStatusCode { get; set; }
public SaStatus MemberStatus { get { return SacsSystem.GetStatusForCode(MemberStatusCode); } }
//-------------------------------------------------------------------
// Validation.
//-------------------------------------------------------------------
// Organisation member validation.
public bool ValidateOrganisationMemberData(SaxController_Base.AjaxResponse_Data responseData)
{
// User email address.
if (MemberUserEmailAddress != null)
{
responseData.SetControlValue(nameof(MemberUserEmailAddress), MemberUserEmailAddress);
if (StringUtil.IsEmpty(MemberUserEmailAddress))
responseData.SetControlError(nameof(MemberUserEmailAddress), Localiser["msg.validation.mandatory.value"]);
else if (!AccountMgr.UserAccountExistsByEmailAddress(MemberUserEmailAddress))
responseData.SetControlError(nameof(MemberUserEmailAddress), Localiser["msg.validation.invalid.value"]);
}
else
{
// User Account.
if (MemberUserAccountId != null)
{
SysId memberUserAccountId = new SysId(StringUtil.ToLong(MemberUserAccountId));
if (!SysId.IsValid(memberUserAccountId))
responseData.SetControlError(nameof(MemberUserAccountId), Localiser["msg.validation.mandatory.value"]);
}
}
// Member roles.
if (MemberRoleCodes != null)
{
// Roles can be left empty as the 'Member' role is automatically applied.
}
return !responseData.HasControlErrors();
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,26 @@
using Saxonite.Net.Core.Web.Base.App;
using Saxonite.Net.Core.Web.Base.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Admin.Models
{
//=======================================================================
public class Model_Policies : SaxModel_Admin
{
//-------------------------------------------------------------------
// Construction.
public Model_Policies(SaxLocaliser localiser) :
base(localiser)
{
}
//-------------------------------------------------------------------
// Properties.
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,141 @@
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.Base.App;
using Saxonite.Net.Core.Web.Base.App;
using Saxonite.Net.Core.Web.Base.Controllers;
using Saxonite.Net.Core.Web.Base.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.Models
{
//=======================================================================
public class Model_Region : SaxModel_Admin
{
//-------------------------------------------------------------------
// Construction.
public Model_Region(SaxLocaliser localiser) :
base(localiser)
{
AdditionalFiles_JS.Add("js/sax-region.js");
}
//-------------------------------------------------------------------
// List properties.
public class FilterData
{
public String Region { get; set; }
public String RegionTypeCode { get; set; }
}
public FilterData Filter { get; } = new FilterData();
public Sax.RegionType FilterRegionType
{
get { return (Sax.RegionType)RegionTypes.GetTypeForCode(Filter.RegionTypeCode); }
}
//-------------------------------------------------------------------
// Region properties.
public String RegionIdValue { get; set; }
public SysId RegionId
{
get { return new SysId(StringUtil.ToInt(RegionIdValue)); }
set { RegionIdValue = SysId.ToString(value); }
}
public String RegionTypeCode { get; set; }
public Sax.RegionType RegionType
{
get { return (Sax.RegionType)RegionTypes.GetTypeForCode(RegionTypeCode); }
set { RegionTypeCode = RegionTypes.GetCodeForType(value); }
}
public String RegionCountryIso2Code { get; set; }
public String RegionName { get; set; }
public String RegionStatusCode { get; set; }
public SaStatus RegionStatus { get { return SacsSystem.GetStatusForCode(RegionStatusCode); } }
//-------------------------------------------------------------------
// Validation.
//-------------------------------------------------------------------
// Region.
public bool ValidateRegionData(SaxController_Base.AjaxResponse_Data responseData)
{
// Region Type.
if (RegionTypeCode != null)
{
responseData.SetControlValue(nameof(RegionTypeCode), RegionTypeCode);
if (StringUtil.IsEmpty(RegionTypeCode) || (int)RegionType < 1)
responseData.SetControlError(nameof(RegionTypeCode), Localiser["msg.validation.mandatory.value"]);
}
// Region ISO2 code.
if (RegionCountryIso2Code != null)
{
RegionCountryIso2Code = RegionCountryIso2Code.ToUpperInvariant();
responseData.SetControlValue(nameof(RegionCountryIso2Code), RegionCountryIso2Code);
if (RegionType == Sax.RegionType.Country)
{
if (StringUtil.IsEmpty(RegionCountryIso2Code))
responseData.SetControlError(nameof(RegionCountryIso2Code), Localiser["msg.validation.mandatory.value"]);
else if (StringUtil.GetLength(RegionCountryIso2Code) != GetControlMaxLength(SaxDbDfn.ColumnIdx.Region_CountryIso2Code))
responseData.SetControlError(nameof(RegionCountryIso2Code), Localiser["msg.validation.invalid.length", GetControlMaxLength(SaxDbDfn.ColumnIdx.Region_CountryIso2Code)]);
else
{
// Check for duplicate code.
SaxRegion.Params prmRegion = new SaxRegion.Params();
if (SysId.IsValid(RegionId))
prmRegion.IdsExcluded.Add(RegionId);
prmRegion.CountryIso2CodesIncluded.Add(RegionCountryIso2Code);
prmRegion.StatusCat = SaxRegion.StatusCat.Any;
if (ReferenceMgr.GetRegionCount(prmRegion) > 0)
responseData.SetControlError(nameof(RegionCountryIso2Code), Localiser["msg.validation.duplicate.value"]);
}
}
else if ((int)RegionType > 0 && RegionType != Sax.RegionType.Country)
{
if (!StringUtil.IsEmpty(RegionCountryIso2Code))
responseData.SetControlError(nameof(RegionCountryIso2Code), Localiser["msg.validation.invalid.value"]);
}
}
// Region name.
if (RegionName != null)
{
responseData.SetControlValue(nameof(RegionName), RegionName);
if (StringUtil.IsEmpty(RegionName))
responseData.SetControlError(nameof(RegionName), Localiser["msg.validation.mandatory.value"]);
else if (StringUtil.GetLength(RegionName) > GetControlMaxLength(SaxDbDfn.ColumnIdx.Region_Name))
responseData.SetControlError(nameof(RegionName), Localiser["msg.validation.maximum.length", GetControlMaxLength(SaxDbDfn.ColumnIdx.Region_Name)]);
else
{
// Check for duplicate name.
SaxRegion.Params prmRegion = new SaxRegion.Params();
if (SysId.IsValid(RegionId))
prmRegion.IdsExcluded.Add(RegionId);
prmRegion.NameFull = RegionName;
prmRegion.StatusCat = SaxRegion.StatusCat.Any;
if (ReferenceMgr.GetRegionCount(prmRegion) > 0)
responseData.SetControlError(nameof(RegionName), Localiser["msg.validation.duplicate.value"]);
}
}
// Region status.
if (RegionStatusCode != null)
{
if (StringUtil.IsEmpty(RegionStatusCode))
responseData.SetControlError(nameof(RegionStatusCode), Localiser["msg.validation.mandatory.value"]);
else if (!SaxRegion.ValidStatuses.Contains(RegionStatus))
responseData.SetControlError(nameof(RegionStatusCode), Localiser["msg.validation.invalid.value"]);
}
return !responseData.HasControlErrors();
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,113 @@
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Helpers;
using Sarand.Net.Standard.Base.Utils;
using Saxonite.Net.Core.Web.Base.App;
using Saxonite.Net.Core.Web.Base.Controllers;
using Saxonite.Net.Core.Web.Base.Models;
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.Models
{
//=======================================================================
public class Model_Security : SaxModel_Admin
{
//-------------------------------------------------------------------
// Construction.
public Model_Security(SaxLocaliser localiser) :
base(localiser)
{
AdditionalFiles_CSS.Add("css/sax-security.css");
AdditionalFiles_JS.Add("js/sax-security.js");
}
//-------------------------------------------------------------------
// Page configuration.
public bool RequestPasswordMode { get { return !StringUtil.IsEmpty(rpe); } }
//-------------------------------------------------------------------
// Form properties.
public String EmailAddress { get; set; }
public String PasswordCurrent { get; set; }
public String Password { get; set; }
public String PasswordConfirm { get; set; }
public bool KeepMeSignedIn { get; set; }
/// <summary>Request Password Encrypted params - to allow Change Password without session or requiring old password.</summary>
public String rpe { get; set; }
//-------------------------------------------------------------------
// Validation.
//-------------------------------------------------------------------
// Sign-in.
public bool ValidateSignInData(SaxController_Base.AjaxResponse_Data responseData)
{
// Email address.
responseData.SetControlValue(nameof(EmailAddress), EmailAddress);
if (StringUtil.IsEmpty(EmailAddress))
responseData.SetControlError(nameof(EmailAddress), Localiser["msg.validation.mandatory.value"]);
else if (!SacsDataValidationHelper.IsValidEmailAddress(EmailAddress))
responseData.SetControlError(nameof(EmailAddress), Localiser["msg.validation.invalid.value"]);
// Password.
// NB Do not return the password value in the response.
if (StringUtil.IsEmpty(Password))
responseData.SetControlError(nameof(Password), Localiser["msg.validation.mandatory.value"]);
return !responseData.HasControlErrors();
}
//-------------------------------------------------------------------
// Change password.
public bool ValidateChangePasswordData(SaxUserAccount userAccount, SaxController_Base.AjaxResponse_Data responseData)
{
// Validate Passwords.
// NB Do not return the password values in the response.
// NB Only validate populated values - any null values are not part of the form for this request.
// - 'Current' password is not shown when changing password from 'Request Password' email link.
if (PasswordCurrent != null && StringUtil.IsEmpty(PasswordCurrent))
responseData.SetControlError(nameof(PasswordCurrent), Localiser["msg.validation.mandatory.value"]);
if (StringUtil.IsEmpty(Password))
responseData.SetControlError(nameof(Password), Localiser["msg.validation.mandatory.value"]);
if (StringUtil.IsEmpty(PasswordConfirm))
responseData.SetControlError(nameof(PasswordConfirm), Localiser["msg.validation.mandatory.value"]);
if (!StringUtil.IsEmpty(Password) && !StringUtil.IsEmpty(PasswordConfirm))
{
if (!Password.Equals(PasswordConfirm))
responseData.SetControlError(nameof(PasswordConfirm), Localiser["msg.validation.invalid.value"]);
if (!SaxPasswordHelper.IsValidPassword(Password))
responseData.SetControlError(nameof(PasswordConfirm), Localiser.GetText("msg.warn.password.invalid", new object[] { SaxPasswordHelper.Params.MinLength, SaxPasswordHelper.Params.MinLengthNoComplexity }));
}
if (!responseData.HasControlErrors())
{
if (PasswordCurrent != null && !SaxPasswordHelper.VerifyHashedPassword(userAccount, PasswordCurrent))
responseData.SetControlError(nameof(PasswordCurrent), Localiser["security.change.password.current.invalid"]);
if (SaxPasswordHelper.VerifyHashedPassword(userAccount, Password))
responseData.SetControlError(nameof(Password), Localiser["security.change.password.not.changed"]);
}
return !responseData.HasControlErrors();
}
//-------------------------------------------------------------------
// Request Password.
public bool ValidateRequestPasswordData(SaxController_Base.AjaxResponse_Data responseData)
{
// Email address.
responseData.SetControlValue(nameof(EmailAddress), EmailAddress);
if (StringUtil.IsEmpty(EmailAddress))
responseData.SetControlError(nameof(EmailAddress), Localiser["msg.validation.mandatory.value"]);
else if (!SacsDataValidationHelper.IsValidEmailAddress(EmailAddress))
responseData.SetControlError(nameof(EmailAddress), Localiser["msg.validation.invalid.value"]);
return !responseData.HasControlErrors();
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,91 @@
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.Base.App;
using Saxonite.Net.Core.Web.Base.App;
using Saxonite.Net.Core.Web.Base.Controllers;
using Saxonite.Net.Core.Web.Base.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.Models
{
//=======================================================================
public class Model_Sport : SaxModel_Admin
{
//-------------------------------------------------------------------
// Construction.
public Model_Sport(SaxLocaliser localiser) :
base(localiser)
{
AdditionalFiles_JS.Add("js/sax-sport.js");
}
//-------------------------------------------------------------------
// Sport list configuration.
public class FilterData
{
public String Sport { get; set; }
}
public FilterData Filter { get; } = new FilterData();
//-------------------------------------------------------------------
// Sport properties.
public SysId SportId { get; set; }
public String SportIdValue
{
get { return SysId.ToString(SportId); }
set { SportId = new SysId(StringUtil.ToInt(value)); }
}
public String SportName { get; set; }
public String SportStatusCode { get; set; }
public SaStatus SportStatus { get { return SacsSystem.GetStatusForCode(SportStatusCode); } }
//-------------------------------------------------------------------
// Validation.
//-------------------------------------------------------------------
// Sport.
public bool ValidateSportData(SaxController_Base.AjaxResponse_Data responseData)
{
// Sport name.
if (SportName != null)
{
responseData.SetControlValue(nameof(SportName), SportName);
if (StringUtil.IsEmpty(SportName))
responseData.SetControlError(nameof(SportName), Localiser["msg.validation.mandatory.value"]);
else if (StringUtil.GetLength(SportName) > GetControlMaxLength(SaxDbDfn.ColumnIdx.Sport_Name))
responseData.SetControlError(nameof(SportName), Localiser["msg.validation.maximum.length", GetControlMaxLength(SaxDbDfn.ColumnIdx.Sport_Name)]);
else
{
// Check for duplicate name.
SaxSport.Params prmSport = new SaxSport.Params();
if (SysId.IsValid(SportId))
prmSport.ExcludedIds.Add(SportId);
prmSport.NamePartial = SportName;
prmSport.StatusCat = SaxSport.StatusCat.Any;
if (ReferenceMgr.GetSportCount(prmSport) > 0)
responseData.SetControlError(nameof(SportName), Localiser["msg.validation.duplicate.value"]);
}
}
// Sport status.
if (SportStatusCode != null)
{
if (StringUtil.IsEmpty(SportStatusCode))
responseData.SetControlError(nameof(SportStatusCode), Localiser["msg.validation.mandatory.value"]);
else if (!SaxSport.ValidStatuses.Contains(SportStatus))
responseData.SetControlError(nameof(SportStatusCode), Localiser["msg.validation.invalid.value"]);
}
return !responseData.HasControlErrors();
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,133 @@
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.Base.App;
using Saxonite.Net.Core.Web.Base.App;
using Saxonite.Net.Core.Web.Base.Controllers;
using Saxonite.Net.Core.Web.Base.Models;
using Saxonite.Net.Standard.Base.App;
using Saxonite.Net.Standard.Base.Bus;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Admin.Models
{
//=======================================================================
public class Model_User : SaxModel_Admin
{
//-------------------------------------------------------------------
// Construction.
public Model_User(SaxLocaliser localiser) :
base(localiser)
{
AdditionalFiles_JS.Add("js/sax-user.js");
}
//-------------------------------------------------------------------
// User list configuration.
public class FilterData
{
public String User { get; set; }
}
public FilterData Filter { get; } = new FilterData();
//-------------------------------------------------------------------
// User properties.
public SysId UserAccountId { get; set; }
public String UserAccountIdValue
{
get { return SysId.ToString(UserAccountId); }
set { UserAccountId = new SysId(StringUtil.ToInt(value)); }
}
[EmailAddress]
public String UserEmailAddress { get; set; }
public String UserTitle { get; set; }
public String UserFirstName { get; set; }
public String UserLastName { get; set; }
public String UserRoleCodeDelim = ",";
public List<String> UserRoleCodes { get; set; }
public List<String> GetUserRoleCodes()
{
List<String> codes = new List<string>();
foreach (String code in UserRoleCodes)
codes.AddRange(code.Split(UserRoleCodeDelim));
return codes;
}
public String UserStatusCode { get; set; }
public SaStatus UserStatus { get { return SacsSystem.GetStatusForCode(UserStatusCode); } }
//-------------------------------------------------------------------
// Validation.
//-------------------------------------------------------------------
// User.
public bool ValidateUserData(SaxController_Base.AjaxResponse_Data responseData)
{
// User email.
if (UserEmailAddress != null)
{
UserEmailAddress = UserEmailAddress.ToLowerInvariant();
responseData.SetControlValue(nameof(UserEmailAddress), UserEmailAddress);
if (StringUtil.IsEmpty(UserEmailAddress))
responseData.SetControlError(nameof(UserEmailAddress), Localiser["msg.validation.mandatory.value"]);
else if (StringUtil.GetLength(UserEmailAddress) > GetControlMaxLength(SaxDbDfn.ColumnIdx.UserAccount_EmailAddress))
responseData.SetControlError(nameof(UserEmailAddress), Localiser["msg.validation.maximum.length", GetControlMaxLength(SaxDbDfn.ColumnIdx.UserAccount_EmailAddress)]);
else
{
// Check for duplicate email address.
SaxUserAccount.Params prmUserAccount = new SaxUserAccount.Params();
if (SysId.IsValid(UserAccountId))
prmUserAccount.IdsExcluded.Add(UserAccountId);
prmUserAccount.EmailAddress = UserEmailAddress;
prmUserAccount.StatusCat = SaxUserAccount.StatusCat.Any;
if (AccountMgr.GetUserAccountCount(prmUserAccount) > 0)
responseData.SetControlError(nameof(UserEmailAddress), Localiser["msg.validation.duplicate.value"]);
}
}
// User title.
if (UserTitle != null)
{
if (StringUtil.IsEmpty(UserTitle))
responseData.SetControlError(nameof(UserTitle), Localiser["msg.validation.mandatory.value"]);
}
// User first name.
if (UserFirstName != null)
{
responseData.SetControlValue(nameof(UserFirstName), UserFirstName);
if (StringUtil.IsEmpty(UserFirstName))
responseData.SetControlError(nameof(UserFirstName), Localiser["msg.validation.mandatory.value"]);
else if (StringUtil.GetLength(UserFirstName) > GetControlMaxLength(SaxDbDfn.ColumnIdx.UserAccount_FirstName))
responseData.SetControlError(nameof(UserFirstName), Localiser["msg.validation.maximum.length", GetControlMaxLength(SaxDbDfn.ColumnIdx.UserAccount_FirstName)]);
}
// User last name.
if (UserLastName != null)
{
responseData.SetControlValue(nameof(UserLastName), UserLastName);
if (StringUtil.IsEmpty(UserLastName))
responseData.SetControlError(nameof(UserLastName), Localiser["msg.validation.mandatory.value"]);
else if (StringUtil.GetLength(UserLastName) > GetControlMaxLength(SaxDbDfn.ColumnIdx.UserAccount_LastName))
responseData.SetControlError(nameof(UserLastName), Localiser["msg.validation.maximum.length", GetControlMaxLength(SaxDbDfn.ColumnIdx.UserAccount_LastName)]);
}
// User status.
if (UserStatusCode != null)
{
if (StringUtil.IsEmpty(UserStatusCode))
responseData.SetControlError(nameof(UserStatusCode), Localiser["msg.validation.mandatory.value"]);
else if (!SaxUserAccount.ValidStatuses.Contains(UserStatus))
responseData.SetControlError(nameof(UserStatusCode), Localiser["msg.validation.invalid.value"]);
}
return !responseData.HasControlErrors();
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,23 @@
using Saxonite.Net.Core.Web.Base.App;
using Saxonite.Net.Core.Web.Base.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Admin.Models
{
//=======================================================================
public class SaxModel_Admin : SaxModel_Base
{
//-------------------------------------------------------------------
// Protected construction.
protected SaxModel_Admin(SaxLocaliser localiser) :
base(localiser)
{
}
//-------------------------------------------------------------------
}
//=======================================================================
}