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
@@ -0,0 +1,12 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "5.0.5",
"commands": [
"dotnet-ef"
]
}
}
}
+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)
{
}
//-------------------------------------------------------------------
}
//=======================================================================
}
@@ -0,0 +1,408 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="company.name" xml:space="preserve">
<value>Solum Forti</value>
</data>
<data name="copyright.symbol.year.html" xml:space="preserve">
<value>&amp;copy; {0}</value>
</data>
<data name="dialog.title.alert" xml:space="preserve">
<value>Alert</value>
</data>
<data name="dialog.title.confirm" xml:space="preserve">
<value>Confirm</value>
</data>
<data name="dialog.title.progress" xml:space="preserve">
<value>Progress</value>
</data>
<data name="dialog.title.signout" xml:space="preserve">
<value>Sign-out</value>
</data>
<data name="home.caption.application" xml:space="preserve">
<value>Admin</value>
</data>
<data name="home.caption.home" xml:space="preserve">
<value>Home</value>
</data>
<data name="label.accounts" xml:space="preserve">
<value>Accounts</value>
</data>
<data name="label.cancel" xml:space="preserve">
<value>Cancel</value>
</data>
<data name="label.category" xml:space="preserve">
<value>Category</value>
</data>
<data name="label.characters.count" xml:space="preserve">
<value>{0} characters</value>
</data>
<data name="label.characters.one" xml:space="preserve">
<value>1 character</value>
</data>
<data name="label.close" xml:space="preserve">
<value>Close</value>
</data>
<data name="label.create" xml:space="preserve">
<value>Create</value>
</data>
<data name="label.delete" xml:space="preserve">
<value>Delete</value>
</data>
<data name="label.email.address" xml:space="preserve">
<value>Email address</value>
</data>
<data name="label.first.name" xml:space="preserve">
<value>First name</value>
</data>
<data name="label.forgotten.password" xml:space="preserve">
<value>Forgotten password?</value>
</data>
<data name="label.iso2.code" xml:space="preserve">
<value>ISO2 code</value>
</data>
<data name="label.keep.signed.in" xml:space="preserve">
<value>Keep me signed-in</value>
</data>
<data name="label.last.name" xml:space="preserve">
<value>Last name</value>
</data>
<data name="label.members" xml:space="preserve">
<value>Members</value>
</data>
<data name="label.name" xml:space="preserve">
<value>Name</value>
</data>
<data name="label.no" xml:space="preserve">
<value>No</value>
</data>
<data name="label.ok" xml:space="preserve">
<value>OK</value>
</data>
<data name="label.password" xml:space="preserve">
<value>Password</value>
</data>
<data name="label.password.confirm" xml:space="preserve">
<value>Confirm password</value>
</data>
<data name="label.password.current" xml:space="preserve">
<value>Current password</value>
</data>
<data name="label.reference" xml:space="preserve">
<value>Reference</value>
</data>
<data name="label.refresh" xml:space="preserve">
<value>Refresh</value>
</data>
<data name="label.remove" xml:space="preserve">
<value>Remove</value>
</data>
<data name="label.roles" xml:space="preserve">
<value>Roles</value>
</data>
<data name="label.security" xml:space="preserve">
<value>Security</value>
</data>
<data name="label.select" xml:space="preserve">
<value>Select</value>
</data>
<data name="label.selected.count" xml:space="preserve">
<value>{0} selected</value>
</data>
<data name="label.selected.none" xml:space="preserve">
<value>none selected</value>
</data>
<data name="label.strength" xml:space="preserve">
<value>Strength</value>
</data>
<data name="label.title" xml:space="preserve">
<value>Title</value>
</data>
<data name="label.type" xml:space="preserve">
<value>Type</value>
</data>
<data name="label.update" xml:space="preserve">
<value>update</value>
</data>
<data name="label.user.roles" xml:space="preserve">
<value>User roles</value>
</data>
<data name="label.yes" xml:space="preserve">
<value>Yes</value>
</data>
<data name="msg.data.noneFound" xml:space="preserve">
<value>No matching data has been found.</value>
</data>
<data name="msg.data.querying" xml:space="preserve">
<value>Querying data. Please wait ...</value>
</data>
<data name="msg.data.refreshing" xml:space="preserve">
<value>Refreshing data. Please wait ...</value>
</data>
<data name="msg.error.data.incomplete" xml:space="preserve">
<value>Data is incomplete</value>
</data>
<data name="msg.error.item.delete.failed" xml:space="preserve">
<value>Item deletion failed</value>
</data>
<data name="msg.error.item.id.not.found" xml:space="preserve">
<value>Item not found for specified Id</value>
</data>
<data name="msg.error.item.id.not.specified" xml:space="preserve">
<value>Item Id not specified</value>
</data>
<data name="msg.error.item.insert.failed" xml:space="preserve">
<value>Item creation failed</value>
</data>
<data name="msg.error.item.update.failed" xml:space="preserve">
<value>Item update failed</value>
</data>
<data name="msg.error.system" xml:space="preserve">
<value>System error encountered.</value>
</data>
<data name="msg.something.gone.wrong" xml:space="preserve">
<value>Unfortunately, something has gone wrong.\n\nYou will be returned to the home page.</value>
</data>
<data name="msg.validation.duplicate.value" xml:space="preserve">
<value>Value is already in use</value>
</data>
<data name="msg.validation.invalid.length" xml:space="preserve">
<value>Value must be {0} characters</value>
</data>
<data name="msg.validation.invalid.value" xml:space="preserve">
<value>Invalid value</value>
</data>
<data name="msg.validation.mandatory.value" xml:space="preserve">
<value>Mandatory value</value>
</data>
<data name="msg.validation.maximum.length" xml:space="preserve">
<value>Value must not exceed {0} characters</value>
</data>
<data name="organisation.caption.member" xml:space="preserve">
<value>Organisation Member</value>
</data>
<data name="organisation.caption.member.create" xml:space="preserve">
<value>Add Member</value>
</data>
<data name="organisation.caption.member.remove" xml:space="preserve">
<value>Remove Member</value>
</data>
<data name="organisation.caption.member.update" xml:space="preserve">
<value>Update Member</value>
</data>
<data name="organisation.caption.members" xml:space="preserve">
<value>Organisation Members</value>
</data>
<data name="organisation.caption.organisation" xml:space="preserve">
<value>Organisation</value>
</data>
<data name="organisation.caption.organisation.create" xml:space="preserve">
<value>Create Organisation</value>
</data>
<data name="organisation.caption.organisation.update" xml:space="preserve">
<value>Update Organisation</value>
</data>
<data name="organisation.caption.organisations" xml:space="preserve">
<value>Organisations</value>
</data>
<data name="organisation.caption.select.member.roles" xml:space="preserve">
<value>Select Member Roles</value>
</data>
<data name="organisation.caption.select.organisation" xml:space="preserve">
<value>Select Organisation</value>
</data>
<data name="policies.caption.privacy" xml:space="preserve">
<value>Privacy</value>
</data>
<data name="region.caption.region" xml:space="preserve">
<value>Region</value>
</data>
<data name="region.caption.region.create" xml:space="preserve">
<value>Create Region</value>
</data>
<data name="region.caption.region.update" xml:space="preserve">
<value>Update Region</value>
</data>
<data name="region.caption.regions" xml:space="preserve">
<value>Regions</value>
</data>
<data name="security.caption.change.password" xml:space="preserve">
<value>Change Password</value>
</data>
<data name="security.caption.request.password" xml:space="preserve">
<value>Request Password</value>
</data>
<data name="security.caption.signin" xml:space="preserve">
<value>Sign-in</value>
</data>
<data name="security.caption.signout" xml:space="preserve">
<value>Sign-out</value>
</data>
<data name="security.change.password.current.invalid" xml:space="preserve">
<value>Current password does not match.</value>
</data>
<data name="security.change.password.not.changed" xml:space="preserve">
<value>Your new password must be different from the previous.</value>
</data>
<data name="security.msg.change.password.failed" xml:space="preserve">
<value>Failed to change password.</value>
</data>
<data name="security.msg.request.password.email.sent" xml:space="preserve">
<value>If your email address matches an active user account, you will shortly receive an email with instructions for changing your password.</value>
</data>
<data name="security.msg.request.password.saved" xml:space="preserve">
<value>Password has been updated</value>
</data>
<data name="security.msg.signin.invalid" xml:space="preserve">
<value>Incorrect account credentials entered.</value>
</data>
<data name="security.msg.signout.confirm" xml:space="preserve">
<value>Are you sure that you want to sign-out?</value>
</data>
<data name="security.msg.warn.sessionExpired" xml:space="preserve">
<value>Your session has expired.</value>
</data>
<data name="sport.caption.sport" xml:space="preserve">
<value>Sport</value>
</data>
<data name="sport.caption.sport.create" xml:space="preserve">
<value>Create Sport</value>
</data>
<data name="sport.caption.sport.update" xml:space="preserve">
<value>Update Sport</value>
</data>
<data name="sport.caption.sports" xml:space="preserve">
<value>Sports</value>
</data>
<data name="user.caption.select.user.roles" xml:space="preserve">
<value>Select User Roles</value>
</data>
<data name="user.caption.user" xml:space="preserve">
<value>User</value>
</data>
<data name="user.caption.user.create" xml:space="preserve">
<value>Create User</value>
</data>
<data name="user.caption.user.update" xml:space="preserve">
<value>Update User</value>
</data>
<data name="user.caption.users" xml:space="preserve">
<value>Users</value>
</data>
</root>
@@ -0,0 +1,29 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:50421",
"sslPort": 44351
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation"
}
},
"SaxoniteCoreWebAdmin": {
"commandName": "Project",
"launchBrowser": true,
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation"
}
}
}
}
@@ -0,0 +1,39 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<CopyRefAssembliesToPublishDirectory>false</CopyRefAssembliesToPublishDirectory>
<RootNamespace>Saxonite.Net.Core.Web</RootNamespace>
<AssemblyName>Saxonite.Net.Core.Web</AssemblyName>
<UserSecretsId>9d0d73e2-7295-4e06-a75d-9989fc0ceacb</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="jQuery.UI.Combined" Version="1.12.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="3.1.13" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Common-VS\Workspace\SacsCoreWeb\SacsCoreWeb.csproj" />
<ProjectReference Include="..\..\..\Common-VS\Workspace\SacsStandardBase\SacsStandardBase.csproj" />
<ProjectReference Include="..\..\..\Common-VS\Workspace\SacsStandardDbas\SacsStandardDbas.csproj" />
<ProjectReference Include="..\SaxoniteCoreWeb\SaxoniteCoreWeb.csproj" />
<ProjectReference Include="..\SaxoniteStandardBase\SaxoniteStandardBase.csproj" />
<ProjectReference Include="..\SaxoniteStandardDbas\SaxoniteStandardDbas.csproj" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Localisation\SaxAdminProperties.resx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Content Update="appsettings.Development.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
</ItemGroup>
</Project>
@@ -0,0 +1,60 @@
@model SaxModel_Admin
@inject SaxLocaliser Localiser
<link rel="stylesheet" href="~/css/sax-codeSelection.css?@Model.HrefVersion" />
<script src="~/js/sax-codeSelection.js?@Model.HrefVersion"></script>
<script type="text/javascript">
</script>
<div id="dlgCodeSelection" class="sax-dialog sax-form">
@* Filter *@
<div class="">
</div>
<div class="row h-100">
@* Available codes *@
<div class="col-sm-5">
<div class="h-100 sax-codeSelection-codes AvailableCodes">
</div>
</div>
@* Code selection *@
<div class="col-sm-2">
<div class="row">
<div class="col-sx-6 col-sm-12 center">
<button class="sax-button" onclick="">@Localiser["label.select"]</button>
</div>
<div class="col-sx-6 col-sm-12 center">
<button class="sax-button" onclick="">@Localiser["label.remove"]</button>
</div>
</div>
</div>
@* Selected codes *@
<div class="col-sm-5">
<div class="h-100 sax-codeSelection-codes SelectedCodes">
</div>
</div>
</div>
<div class="sax-panel-item-template">
<div class="sax-panel-item Code">
<div class="CodeDesc"></div>
</div>
</div>
</div>
@@ -0,0 +1,42 @@
@model SaxModel_Admin
@inject SaxLocaliser Localiser
<link rel="stylesheet" href="~/css/sax-organisationSelection.css?@Model.HrefVersion" />
<script src="~/js/sax-organisationSelection.js?@Model.HrefVersion"></script>
<script type="text/javascript">
sax.organisationSelection.dlgTitleSelectOrganisation = '@Localiser.GetJavascriptText("organisation.caption.select.organisation")';
</script>
<div id="dlgOrganisationSelection" class="sax-dialog sax-form">
@* Filter *@
<div class="sax-panel-filter">
<div class="sax-content">
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmOrganisationSelectionFilter", @class = "sax-form", @onSubmit = "return false;" }))
{
<div class="row">
<div class="col-sm-5 sax-label text-left text-sm-right">@Localiser["organisation.caption.organisation"]</div>
<div class="col-sm-7"><input type="text" id="" class="sax-input" name="Filter.Organisation" onkeypress="sax.organisationSelection.fn.refreshOrganisations(250)" /></div>
</div>
}
</div>
</div>
<div class="">
@* Organisations *@
<div class="sax-selection-results Organisations"></div>
</div>
<div class="sax-panel-item-template">
<div class="sax-panel-item Organisation">
<div class="Name"></div>
</div>
</div>
</div>
@@ -0,0 +1,8 @@
@inject SaxLocaliser Localiser
@{
ViewData["Title"] = "Home Page";
}
<div class="text-center">
<h1 class="display-4">Welcome</h1>
</div>
@@ -0,0 +1,57 @@
@model Model_Organisation
@inject SaxLocaliser Localiser
@{
/* Organisations - Index */
ViewData["Title"] = "Organisations";
}
<div class="">
<div class="sax-panel-filter">
<div class="sax-header">
<div class="floatl sax-caption">@Localiser["organisation.caption.organisations"]</div>
<div class="floatr sax-action sax-results-refresh"><img src="~/img/action/refresh24.png" alt="@(Localiser["label.refresh"])" title="@(Localiser["label.refresh"])" /></div>
<div class="floatr sax-action sax-item-create"><img src="~/img/action/add24.png" alt="@(Localiser["label.create"])" title="@(Localiser["label.create"])" /></div>
<div class="clearb"></div>
</div>
<div class="sax-content">
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmOrganisationFilter", @class = "sax-form", @onSubmit = "return false;" }))
{
<div class="row">
<div class="col-sm-5 sax-label text-left text-sm-right">@Localiser["organisation.caption.organisation"]</div>
<div class="col-sm-7">@Html.TextBoxFor(model => model.Filter.Organisation, new { @id = "ifFilterOrganisation", @class = "FilterOrganisation", @maxlength = "32", @onkeypress = "sax.refreshResults(250)" })</div>
</div>
}
</div>
</div>
<div class="sax-panel-results">
<div class="sax-panel-messages"></div>
<div class="sax-panel-item-list"></div>
<div class="sax-panel-item-template">
<div class="sax-panel-item Organisation">
<div class="sax-header">
<div class="floatl sax-caption"><div class="OrganisationName">Organisation name</div></div>
<div class="floatr sax-action sax-item-update"><img src="~/img/action/edit24.png" alt="@(Localiser["label.update"])" title="@(Localiser["label.update"])" /></div>
<div class="clearb"></div>
</div>
<div class="sax-content">
<div class="row">
<div class="col-sm-4"><div class="OrganisationCategory"></div></div>
<div class="col-sm-4"><div class="OrganisationType"></div></div>
<div class="col-sm-4 right"><div class=""></div></div>
</div>
<div class="row">
<div class="col-sm-4"><div class="OrganisationMemberCount sax-action-text">@Localiser["label.members"]:&nbsp;<span class="MemberCount"></span></div></div>
<div class="col-sm-4"><div class=""></div></div>
<div class="col-sm-4 right"><div class="StatusDesc"></div></div>
</div>
</div>
</div>
</div>
</div>
</div>
<partial name="Panel-OrganisationDialog.cshtml" />
@@ -0,0 +1,65 @@
@model Model_OrganisationMembers
@inject SaxLocaliser Localiser
@{
/* Organisation Members - Index */
ViewData["Title"] = "Organisation Members";
}
<div class="">
<div class="sax-panel-filter OrganisationMembers">
<div class="sax-header">
<div class="floatl sax-caption">@Localiser["organisation.caption.members"]</div>
<div class="floatr sax-action sax-results-refresh"><img src="~/img/action/refresh24.png" alt="@(Localiser["label.refresh"])" title="@(Localiser["label.refresh"])" /></div>
<div class="floatr sax-action sax-item-create"><img src="~/img/action/add24.png" alt="@(Localiser["label.create"])" title="@(Localiser["label.create"])" /></div>
<div class="clearb"></div>
</div>
<div class="sax-content">
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmMemberFilter", @class = "sax-form", @onSubmit = "return false;" }))
{
<div class="row">
<div class="col-sm-3 sax-label text-left text-sm-right">@Localiser["organisation.caption.organisation"]</div>
<div class="col-sm-9">
<div class="row">
<div class="col">
@Html.HiddenFor(model => model.Filter.OrganisationId, new { @id = "ifFilterOrganisationId" })
<input type="text" id="ifFilterOrganisation" class="sax-input" readonly="readonly" value="@(Model.MemberOrganisationName)" />
</div>
<div class="fixed"><button id="" class="sax-button" onclick="sax.selectOrganisation()">@Localiser["label.select"]</button></div>
</div>
</div>
</div>
}
</div>
</div>
<div class="sax-panel-results">
<div class="sax-panel-messages"></div>
<div class="sax-panel-item-list"></div>
<div class="sax-panel-item-template">
<div class="sax-panel-item Member">
<div class="sax-header">
<div class="floatl sax-caption"><div class="UserDisplayName"></div></div>
<div class="floatr sax-action sax-item-update"><img src="~/img/action/edit24.png" alt="@(Localiser["label.update"])" title="@(Localiser["label.update"])" /></div>
<div class="clearb"></div>
</div>
<div class="sax-content">
<div class="row">
<div class="col-sm-6"><div class="UserEmailAddress"></div></div>
<div class="col-sm-6 right"><div class="MemberRoleCount">@Localiser["label.roles"]:&nbsp;<span class="RoleCount"></span></div></div>
</div>
<div class="row">
<div class="col-sm-4"></div>
<div class="col-sm-4"><div class=""></div></div>
<div class="col-sm-4 right"><div class="StatusDesc"></div></div>
</div>
</div>
</div>
</div>
</div>
</div>
<partial name="Panel-MemberDialog.cshtml" />
<partial name="~/Views/Common/OrganisationSelection.cshtml" />
@@ -0,0 +1,65 @@
@model Model_OrganisationMembers
@inject SaxLocaliser Localiser
@{
/* Organisations - Organisation dialog panel */
}
<script type="text/javascript">
sax.dlg.title.selectOrganisation = '@Localiser.GetJavascriptText("organisation.caption.select.organisation")';
sax.dlg.title.createMember = '@Localiser.GetJavascriptText("organisation.caption.member.create")';
sax.dlg.title.updateMember = '@Localiser.GetJavascriptText("organisation.caption.member.update")';
sax.dlg.title.removeMember = '@Localiser.GetJavascriptText("organisation.caption.member.remove")';
sax.dlg.title.selectMemberRoles = '@Localiser.GetJavascriptText("organisation.caption.select.member.roles")';
sax.data.userRoleCodeDelim = '@Model.MemberRoleCodeDelim';
</script>
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmMemberId", @class = "sax-form" }))
{
@Html.HiddenFor(model => model.UserOrganisationId, new { @id = "", @class = "UserOrganisationId" })
}
<div id="dlgMember" class="sax-dialog" title="">
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmMember", @class = "sax-form", @onSubmit = "return false;" }))
{
@Html.HiddenFor(model => model.UserOrganisationId, new { @id = "", @class = "UserOrganisationId" })
@Html.HiddenFor(model => model.MemberOrganisationId, new { @id = "", @class = "MemberOrganisationId" })
@* User email *@
<div class="sax-form-control UserEmailAddress">
<div class="sax-label">
<label for="ifMemberUserEmailAddress"><span><span class="required">*</span> @Localiser["label.email.address"]</span></label>
</div>
<div class="sax-control">
@Html.TextBoxFor(model => model.MemberUserEmailAddress, new { @id = "ifMemberUserEmailAddress", @class = "MemberUserEmailAddress sax-input", @maxlength = @Model.GetControlMaxLength(SaxDbDfn.ColumnIdx.UserAccount_EmailAddress) })
</div>
<div class="sax-message"></div>
</div>
@* User email *@
<div class="sax-form-control UserName">
<div class="sax-label">
<label for="ifMemberUserName"><span>@Localiser["label.name"]</span></label>
</div>
<div class="sax-control">
<input type="text" id="ifMemberUserName" class="MemberUserName sax-input" readonly="readonly" />
</div>
<div class="sax-message"></div>
</div>
@* Member roles *@
<div class="sax-form-control MemberRoleCodes">
<div class="sax-label">
<label for="ifMemberRoles"><span>@Localiser["label.roles"]</span></label>
</div>
<div class="sax-control row">
@Html.HiddenFor(model => model.MemberRoleCodes, new { @id = "ifMemberRoleCodes", @class = "MemberRoleCodes sax-input" })
<div class="col"><textarea id="ifMemberRoles" class="MemberRoles sax-input" rows="6" readonly="readonly" ondblclick="sax.selectMemberRoles()"></textarea></div>
<div class="fixed"><button id="" class="sax-button" style="min-width:auto;margin-left:4px;padding:4px 6px" onclick="sax.selectMemberRoles()">@Localiser["label.select"]</button></div>
</div>
<div class="sax-message"></div>
</div>
}
<partial name="~/Views/Common/CodeSelection.cshtml" />
</div>
@@ -0,0 +1,58 @@
@model Model_Organisation
@inject SaxLocaliser Localiser
@{
/* Organisations - Organisation dialog panel */
}
<script type="text/javascript">
sax.dlg.title.createOrganisation = '@Localiser.GetJavascriptText("organisation.caption.organisation.create")';
sax.dlg.title.updateOrganisation = '@Localiser.GetJavascriptText("organisation.caption.organisation.update")';
</script>
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmOrganisationId", @class = "sax-form" }))
{
@Html.HiddenFor(model => model.OrganisationId, new { @class = "OrganisationId" })
}
<div id="dlgOrganisation" class="sax-dialog" title="">
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmOrganisation", @class = "sax-form", @onSubmit = "return false;" }))
{
@Html.HiddenFor(model => model.OrganisationId, new { @id="", @class = "OrganisationId" })
@* Organisation name *@
<div class="sax-form-control OrganisationName">
<div class="sax-label">
<label for="ifOrganisationName"><span><span class="required">*</span> @Localiser["label.name"]</span></label>
</div>
<div class="sax-control">
@Html.TextBoxFor(model => model.OrganisationName, new { @id = "ifOrganisationName", @class = "OrganisationName sax-input", @maxlength = @Model.GetControlMaxLength(SaxDbDfn.ColumnIdx.Organisation_Name) })
</div>
<div class="sax-message"></div>
</div>
@* Organisation type *@
<div class="sax-form-control OrganisationType">
<div class="sax-label">
<label for="cbOrganisationType"><span><span class="required">*</span> @Localiser["label.type"]</span></label>
</div>
<div class="sax-control">
@Html.DropDownListFor(model => model.OrganisationTypeValue, Model.OrganisationTypesForSelect, Localiser["label.select"], new { @id = "cbOrganisationType", @class = "OrganisationType sax-select" })
</div>
<div class="sax-message"></div>
</div>
@* Organisation category *@
<div class="sax-form-control OrganisationCategory">
<div class="sax-label">
<label for="cbOrganisationCategory"><span><span class="required">*</span> @Localiser["label.category"]</span></label>
</div>
<div class="sax-control">
@Html.DropDownListFor(model => model.OrganisationCategoryValue, Model.OrganisationCategoriesForSelect, Localiser["label.select"], new { @id = "cbOrganisationCategory", @class = "OrganisationCategory sax-select" })
</div>
<div class="sax-message"></div>
</div>
@* Organisation country *@
}
</div>
@@ -0,0 +1,6 @@
@{
ViewData["Title"] = "Privacy Policy";
}
<h1>@ViewData["Title"]</h1>
<p>Use this page to detail your site's privacy policy.</p>
@@ -0,0 +1,56 @@
@model Model_Region
@inject SaxLocaliser Localiser
@{
/* Regions - Index */
ViewData["Title"] = "Regions";
}
<div class="">
<div class="sax-panel-filter">
<div class="sax-header">
<div class="floatl sax-caption">@Localiser["region.caption.regions"]</div>
<div class="floatr sax-action sax-results-refresh"><img src="~/img/action/refresh24.png" alt="@(Localiser["label.refresh"])" title="@(Localiser["label.refresh"])" /></div>
<div class="floatr sax-action sax-item-create"><img src="~/img/action/add24.png" alt="@(Localiser["label.create"])" title="@(Localiser["label.create"])" /></div>
<div class="clearb"></div>
</div>
<div class="sax-content">
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmRegionFilter", @class = "sax-form", @onSubmit = "return false;" }))
{
<div class="row">
<div class="col-sm-5 sax-label text-left text-sm-right">@Localiser["region.caption.region"]</div>
<div class="col-sm-7">@Html.TextBoxFor(model => model.Filter.Region, new { @id = "ifFilterRegion", @class = "FilterRegion", @maxlength = "32", @onkeypress = "sax.refreshResults(250)" })</div>
</div>
}
</div>
</div>
<div class="sax-panel-results">
<div class="sax-panel-messages"></div>
<div class="sax-panel-item-list"></div>
<div class="sax-panel-item-template">
<div class="sax-panel-item Region">
<div class="sax-header">
<div class="floatl sax-caption"><span class="RegionName"></span> <span class="RegionCountryIso2Code"></span></div>
<div class="floatr sax-action sax-item-update"><img src="~/img/action/edit24.png" alt="@(Localiser["label.update"])" title="@(Localiser["label.update"])" /></div>
<div class="clearb"></div>
</div>
<div class="sax-content">
<div class="row">
<div class="col-sm-4"><div class="RegionTypeDesc"></div></div>
<div class="col-sm-4"><div class="RegionParentName"></div></div>
<div class="col-sm-4 right"><div class="RegionStatusDesc"></div></div>
</div>
<div class="row">
<div class="col-sm-4"></div>
<div class="col-sm-4"><div class=""></div></div>
</div>
</div>
</div>
</div>
</div>
</div>
<partial name="Panel-RegionDialog.cshtml" />
@@ -0,0 +1,56 @@
@model Model_Region
@inject SaxLocaliser Localiser
@{
/* Region form dialog panel */
}
<script type="text/javascript">
sax.dlg.title.createRegion = '@Localiser.GetJavascriptText("region.caption.region.create")';
sax.dlg.title.updateRegion = '@Localiser.GetJavascriptText("region.caption.region.update")';
</script>
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmRegionId", @class = "sax-form" }))
{
@Html.HiddenFor(model => model.RegionIdValue, new { @class = "RegionId" })
}
<div id="dlgRegion" class="sax-dialog" title="">
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmRegion", @class = "sax-form", @onSubmit = "return false;" }))
{
@Html.HiddenFor(model => model.RegionIdValue, new { @class = "RegionId" })
@* Region type *@
<div class="sax-form-control RegionType">
<div class="sax-label">
<label for="cbRegionType"><span><span class="required">*</span> @Localiser["label.type"]</span></label>
</div>
<div class="sax-control">
@Html.DropDownListFor(model => model.RegionTypeCode, Model.RegionTypesForSelect, Localiser["label.select"], new { @id = "cbRegionType", @class = "RegionType sax-select" })
</div>
<div class="sax-message"></div>
</div>
@* Country ISO2 code *@
<div class="sax-form-control RegionIso2Code">
<div class="sax-label">
<label for="ifRegionIso2Code"><span><span class="required">*</span> @Localiser["label.iso2.code"]</span></label>
</div>
<div class="sax-control">
@Html.TextBoxFor(model => model.RegionCountryIso2Code, new { @id = "ifRegionCountryIso2Code", @class = "RegionCountryIso2Code sax-input", @maxlength = @Model.GetControlMaxLength(SaxDbDfn.ColumnIdx.Region_CountryIso2Code) })
</div>
<div class="sax-message"></div>
</div>
@* Region name *@
<div class="sax-form-control RegionName">
<div class="sax-label">
<label for="ifRegionName"><span><span class="required">*</span> @Localiser["label.name"]</span></label>
</div>
<div class="sax-control">
@Html.TextBoxFor(model => model.RegionName, new { @id = "ifRegionName", @class = "RegionName sax-input", @maxlength = @Model.GetControlMaxLength(SaxDbDfn.ColumnIdx.Region_Name) })
</div>
<div class="sax-message"></div>
</div>
}
</div>
@@ -0,0 +1,63 @@
@model Model_Security
@inject SaxLocaliser Localiser
@{
ViewData["Title"] = "Change Password";
}
<div class="">
<div class="sax-panel-form security">
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmChangePassword", @class = "sax-form" }))
{
<div class="sax-heading"><div class="sax-caption">@Localiser["security.caption.change.password"]</div></div>
@if (!Model.RequestPasswordMode)
{
/* Current password - required when setting password within user session. */
<div class="sax-form-control">
<div class="sax-label"><label for="ifPasswordCurrent"><span><span class="required">*</span> @Localiser["label.password.current"]</span></label></div>
<div class="sax-control">
@Html.PasswordFor(model => model.PasswordCurrent, new { @id = "ifPasswordCurrent", @class = "sax-input", @maxlength = Model.PasswordMaxLength })
</div>
<div class="sax-message"></div>
</div>
}
else
{
/* 'Request Password' encrypted parameters from email link. */
@Html.HiddenFor(model => model.rpe, new { @id = "ifRpe" });
}
<div class="spacer4"></div>
<div class="sax-form-control">
<div class="sax-label"><label for="ifPassword"><span><span class="required">*</span> @Localiser["label.password"]</span></label></div>
<div class="sax-control">
@Html.PasswordFor(model => model.Password, new { @id = "ifPassword", @class = "sax-input", @maxlength = Model.PasswordMaxLength })
</div>
<div class="row">
<div class="col-auto sax-label-text pt4">@Localiser["label.strength"]&nbsp;</div>
<div id="" class="col-auto"><div id="prgPasswordStrength" class="col-auto"></div></div>
<div class="clearb"></div>
</div>
<div class="sax-message"></div>
</div>
<div class="sax-form-control">
<div class="sax-label"><label for="ifPasswordConfirm"><span><span class="required">*</span> @Localiser["label.password.confirm"]</span></label></div>
<div class="sax-control">
@Html.PasswordFor(model => model.PasswordConfirm, new { @id = "ifPasswordConfirm", @class = "sax-input", @maxlength = Model.PasswordMaxLength })
</div>
<div class="sax-message"></div>
</div>
<div class="spacer4"></div>
<div class="">
<div id="pbChangePassword" class="sax-button-action fullwidth pointer" style="margin:16px 0;">@Localiser["security.caption.change.password"]</div>
</div>
}
</div>
</div>
@@ -0,0 +1,32 @@
@model Model_Security
@inject SaxLocaliser Localiser
@{
ViewData["Title"] = "Request Password";
}
<div class="">
<div class="sax-panel-form security">
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmRequestPassword", @class = "sax-form" }))
{
<div class="sax-heading"><div class="sax-caption">@Localiser["security.caption.request.password"]</div></div>
<div class="sax-form-control">
<div class="sax-label">
<label for="ifEmailAddress"><span><span class="required">*</span> @Localiser["label.email.address"]</span></label>
</div>
<div class="sax-control">
@Html.TextBoxFor(model => model.EmailAddress, new { @id = "ifEmailAddress", @class = "sax-input lower", @maxlength = "128" })
</div>
<div class="sax-message"></div>
</div>
<div class="spacer4"></div>
<div class="">
<div id="pbRequestPassword" class="sax-button-action fullwidth pointer" style="margin:16px 0;">@Localiser["security.caption.request.password"]</div>
</div>
}
</div>
</div>
@@ -0,0 +1,61 @@
@model Model_Security
@inject SaxLocaliser Localiser
@{
ViewData["Title"] = "Signin";
}
<div class="">
<div class="sax-panel-form security">
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmSignIn", @class = "sax-form" }))
{
<div class="sax-heading"><div class="sax-caption">@Localiser["security.caption.signin"]</div></div>
<div class="sax-form-control">
<div class="sax-label">
<label for="ifEmailAddress"><span><span class="required">*</span> @Localiser["label.email.address"]</span></label>
</div>
<div class="sax-control">
@Html.TextBoxFor(model => model.EmailAddress, new { @id = "ifEmailAddress", @class = "sax-input lower", @maxlength = @Model.GetControlMaxLength(SaxDbDfn.ColumnIdx.UserAccount_EmailAddress) })
</div>
<div class="sax-message"></div>
</div>
<div class="sax-form-control">
<div class="row">
<div class="col-6">
<div class="sax-label">
<label for="ifPassword"><span><span class="required">*</span> @Localiser["label.password"]</span></label>
</div>
</div>
<div class="col-6 right">
<div class="sax-action-link" style="font-size:12px;" onclick="sax.goToPage('Security/RequestPassword');">@Localiser["label.forgotten.password"]</div>
</div>
</div>
<div class="clearb"></div>
<div class="sax-control">
@Html.PasswordFor(model => model.Password, new { @id = "ifPassword", @class = "sax-input", @maxlength = @Model.PasswordMaxLength })
</div>
<div class="sax-message"></div>
</div>
<div class="spacer4"></div>
<div class="sax-form-control">
<div class="">
@Html.CheckBoxFor(model => model.KeepMeSignedIn, new { @id = "chkKeepMeSignedIn" })
&nbsp;
<span class="sax-label pointer" onclick="$('#chkKeepMeSignedIn').click()">@Localiser["label.keep.signed.in"]</span>
</div>
</div>
<div class="">
<div id="pbSignIn" class="sax-button-action fullwidth pointer" style="margin:16px 0;">@Localiser["security.caption.signin"]</div>
</div>
}
</div>
</div>
@@ -0,0 +1,25 @@
@model ErrorViewModel
@{
ViewData["Title"] = "Error";
}
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>
@if (Model.ShowRequestId)
{
<p>
<strong>Request ID:</strong> <code>@Model.RequestId</code>
</p>
}
<h3>Development Mode</h3>
<p>
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
</p>
<p>
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
and restarting the app.
</p>
@@ -0,0 +1,25 @@
@using Microsoft.AspNetCore.Http.Features
@{
var consentFeature = Context.Features.Get<ITrackingConsentFeature>();
var showBanner = !consentFeature?.CanTrack ?? false;
var cookieString = consentFeature?.CreateConsentCookie();
}
@if (showBanner)
{
<div id="cookieConsent" class="alert alert-info alert-dismissible fade show" role="alert">
Use this space to summarize your privacy and cookie use policy. <a asp-area="" asp-controller="Policies" asp-action="Privacy">Learn More</a>.
<button type="button" class="accept-policy close" data-dismiss="alert" aria-label="Close" data-cookie-string="@cookieString">
<span aria-hidden="true">Accept</span>
</button>
</div>
<script>
(function () {
var button = document.querySelector("#cookieConsent button[data-cookie-string]");
button.addEventListener("click", function (event) {
document.cookie = button.dataset.cookieString;
}, false);
})();
</script>
}
@@ -0,0 +1 @@
<div>Google Analytics</div>
@@ -0,0 +1,213 @@
@model SaxModel_Admin
@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery AntiForgery;
@inject SaxLocaliser Localiser
@*
Application: Online
Purpose: Page template.
*@
@{
// Model properties to become javascript values.
var awaitingLogin = Model.AwaitingLogin ? "1" : "0";
var keepLoggedIn = Model.KeepLoggedIn ? "1" : "0";
var debug = Model.DebugMode ? "1" : "0";
}
<!DOCTYPE html>
<html lang="en">
<head>
@* Set base URL the same for all pages so that relative URLs work the same for all. *@
<base href="@Model.RedirectLocation.Base" />
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
@* IE11 incompatibility eg. 'border-radius' not recognised. Must be at top of head *@
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
@if (Model.ProhibitCache)
{
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
}
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="Content-Script-Type" content="application/javascript" />
<title>@ViewData["Title"]</title>
@* Stylesheets *@
<link rel="stylesheet" href="~/lib/jquery-ui/jquery-ui.css" />
<environment include="Development">
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
</environment>
<environment exclude="Development">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute"
crossorigin="anonymous"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" />
</environment>
<link rel="stylesheet" href="~/css/sax-base.css?@Model.HrefVersion" />
<link rel="stylesheet" href="~/css/sax-main.css?@Model.HrefVersion" />
<link rel="stylesheet" href="~/css/sax-data.css?@Model.HrefVersion" />
<link rel="stylesheet" href="~/css/sax-form.css?@Model.HrefVersion" />
@foreach (var cssFile in Model.AdditionalFiles_CSS)
{
<link rel="stylesheet" href="~/@cssFile?@Model.HrefVersion" />
}
@* Scripts *@
<script>
var sax = sax || { page: {}, url: {}, links: {}, form: {}, data: {}, msg: {}, lbl: {}, dlg: { title: {}, btn: {} }, $frm: {}, $dlg: {} };
sax.url.base = '@Model.RedirectLocation.Base';
sax.url.home = '@Model.RedirectLocation.Home';
sax.url.sessionExpired = '@Model.RedirectLocation.Home_SessionExpired';
sax.links.facebook = '@Model.Links.Facebook';
sax.links.linkedin = '@Model.Links.LinkedIn';
sax.dlg.title.alert = '@Localiser.GetJavascriptText("dialog.title.alert")';
sax.dlg.title.confirm = '@Localiser.GetJavascriptText("dialog.title.confirm")';
sax.dlg.title.progress = '@Localiser.GetJavascriptText("dialog.title.progress")';
sax.dlg.title.signOut = '@Localiser.GetJavascriptText("dialog.title.signout")';
sax.dlg.btn.yes = '@Localiser["label.yes"]';
sax.dlg.btn.no = '@Localiser["label.no"]';
sax.dlg.btn.ok = '@Localiser["label.ok"]';
sax.dlg.btn.close = '@Localiser["label.close"]';
sax.dlg.btn.cancel = '@Localiser["label.cancel"]';
sax.lbl.availableCharacter = '@Localiser.GetJavascriptText("label.characters.one")';
sax.lbl.availableCharacters = '@Localiser.GetJavascriptText("label.characters.count", "{0}")';
sax.lbl.noneSelected = '@Localiser.GetJavascriptText("label.selected.none")';
sax.lbl.numberSelected = '@Localiser.GetJavascriptText("label.selected.count", "{0}")';
sax.msg.signOutConfirm = '@Localiser.GetJavascriptText("security.msg.signout.confirm")';
sax.msg.somethingGoneWrong = '@Localiser.GetJavascriptText("msg.something.gone.wrong")';
sax.msg.dataRefreshing = '@Localiser.GetJavascriptText("msg.data.refreshing")';
sax.msg.dataQuerying = '@Localiser.GetJavascriptText("msg.data.querying")';
sax.msg.dataNoneFound = '@Localiser.GetJavascriptText("msg.data.noneFound")';
sax.sessionCookieName = '@Model.SessionCookieName';
sax.awaitingLogin = @awaitingLogin;
sax.keepLoggedIn = @keepLoggedIn;
sax.keepLoggedInDelay = @(Sax.Config.Admin.Session.Cookie.KeepLoggedInTimeoutMillis);
sax.debugMode = @debug;
</script>
</head>
<body>
@* Header *@
<header>
@* Navigation bar *@
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container">
<a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">@Localiser["home.caption.application"]</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">@Localiser["home.caption.home"]</a>
</li>
@if (Model.AwaitingLogin)
{
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Policies" asp-action="Privacy">@Localiser["policies.caption.privacy"]</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Security" asp-action="">@Localiser["security.caption.signin"]</a>
</li>
}
else
{
<li class="nav-item dropdown">
<a class="nav-link text-dark dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
@Localiser["label.reference"]
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<div class="dropdown-item text-dark sax-action" onclick="sax.goToPage('Region');">@Localiser["region.caption.regions"]</div>
<div class="dropdown-item text-dark sax-action" onclick="sax.goToPage('Sport');">@Localiser["sport.caption.sports"]</div>
</div>
</li>
<li class="nav-item dropdown">
<a class="nav-link text-dark dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
@Localiser["label.accounts"]
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<div class="dropdown-item text-dark sax-action" onclick="sax.goToPage('User');">@Localiser["user.caption.users"]</div>
<div class="dropdown-item text-dark sax-action" onclick="sax.goToPage('Organisation');">@Localiser["organisation.caption.organisations"]</div>
</div>
</li>
<li class="nav-item dropdown">
<a class="nav-link text-dark dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
@Localiser["label.security"]
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<div class="dropdown-item text-dark sax-action" onclick="sax.navigateTo('Security/ChangePassword');">@Localiser["security.caption.change.password"]</div>
<div class="dropdown-divider"></div>
<div class="dropdown-item text-dark sax-action" onclick="sax.onSignOut();">@Localiser["security.caption.signout"]</div>
</div>
</li>
}
</ul>
</div>
</div>
</nav>
@* Page caption *@
@if (!StringUtil.IsEmpty(Model.PageCaption))
{
<div class="container">
<div class="sax-page-caption">@(Model.PageCaption)</div>
</div>
}
</header>
@* Page body *@
<div class="container">
<main role="main" class="pb-3">
@RenderBody()
</main>
</div>
@* Footer *@
<footer class="border-top footer text-muted">
<div class="container">
@(Model.CopyrightNotice) - @Localiser["company.name"] @(Model.InstanceVersion) - <a asp-area="" asp-controller="Home" asp-action="">Home</a> - <a asp-area="" asp-controller="Policies" asp-action="Privacy">Privacy</a>
</div>
</footer>
@* Form to allow page navigation. *@
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmNavigate" })) { }
<div id='sax-please-wait'></div>
@* Scripts *@
<environment include="Development">
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.js"></script>
</environment>
<environment exclude="Development">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"
asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
asp-fallback-test="window.jQuery"
crossorigin="anonymous"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=">
</script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.bundle.min.js"
asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"
asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal"
crossorigin="anonymous"
integrity="sha384-xrRywqdh3PHs8keKZN+8zzc5TX0GRTLCcmivcbNJWm2rs5C8PRhcEn3czEjhAO9o">
</script>
</environment>
<script src="~/lib/jquery-ui/jquery-ui.js"></script>
<script src="~/js/sax-main.js?@Model.HrefVersion"></script>
@foreach (var jsFile in Model.AdditionalFiles_JS)
{
<script src="~/@jsFile?@Model.HrefVersion"></script>
}
@if (Model.IncludeGoogleAnalytics)
{
<partial name="~/Views/Shared/_GoogleAnalytics.cshtml" />
}
@RenderSection("Scripts", required: false)
</body>
</html>
@@ -0,0 +1,2 @@
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
@@ -0,0 +1,56 @@
@model Model_Sport
@inject SaxLocaliser Localiser
@{
/* Sports - Index */
ViewData["Title"] = "Sports";
}
<div class="">
<div class="sax-panel-filter">
<div class="sax-header">
<div class="floatl sax-caption">@Localiser["sport.caption.sports"]</div>
<div class="floatr sax-action sax-results-refresh"><img src="~/img/action/refresh24.png" alt="@(Localiser["label.refresh"])" title="@(Localiser["label.refresh"])" /></div>
<div class="floatr sax-action sax-item-create"><img src="~/img/action/add24.png" alt="@(Localiser["label.create"])" title="@(Localiser["label.create"])" /></div>
<div class="clearb"></div>
</div>
<div class="sax-content">
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmSportFilter", @class = "sax-form", @onSubmit = "return false;" }))
{
<div class="row">
<div class="col-sm-5 sax-label text-left text-sm-right">@Localiser["sport.caption.sport"]</div>
<div class="col-sm-7">@Html.TextBoxFor(model => model.Filter.Sport, new { @id = "ifFilterSport", @class = "FilterSport", @maxlength = "32", @onkeypress = "sax.refreshResults(250)" })</div>
</div>
}
</div>
</div>
<div class="sax-panel-results">
<div class="sax-panel-messages"></div>
<div class="sax-panel-item-list"></div>
<div class="sax-panel-item-template">
<div class="sax-panel-item Sport">
<div class="sax-header">
<div class="floatl sax-caption"><div class="SportName">Sport name</div></div>
<div class="floatr sax-action sax-item-update"><img src="~/img/action/edit24.png" alt="@(Localiser["label.update"])" title="@(Localiser["label.update"])" /></div>
<div class="clearb"></div>
</div>
<div class="sax-content">
<div class="row">
<div class="col-sm-4"><div class="SportCategory"></div></div>
<div class="col-sm-4"><div class="SportType"></div></div>
<div class="col-sm-4 right"><div class="StatusDesc"></div></div>
</div>
<div class="row">
<div class="col-sm-4"></div>
<div class="col-sm-4"><div class=""></div></div>
</div>
</div>
</div>
</div>
</div>
</div>
<partial name="Panel-SportDialog.cshtml" />
@@ -0,0 +1,34 @@
@model Model_Sport
@inject SaxLocaliser Localiser
@{
/* Sports - Sport dialog panel */
}
<script type="text/javascript">
sax.dlg.title.createSport = '@Localiser.GetJavascriptText("sport.caption.sport.create")';
sax.dlg.title.updateSport = '@Localiser.GetJavascriptText("sport.caption.sport.update")';
</script>
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmSportId", @class = "sax-form" }))
{
@Html.HiddenFor(model => model.SportIdValue, new { @class = "SportId" })
}
<div id="dlgSport" class="sax-dialog" title="">
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmSport", @class = "sax-form", @onSubmit = "return false;" }))
{
@Html.HiddenFor(model => model.SportIdValue, new { @class = "SportId" })
@* Sport name *@
<div class="sax-form-control SportName">
<div class="sax-label">
<label for="ifSportName"><span><span class="required">*</span> @Localiser["label.name"]</span></label>
</div>
<div class="sax-control">
@Html.TextBoxFor(model => model.SportName, new { @id = "ifSportName", @class = "SportName sax-input", @maxlength = @Model.GetControlMaxLength(SaxDbDfn.ColumnIdx.Sport_Name) })
</div>
<div class="sax-message"></div>
</div>
}
</div>
@@ -0,0 +1,56 @@
@model Model_User
@inject SaxLocaliser Localiser
@{
/* Users - Index */
ViewData["Title"] = "Users";
}
<div class="">
<div class="sax-panel-filter">
<div class="sax-header">
<div class="floatl sax-caption">@Localiser["user.caption.users"]</div>
<div class="floatr sax-action sax-results-refresh"><img src="~/img/action/refresh24.png" alt="@(Localiser["label.refresh"])" title="@(Localiser["label.refresh"])" /></div>
<div class="floatr sax-action sax-item-create"><img src="~/img/action/add24.png" alt="@(Localiser["label.create"])" title="@(Localiser["label.create"])" /></div>
<div class="clearb"></div>
</div>
<div class="sax-content">
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmUserFilter", @class = "sax-form", @onSubmit = "return false;" }))
{
<div class="row">
<div class="col-sm-5 sax-label text-left text-sm-right">@Localiser["user.caption.user"]</div>
<div class="col-sm-7">@Html.TextBoxFor(model => model.Filter.User, new { @id = "ifFilterUser", @class = "FilterUser", @maxlength = "32", @onkeypress = "sax.refreshResults(250)" })</div>
</div>
}
</div>
</div>
<div class="sax-panel-results">
<div class="sax-panel-messages"></div>
<div class="sax-panel-item-list"></div>
<div class="sax-panel-item-template">
<div class="sax-panel-item User">
<div class="sax-header">
<div class="floatl sax-caption"><div class="UserEmailAddress"></div></div>
<div class="floatr sax-action sax-item-update"><img src="~/img/action/edit24.png" alt="@(Localiser["label.update"])" title="@(Localiser["label.update"])" /></div>
<div class="clearb"></div>
</div>
<div class="sax-content">
<div class="row">
<div class="col-sm-4"><span class="UserDisplayName"></span></div>
<div class="col-sm-4"><div>Roles: <span class="UserRoleCount"></span></div></div>
<div class="col-sm-4 right"><div class="StatusDesc"></div></div>
</div>
<div class="row">
<div class="col-sm-4"></div>
<div class="col-sm-4"><div class=""></div></div>
</div>
</div>
</div>
</div>
</div>
</div>
<partial name="Panel-UserDialog.cshtml" />
@@ -0,0 +1,83 @@
@model Model_User
@inject SaxLocaliser Localiser
@{
/* Users - User dialog panel */
}
<script type="text/javascript">
sax.dlg.title.createUser = '@Localiser.GetJavascriptText("user.caption.user.create")';
sax.dlg.title.updateUser = '@Localiser.GetJavascriptText("user.caption.user.update")';
sax.dlg.title.selectUserRoles = '@Localiser.GetJavascriptText("user.caption.select.user.roles")';
sax.data.userRoleCodeDelim = '@Model.UserRoleCodeDelim';
</script>
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmUserId", @class = "sax-form" }))
{
@Html.HiddenFor(model => model.UserAccountIdValue, new { @class = "UserAccountId" })
}
<div id="dlgUser" class="sax-dialog" title="">
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmUser", @class = "sax-form", @onSubmit = "return false;" }))
{
@Html.HiddenFor(model => model.UserAccountIdValue, new { @class = "UserAccountId" })
@* User email *@
<div class="sax-form-control UserEmailAddress">
<div class="sax-label">
<label for="ifUserEmailAddress"><span><span class="required">*</span> @Localiser["label.email.address"]</span></label>
</div>
<div class="sax-control">
@Html.TextBoxFor(model => model.UserEmailAddress, new { @id = "ifUserEmailAddress", @class = "UserEmailAddress sax-input", @maxlength = @Model.GetControlMaxLength(SaxDbDfn.ColumnIdx.UserAccount_EmailAddress) })
</div>
<div class="sax-message"></div>
</div>
<div class="sax-form-control UserTitle">
<div class="sax-label">
<label for="cbUserTitle"><span><span class="required"></span> @Localiser["label.title"]</span></label>
</div>
<div class="sax-control">
@Html.DropDownListFor(model => model.UserTitle, Model.TitlesForSelect, Localiser["label.select"], new { @id = "cbUserTitle", @class = "UserTitle sax-select" })
</div>
<div class="sax-message"></div>
</div>
@* User first name *@
<div class="sax-form-control UserFirstName">
<div class="sax-label">
<label for="ifUserFirstName"><span><span class="required">*</span> @Localiser["label.first.name"]</span></label>
</div>
<div class="sax-control">
@Html.TextBoxFor(model => model.UserFirstName, new { @id = "ifUserFirstName", @class = "UserFirstName sax-input", @maxlength = @Model.GetControlMaxLength(SaxDbDfn.ColumnIdx.UserAccount_FirstName) })
</div>
<div class="sax-message"></div>
</div>
@* User last name *@
<div class="sax-form-control UserLastName">
<div class="sax-label">
<label for="ifUserLastName"><span><span class="required">*</span> @Localiser["label.last.name"]</span></label>
</div>
<div class="sax-control">
@Html.TextBoxFor(model => model.UserLastName, new { @id = "ifUserLastName", @class = "UserLastName sax-input", @maxlength = @Model.GetControlMaxLength(SaxDbDfn.ColumnIdx.UserAccount_LastName) })
</div>
<div class="sax-message"></div>
</div>
@* User roles *@
<div class="sax-form-control UserRoles">
<div class="sax-label">
<label for="lbUserRoles"><span>@Localiser["label.user.roles"]</span></label>
</div>
<div class="sax-control row">
@Html.HiddenFor(model => model.UserRoleCodes, new { @id = "ifUserRoleCodes", @class = "UserRoleCodes sax-input" })
<div class="col"><textarea id="ifUserRoles" class="UserRoles sax-input" style="" rows="3" readonly="readonly" onDblClick="sax.selectUserRoles()"></textarea></div>
<div class="fixed"><button id="" class="sax-button" style="min-width:auto;margin-left:4px;padding:4px 6px" onclick="sax.selectUserRoles()">@Localiser["label.select"]</button></div>
</div>
<div class="sax-message"></div>
</div>
}
<partial name="~/Views/Common/CodeSelection.cshtml" />
</div>
@@ -0,0 +1,7 @@
@using Sarand.Net.Standard.Base.Core
@using Sarand.Net.Standard.Base.Utils
@using Saxonite.Base.App
@using Saxonite.Net.Standard.Base.App
@using Saxonite.Net.Core.Web.Base.App
@using Saxonite.Net.Core.Web.Admin.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@@ -0,0 +1,4 @@
@inject Saxonite.Net.Core.Web.Base.App.SaxLocaliser Localiser
@{
Layout = "_Layout";
}
@@ -0,0 +1,26 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"EmailConfigs": {
"SAX-SF-Dev": {
"Message": {
"AddressFrom": "DoNotReply@solumforti.com"
},
"SMTP": {
"Server": "smtp.ionos.co.uk",
"ServerPort": 587,
"UseSSL": true
},
"Send": {
"Using": 2,
"UserName": "info@solumforti.com",
"Password": "solfor-info-5Ekur3"
}
}
}
}
@@ -0,0 +1,57 @@
{
"SessionConfigs": {
"Saxonite-Dev": {
"Environments": [ "Development" ],
"Instance": "Dev",
"CoreDatabase": {
"Location": "MsSql:Sarand106\\SQL2014.Saxonite_Dev",
"User": "saxonite",
"Password": "sax-dev-2021"
},
"Email": {
"Context": "SAX-SF-Dev",
"ContactUs": {
"AddressesTo": [ "dev@solumforti.com" ]
}
},
"Documents": {
"RootFolder": "D:\\Saxonite\\Data\\Docs"
},
"Logging": {
"RootFolder": "D:\\Sarand\\Projects\\User\\AHoward\\VS2017\\Saxonite\\Logs"
}
},
"Saxonite-Test": {
"Environments": [ "Production" ],
"Instance": "QC",
"CoreDatabase": {
"Location": "MsSql:Sarand106\\SQL2014.Saxonite_Dev",
"User": "saxonite",
"Password": "sax-dev-2021"
},
"Email": {
"Context": "SAX-SF-Dev",
"ContactUs": {
"AddressesTo": [ "qc@solumforti.com" ],
"AddressesBcc": [ ]
}
},
"Documents": {
"RootFolder": "D:\\Saxonite\\Data\\Docs"
},
"Logging": {
"RootFolder": "D:\\Sarand\\Projects\\User\\AHoward\\VS2017\\Saxonite\\QC\\Logs"
},
"Variants": [
{
"ApplicationRootFolder": "D:\\Sarand\\Projects\\User\\AHoward\\VS2017\\Saxonite\\QC2\\Bin",
"Instance": "QC",
"Logging": {
"RootFolder": "D:\\Sarand\\Projects\\User\\AHoward\\VS2017\\Saxonite\\QC2\\Logs"
}
}
]
}
}
}
@@ -0,0 +1,57 @@
{
"SessionConfigs": {
"Saxonite-Dev": {
"Environments": [ "Development" ],
"Instance": "Dev",
"CoreDatabase": {
"Location": "MsSql:Sarand107\\SQL2016.Saxonite_Dev",
"User": "saxonite",
"Password": "sax-dev-2021"
},
"Email": {
"Context": "SAX-SF-Dev",
"ContactUs": {
"AddressesTo": [ "dev@solumforti.com" ]
}
},
"Documents": {
"RootFolder": "D:\\Sarand\\Projects\\Users\\AHoward\\VS2019\\Saxonite\\Docs"
},
"Logging": {
"RootFolder": "D:\\Sarand\\Projects\\Users\\AHoward\\VS2019\\Saxonite\\Logs\\Admin"
}
},
"Saxonite-Test": {
"Environments": [ "Production" ],
"Instance": "QC",
"CoreDatabase": {
"Location": "MsSql:Sarand106\\SQL2014.Saxonite_Dev",
"User": "saxonite",
"Password": "sax-dev-2021"
},
"Email": {
"Context": "SAX-SF-Dev",
"ContactUs": {
"AddressesTo": [ "qc@solumforti.com" ],
"AddressesBcc": [ ]
}
},
"Documents": {
"RootFolder": "D:\\Sarand\\Projects\\Users\\AHoward\\VS2019\\Saxonite\\Docs"
},
"Logging": {
"RootFolder": "D:\\Sarand\\Projects\\Users\\AHoward\\VS2019\\Saxonite\\QC\\Logs\\Admin"
},
"Variants": [
{
"ApplicationRootFolder": "D:\\Sarand\\Projects\\Users\\AHoward\\VS2019\\Saxonite\\QC2\\App",
"Instance": "QC",
"Logging": {
"RootFolder": "D:\\Sarand\\Projects\\Users\\AHoward\\VS2019\\Saxonite\\QC2\\Logs\\Admin"
}
}
]
}
}
}
@@ -0,0 +1,60 @@
{
"SessionConfigs": {
"SAX-Prod": {
"Environments": [ "Production" ],
"Instance": "",
"CoreDatabase": {
"Location": "MsSql:.\\SQLExpress2012.Saxonite_Core_Demo",
"User": "sax-demo-user",
"Password": "sax-demo-202105"
},
"Security": {
"Password": {
"MinLength": 9
},
"SignIn": {
"KeepLoggedIn": {
"Allowed": true,
"CookieExpiresDays": 5
},
"PasswordRequest": {
"LinkExpiresMins": 30
}
}
},
"Email": {
"Context": "SAX-SF-Prod",
"ContactUs": {
"AddressesTo": [],
"AddressesBcc": [ "dev@solumforti.com" ]
},
"Signup": {
"Activate": {
"AddressesBcc": [ "signup@solumforti.com" ]
},
"Welcome": {
"AddressesBcc": [ "signup@solumforti.com" ]
}
}
},
"Documents": {
"RootFolder": "C:\\Sarand\\Saxonite\\Demo\\Admin\\Docs"
},
"Logging": {
"RootFolder": "C:\\Sarand\\Saxonite\\Demo\\Admin\\Logs"
},
"Variants": [
{
"ApplicationRootFolder": "C:\\Sarand\\Saxonite\\Demo\\Admin\\App",
"Instance": "Demo",
"Documents": {
"RootFolder": "C:\\Sarand\\Saxonite\\Demo\\Admin\\Docs"
},
"Logging": {
"RootFolder": "C:\\Sarand\\Saxonite\\Demo\\Admin\\Logs"
}
}
]
}
}
}
@@ -0,0 +1,60 @@
{
"SessionConfigs": {
"SAX-Prod": {
"Environments": [ "Production" ],
"Instance": "",
"CoreDatabase": {
"Location": "MsSql:.\\SQL2016EXP.Saxonite_Core_Live",
"User": "sax-prod-user",
"Password": "sax-live-202104"
},
"Security": {
"Password": {
"MinLength": 8
},
"SignIn": {
"KeepLoggedIn": {
"Allowed": true,
"CookieExpiresDays": 30
},
"PasswordRequest": {
"LinkExpiresMins": 30
}
}
},
"Email": {
"Context": "SAX-SF-Prod",
"ContactUs": {
"AddressesTo": [],
"AddressesBcc": [ "dev@solumforti.com" ]
},
"Signup": {
"Activate": {
"AddressesBcc": [ "signup@solumforti.com" ]
},
"Welcome": {
"AddressesBcc": [ "signup@solumforti.com" ]
}
}
},
"Documents": {
"RootFolder": "C:\\Sarand\\Saxonite\\Prod\\SaxoniteAdmin\\Docs"
},
"Logging": {
"RootFolder": "C:\\Sarand\\Saxonite\\Prod\\SaxoniteAdmin\\Logs"
},
"Variants": [
{
"ApplicationRootFolder": "C:\\Sarand\\Saxonite\\PreProd\\SaxoniteAdmin\\Bin",
"Instance": "PreProd",
"Documents": {
"RootFolder": "C:\\Sarand\\Saxonite\\PreProd\\SaxoniteAdmin\\Docs"
},
"Logging": {
"RootFolder": "C:\\Sarand\\Saxonite\\PreProd\\SaxoniteAdmin\\Logs"
}
}
]
}
}
}
@@ -0,0 +1,2 @@
{
}
@@ -0,0 +1,19 @@
{
"EmailConfigs": {
"SAX-SF-Prod": {
"Message": {
"AddressFrom": "DoNotReply@solumforti.com"
},
"SMTP": {
"Server": "smtp.ionos.co.uk",
"ServerPort": 587,
"UseSSL": true
},
"Send": {
"Using": 2,
"UserName": "info@solumforti.com",
"Password": "solfor-info-5Ekur3"
}
}
}
}
+10
View File
@@ -0,0 +1,10 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
+10
View File
@@ -0,0 +1,10 @@
{
"version": "1.0",
"defaultProvider": "cdnjs",
"libraries": [
{
"library": "jqueryui@1.12.1",
"destination": "wwwroot/lib/jquery-ui/"
}
]
}
@@ -0,0 +1,415 @@
/*
Admin - Base stylesheet.
*/
body
{
margin: 0;
padding: 0;
font-family: Verdana, Helvetica, Arial, sans-serif;
font-size: 1rem;
line-height: 1.25rem;
background-color: #fff;
overflow: auto;
}
div, button, input, select, textarea, table, tr, td
{
font-family: inherit;
font-size: 1rem;
}
:focus, button:focus
{
outline: none;
box-shadow: 0 0 0 2px #80808040 inset;
}
/* Fonts */
/* Common styles */
h1, h2, h3
{
color: #404040;
}
h1
{
font-size: 14px;
font-weight: bold;
margin: 6px 0 6px 0;
padding: 0;
}
h2
{
font-size: 12px;
font-weight: bold;
margin: 3px 0 6px 0;
padding: 0;
}
h3
{
font-size: 11px;
font-weight: bold;
margin: 3px 0 6px 0;
padding: 0;
}
div, form
{
margin: 0;
padding: 0;
}
p
{
margin: 3px 0 6px 0;
}
hr
{
height: 4px;
width: 90%;
text-align: center;
color: #000;
margin: 4px auto;
}
a
{
font-size: 1rem;
}
img
{
border-style: none;
}
ul
{
margin: 3px 0 6px 0;
}
table
{
margin: 0;
border-collapse: separate;
border-spacing: 2px;
border-width: 0;
empty-cells: inherit;
}
th, td
{
vertical-align: top;
padding: 0;
}
label
{
margin: 0;
}
input
{
margin: 0;
padding: 0 2px 1px 2px;
}
textarea
{
resize: none;
}
.bold, b
{
font-weight: bold !important;
}
.ul, u
{
text-decoration: underline !important;
}
.italic, i
{
font-style: italic !important;
}
.upper
{
text-transform: uppercase !important;
}
.lower
{
text-transform: lowercase !important;
}
.center
{
text-align: center !important;
}
.right
{
text-align: right !important;
}
.middle
{
vertical-align: middle !important;
}
.bottom
{
vertical-align: bottom !important;
}
.pointer
{
cursor: pointer;
}
.action
{
cursor: pointer;
}
.action:hover
{
text-decoration: underline
}
td.action
{
text-align: center;
}
.fullwidth
{
width: 100%;
}
.inlineblock, .inlineBlock, .iblock, .inlineb, .ib
{
display: inline-block;
}
.floatl, .floatleft
{
float: left;
}
.floatr, .floatright
{
float: right;
}
.clearl, .clearleft
{
clear: left;
}
.clearr, .clearright
{
clear: right;
}
.clearb, .clearboth
{
clear: both;
margin: 0;
padding: 0;
width: 0;
height: 0;
line-height: 0.0em;
font-size: 0;
}
.clear-after:after
{
visibility: hidden;
display: block;
font-size: 0;
content: " ";
clear: both;
height: 0;
}
.nowrap
{
white-space: nowrap !important;
}
.spacer
{
height: 4px;
font-size: 0;
line-height: 0;
}
.spacer2
{
height: 8px;
font-size: 0;
line-height: 0;
}
.spacer4
{
height: 16px;
font-size: 0;
line-height: 0;
}
.spacer8
{
height: 32px;
font-size: 0;
line-height: 0;
}
.pt2
{
padding-top: 2px !important;
}
.pt4
{
padding-top: 4px !important;
}
.pt8
{
padding-top: 8px !important;
}
.pt12
{
padding-top: 12px !important;
}
.pt16
{
padding-top: 16px !important;
}
.pb2
{
padding-bottom: 2px !important;
}
.pb4
{
padding-bottom: 4px !important;
}
.pb8
{
padding-bottom: 8px !important;
}
.pb12
{
padding-bottom: 12px !important;
}
.pb16
{
padding-bottom: 16px !important;
}
.pl2
{
padding-left: 2px !important;
}
.pl4
{
padding-left: 4px !important;
}
.pl8
{
padding-left: 8px !important;
}
.pl12
{
padding-left: 12px !important;
}
.pl16
{
padding-left: 16px !important;
}
.pr2
{
padding-right: 2px !important;
}
.pr4
{
padding-right: 4px !important;
}
.pr8
{
padding-right: 8px !important;
}
.pr12
{
padding-right: 12px !important;
}
.pr16
{
padding-right: 16px !important;
}
.lastTabSeq
{
z-index: 99;
}
.hidden
{
display: none;
}
.shadow
{
-moz-box-shadow: 2px 2px 3px #ccc;
-webkit-box-shadow: 2px 2px 3px #ccc;
box-shadow: 2px 2px 3px #ccc; /* For IE 8 */
-ms-filter: "progid:DXImageTransform.Microsoft.Shadow(Strength=3,Direction=135,Color='#cccccc')"; /* For IE 5.5 - 7 */
filter: progid:DXImageTransform.Microsoft.Shadow(Strength=3,Direction=135,Color='#cccccc');
}
/* Bootstrap */
.container, .container-fluid
{
padding: 0;
}
.row
{
margin: 0;
}
.col, .col-1, .col-10, .col-11, .col-12, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-auto,
.col-lg, .col-lg-1, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-auto,
.col-md, .col-md-1, .col-md-10, .col-md-11, .col-md-12, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-auto,
.col-sm, .col-sm-1, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-auto,
.col-xl, .col-xl-1, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-auto
{
padding: 0;
}
/* The End */
@@ -0,0 +1,36 @@
/*
Admin - Code Selection stylesheet.
*/
/* Panels */
.sax-codeSelection-codes
{
padding:2px 0;
background-color:#f0f0f0;
border: 1px solid #b0b0b0;
border-radius: 6px;
}
#dlgCodeSelection.sax-form button.sax-button
{
margin: 4px 0;
}
.sax-panel-item.Code
{
margin: 2px 4px;
padding: 2px 4px;
background-color:#fff;
border: 2px solid #d8d8ff;
border-radius: 4px;
cursor:pointer;
}
.sax-panel-item.Code.sel
{
background-color: #ececff;
border-color: #8a8aff;
}
/* The End */
@@ -0,0 +1,139 @@
/*
Admin - Data stylesheet.
*/
/* Filter panel */
.sax-panel-filter
{
margin: 8px auto;
border: solid 2px #979797;
border-radius: 6px;
-webkit-border-radius: 6px;
-moz-border-radius: 6px;
}
@media (min-width: 576px)
{
.sax-panel-filter
{
}
}
.sax-panel-filter .sax-header
{
padding: 2px 8px;
min-height: 24px;
color: #f0f0f0;
background-color: #979797;
border-bottom: solid 2px #979797;
}
.sax-panel-filter .sax-header .sax-caption
{
margin-top: 4px;
font-size: 1.2rem;
font-weight: bold;
}
.sax-panel-filter .sax-header .sax-action
{
margin: 2px 0 0 8px;
border-radius: 4px;
}
.sax-panel-filter .sax-header .sax-action:hover
{
background-color: #b0b0b0;
box-shadow: 0 0 4px 2px #80808080;
}
.sax-panel-filter .sax-content
{
margin: 4px 8px;
padding: 4px 8px;
}
.sax-panel-filter .sax-content .sax-label
{
padding-top: 2px;
padding-right: 12px;
text-align: right;
}
.sax-panel-filter .sax-content .sax-button
{
min-width: auto;
margin-left: 4px;
padding: 4px 6px;
}
/* Data Items panel */
.sax-panel-results
{
}
/* Messages */
.sax-panel-results .sax-panel-messages .sax-message
{
margin-top: 8px;
padding-left: 24px;
font-size: 16px;
background-position: left top;
background-repeat: no-repeat;
}
.sax-panel-results .sax-panel-messages .sax-message.sax-error
{
color: #e3120b;
background-image: url("../img/icon/error-18.png");
}
/* Item */
.sax-panel-item-template
{
display: none;
}
.sax-panel-item
{
margin: 8px auto;
border: solid 2px #979797;
border-radius: 6px;
-webkit-border-radius: 6px;
-moz-border-radius: 6px;
overflow:hidden;
}
.sax-panel-item .sax-header
{
padding: 2px 8px;
min-height: 24px;
background-color: #e0e0e0;
border-bottom: solid 2px #979797;
}
.sax-panel-item .sax-caption
{
margin-top: 4px;
font-size: 1.2rem;
font-weight: bold;
}
.sax-panel-item .sax-content
{
margin: 4px 8px;
padding: 4px 4px;
}
@media (min-width: 576px)
{
.sax-panel-filter
{
}
}
/* The End */
@@ -0,0 +1,186 @@
/*
Admin - Form stylesheet.
*/
/* Panels */
.sax-panel-info, .sax-panel-form
{
max-width: 720px;
margin: 24px auto;
padding: 24px 24px;
border: solid 2px #979797;
border-color: var(--color-primary);
border-radius: 4px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
}
.sax-panel-form.security
{
max-width:520px;
}
@media (min-width: 576px)
{
.sax-panel-info, .sax-panel-form
{
}
}
/* Form */
.sax-form
{
}
.sax-form .sax-heading
{
margin-bottom: 20px;
}
.sax-form .sax-heading .sax-caption
{
font-size: 1.5rem;
font-weight: bold;
color: var(--color-primary);
}
/* Controls */
.sax-form .sax-form-control
{
margin: 0 0 16px 0;
}
.sax-form .sax-form-control .sax-label
{
margin: 0 0 8px 0;
font-size: 1.00rem;
}
.sax-form .sax-form-control .sax-label-text
{
font-size: 1.00rem;
}
.sax-form .sax-form-control .sax-note
{
font-size: 0.9rem;
}
.sax-form .sax-input, .sax-form .sax-textarea, .sax-form .sax-select
{
padding: 4px 8px;
width: 100%;
font-size: 1.00rem;
background-color: #fff;
border: 1px solid #595959;
border-radius: 2px;
-webkit-border-radius: 2px;
-moz-border-radius: 2px;
outline: none;
}
.sax-form .sax-textarea, .sax-form .sax-select[multiple="multiple"]
{
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
overflow-y: scroll;
}
.sax-form .sax-input:read-only, .sax-form .sax-textarea:read-only
{
background-color: #f8f8f8;
}
.sax-form .sax-form-control.sax-error .sax-input, .sax-form .sax-form-control.sax-error .sax-textarea, .sax-form .sax-form-control.sax-error .sax-select
{
color: #DC143C;
border-color: #DC143C;
}
/* Control messages */
.sax-form .sax-form-control .sax-message
{
display: none;
margin-top: 8px;
padding-left: 24px;
font-size: 1.00rem;
background-position: left top;
background-repeat: no-repeat;
}
.sax-form .sax-form-control.sax-error .sax-message
{
display: block;
color: #e3120b;
background-image: url("../img/icon/error-18.png");
}
/* Buttons */
.sax-form button.sax-button,
.sax-form input[type="button"].sax-button,
.sax-form input[type="submit"].sax-button
{
height: auto;
width: auto;
min-width: 80px;
padding: 4px 2px;
font-size: 1rem;
text-align: center;
color: #fff;
background-color: #d4af37;
border: 1px solid #d4af37;
border-radius: 4px;
}
.sax-form button.sax-button:hover,
.sax-form input[type="button"].sax-button:hover,
.sax-form input[type="submit"].sax-button:hover
{
}
/* Password strength */
#prgPasswordStrength
{
width: 200px;
height: 18px;
margin: 6px 24px;
border-color: #595959;
-moz-box-shadow: none;
-webkit-box-shadow: none;
overflow: hidden;
}
#prgPasswordStrength .ui-widget-header
{
margin: 0;
height: 20px;
background: none;
background-color: #D2222D;
border: solid 0 #D2222D;
border-radius: 0;
}
#prgPasswordStrength.amber .ui-widget-header
{
background-color: #FFBF00;
border-color: #FFBF00;
}
#prgPasswordStrength.green .ui-widget-header
{
background-color: #238823;
border-color: #238823;
}
#prgPasswordStrength.ui-corner-all
{
border-radius: 8px;
}
/* The End */
@@ -0,0 +1,358 @@
/*
Admin - Main stylesheet.
*/
/* Global variables */
:root
{
/* #4b0082 - Indigo. Ref. https://www.crispedge.com/color/4b0082 */
--color-primary: #4b0082;
--color-light: #9b71b9;
--color-pale: #ebe2f1;
/* Tetrad/Square colors */
--color-alt1: #820a00;
--color-alt2: #378200;
--color-alt3: #007882;
}
/* Document */
html
{
font-size: 14px;
}
@media (min-width: 768px)
{
html
{
font-size: 16px;
}
}
body
{
font-family: Arial, Verdana, Helvetica, Lucida, sans-serif;
}
body > .container
{
padding: 0 1%;
}
/* Sticky footer styles */
html
{
position: relative;
min-height: 100%;
}
body
{
/* Margin bottom by footer height */
margin-bottom: 60px;
}
.footer
{
position: absolute;
bottom: 0;
width: 100%;
padding:0 2%;
white-space: nowrap;
/* Set the fixed height of the footer here */
height: 60px;
line-height: 60px; /* Vertically center the text there */
}
/* Header */
a.navbar-brand
{
white-space: normal;
text-align: center;
word-break: break-all;
}
.border-top
{
border-top: 1px solid #e5e5e5;
}
.border-bottom
{
border-bottom: 1px solid #e5e5e5;
}
.box-shadow
{
box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
}
button.accept-policy
{
font-size: 1rem;
line-height: inherit;
}
/* Page caption */
.sax-page-caption
{
margin: 0;
padding: 16px 24px;
font-size: 48px;
font-weight: bold;
color: #000;
}
/* Actions */
a, a:hover
{
text-decoration: underline;
color: #d4af37;
}
a:focus
{
outline-color: #b0b0b0;
outline-width: thin;
}
.sax-action, .sax-action-text
{
cursor: pointer;
}
.sax-action-text:hover
{
text-decoration: underline;
}
.sax-action-link
{
display: inline-block;
font-size: 1rem;
font-weight: bold;
color: #000;
cursor: pointer;
}
.sax-action-link:hover
{
opacity: .7;
transition: all .4s ease-in-out;
}
.sax-link-text
{
font-size: 18px;
font-weight: bold;
}
/* Buttons */
.sax-button-cta, .sax-button-action
{
display: inline-block;
padding: 12px 12px;
min-width: 100px;
color: #404040;
background-color: #e0e0e0;
font-size: 16px;
font-weight: bold;
text-align: center;
border-radius: 20px;
cursor: pointer;
transition: all .5s ease-in-out;
outline: none;
}
.sax-button-action
{
}
.sax-button-action:hover
{
background-color: #b0b0b0;
}
.sax-button-action:focus
{
outline: none;
box-shadow: 0 0 4px 2px #808080;
}
.sax-button-cta:hover
{
background: #808080 !important;
}
/* --- Notification --- */
#sax-notification
{
display: none;
position: fixed;
top: 0;
right: 0;
min-width: 180px;
margin-right: 12px;
z-index: 1000;
border: 1px solid #808080;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
overflow: hidden;
}
#sax-notification .content
{
background-color: #fbfbfb;
overflow: hidden;
}
#sax-notification .heading
{
font-size: 16px;
font-weight: bold;
background-color: #f0f0f0;
border-bottom: 1px solid #808080;
}
#sax-notification .heading:after
{
content: "";
display: table;
clear: both;
}
#sax-notification .title
{
float: left;
padding: 4px 8px;
white-space: nowrap;
}
#sax-notification .close
{
float: right;
padding: 4px;
font-size: 14px;
font-weight: bold;
cursor: pointer;
}
#sax-notification .message
{
clear: both;
padding: 8px;
font-size: 14px;
}
/* --- Modal Please wait --- */
#sax-please-wait
{
display: none;
cursor: progress;
position: fixed;
z-index: 1001;
top: 0;
left: 0;
height: 100%;
width: 100%;
background: rgba(250,250,250,.25) url('../img/animated/InProgress-01.gif') 50% 50% no-repeat;
}
/* Dialogs */
.sax-dialog
{
display: none;
}
.ui-dialog
{
position: fixed;
padding: 0;
}
.ui-dialog .ui-dialog-titlebar
{
padding: 6px 12px;
}
.ui-dialog .ui-dialog-content
{
padding: 6px 12px;
}
.ui-dialog.ui-widget.ui-widget-content
{
border-width: 2px;
border-color: #d4af37;
-moz-box-shadow: 4px 4px 6px #ccc;
-webkit-box-shadow: 4px 4px 6px #ccc;
overflow: hidden;
}
.ui-dialog.ui-widget.ui-widget-content.ui-corner-all
{
border-radius: 6px;
}
.ui-dialog.ui-widget .ui-widget-header
{
font-size: 1.1rem;
color: #fff;
background-color: #d4af37;
border-color: #d4af37;
}
.ui-widget-content
{
font-size: 16px;
color: #000;
}
.ui-dialog .ui-dialog-titlebar.ui-corner-all
{
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
.ui-dialog.no-close .ui-dialog-titlebar-close
{
/*display: none;*/
}
.ui-dialog .ui-dialog-buttonpane
{
border-color: #d4af37;
}
.ui-dialog .ui-dialog-buttonpane button
{
color: #fff;
background-color: #d4af37;
border-color: #d4af37;
outline: none;
}
.ui-widget-overlay
{
background-color: #b0b0b0;
opacity: 0.25;
}
/* Date Picker */
.ui-datepicker
{
}
.ui-datepicker .ui-datepicker-header
{
background-color: #fff;
border: 1px solid #d4af37;
}
/* The End */
@@ -0,0 +1,47 @@
/*
Admin - Code Selection stylesheet.
*/
/* Panels */
#dlgOrganisationSelection .sax-panel-filter
{
border-width: 1px;
}
#dlgOrganisationSelection .sax-panel-filter .sax-content
{
margin:4px 8px;
padding:2px 4px;
}
#dlgOrganisationSelection .sax-selection-results.Organisations
{
padding:2px 0;
background-color:#f0f0f0;
border: 1px solid #b0b0b0;
border-radius: 6px;
}
#dlgOrganisationSelection.sax-form button.sax-button
{
margin: 4px 0;
}
.sax-panel-item.Organisation
{
margin: 2px 4px;
padding: 2px 4px;
background-color:#fff;
border: 2px solid #d8d8ff;
border-radius: 4px;
cursor:pointer;
}
.sax-panel-item.Organisation.sel
{
background-color: #ececff;
border-color: #8a8aff;
}
/* The End */
Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 923 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 529 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 765 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 946 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 789 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 829 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 697 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1011 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 604 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Some files were not shown because too many files have changed in this diff Show More