Add project files.
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.31019.35
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SaxoniteCoreWebAdmin", "SaxoniteCoreWebAdmin\SaxoniteCoreWebAdmin.csproj", "{F646283D-F7C2-42DD-AB1E-E741455664BD}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SaxoniteStandardDbas", "SaxoniteStandardDbas\SaxoniteStandardDbas.csproj", "{DA383586-69E3-40EB-A20D-AC1AD24E336C}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SaxoniteStandardBase", "SaxoniteStandardBase\SaxoniteStandardBase.csproj", "{38FBE932-C1D0-4A28-9172-1466347FD62B}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SacsStandardBase", "..\..\Common-VS\Workspace\SacsStandardBase\SacsStandardBase.csproj", "{9C5309DA-3205-4423-BE13-923EE61370A6}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SacsStandardDbas", "..\..\Common-VS\Workspace\SacsStandardDbas\SacsStandardDbas.csproj", "{E211FE36-108E-4D07-BD30-799A156DDDBB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SacsStandardMob", "..\..\Common-VS\Workspace\SacsStandardMob\SacsStandardMob.csproj", "{53A962E2-F26B-4DAD-B0F0-EA0B24B520A1}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SaxoniteStandardMob", "SaxoniteStandardMob\SaxoniteStandardMob.csproj", "{89CFDAA1-15C9-45CF-8FBF-88E36E0E32CF}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SaxoniteCoreWeb", "SaxoniteCoreWeb\SaxoniteCoreWeb.csproj", "{CFF91B48-04B7-4075-A5B2-FF3FC0B22BC1}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SaxoniteCoreWebOnline", "SaxoniteCoreWebOnline\SaxoniteCoreWebOnline.csproj", "{C1A3E949-B5EA-4A77-9411-5D8B350056D6}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SacsCoreWeb", "..\..\Common-VS\Workspace\SacsCoreWeb\SacsCoreWeb.csproj", "{BBEEBD5B-7D00-4024-9626-D8B2FBF248A4}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SaxoniteCoreUnitTests", "SaxoniteCoreUnitTests\SaxoniteCoreUnitTests.csproj", "{CA680625-E334-4F76-881B-1327A2F9FE86}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution files", "Solution files", "{BD66A338-2D41-4B64-86B0-670F4F42A489}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{F646283D-F7C2-42DD-AB1E-E741455664BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F646283D-F7C2-42DD-AB1E-E741455664BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F646283D-F7C2-42DD-AB1E-E741455664BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F646283D-F7C2-42DD-AB1E-E741455664BD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{DA383586-69E3-40EB-A20D-AC1AD24E336C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{DA383586-69E3-40EB-A20D-AC1AD24E336C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DA383586-69E3-40EB-A20D-AC1AD24E336C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DA383586-69E3-40EB-A20D-AC1AD24E336C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{38FBE932-C1D0-4A28-9172-1466347FD62B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{38FBE932-C1D0-4A28-9172-1466347FD62B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{38FBE932-C1D0-4A28-9172-1466347FD62B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{38FBE932-C1D0-4A28-9172-1466347FD62B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9C5309DA-3205-4423-BE13-923EE61370A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9C5309DA-3205-4423-BE13-923EE61370A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9C5309DA-3205-4423-BE13-923EE61370A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9C5309DA-3205-4423-BE13-923EE61370A6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E211FE36-108E-4D07-BD30-799A156DDDBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E211FE36-108E-4D07-BD30-799A156DDDBB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E211FE36-108E-4D07-BD30-799A156DDDBB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E211FE36-108E-4D07-BD30-799A156DDDBB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{53A962E2-F26B-4DAD-B0F0-EA0B24B520A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{53A962E2-F26B-4DAD-B0F0-EA0B24B520A1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{53A962E2-F26B-4DAD-B0F0-EA0B24B520A1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{53A962E2-F26B-4DAD-B0F0-EA0B24B520A1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{89CFDAA1-15C9-45CF-8FBF-88E36E0E32CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{89CFDAA1-15C9-45CF-8FBF-88E36E0E32CF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{89CFDAA1-15C9-45CF-8FBF-88E36E0E32CF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{89CFDAA1-15C9-45CF-8FBF-88E36E0E32CF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{CFF91B48-04B7-4075-A5B2-FF3FC0B22BC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CFF91B48-04B7-4075-A5B2-FF3FC0B22BC1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CFF91B48-04B7-4075-A5B2-FF3FC0B22BC1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CFF91B48-04B7-4075-A5B2-FF3FC0B22BC1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C1A3E949-B5EA-4A77-9411-5D8B350056D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C1A3E949-B5EA-4A77-9411-5D8B350056D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C1A3E949-B5EA-4A77-9411-5D8B350056D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C1A3E949-B5EA-4A77-9411-5D8B350056D6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{BBEEBD5B-7D00-4024-9626-D8B2FBF248A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BBEEBD5B-7D00-4024-9626-D8B2FBF248A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BBEEBD5B-7D00-4024-9626-D8B2FBF248A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BBEEBD5B-7D00-4024-9626-D8B2FBF248A4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{CA680625-E334-4F76-881B-1327A2F9FE86}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CA680625-E334-4F76-881B-1327A2F9FE86}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CA680625-E334-4F76-881B-1327A2F9FE86}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CA680625-E334-4F76-881B-1327A2F9FE86}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {0DFC2321-53B9-493B-83D3-2ACD1C47C732}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,29 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
|
||||
<RootNamespace>Saxonite.Net.Core</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
|
||||
<PackageReference Include="Moq" Version="4.16.1" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="2.1.0" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="2.1.0" />
|
||||
<PackageReference Include="coverlet.collector" Version="1.2.0" />
|
||||
</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="..\SaxoniteCoreWebAdmin\SaxoniteCoreWebAdmin.csproj" />
|
||||
<ProjectReference Include="..\SaxoniteCoreWeb\SaxoniteCoreWeb.csproj" />
|
||||
<ProjectReference Include="..\SaxoniteStandardBase\SaxoniteStandardBase.csproj" />
|
||||
<ProjectReference Include="..\SaxoniteStandardDbas\SaxoniteStandardDbas.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,14 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Saxonite.Net.Core.UnitTests.Data.Population
|
||||
{
|
||||
//=======================================================================
|
||||
[TestClass]
|
||||
class SaxoniteRegionPopulation_Tester
|
||||
{
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using Saxonite.Net.Core.Web.Admin.App;
|
||||
using Saxonite.Net.Standard.Base.App;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Saxonite.Net.Core.UnitTests.Web.Admin.App
|
||||
{
|
||||
//=======================================================================
|
||||
[TestClass]
|
||||
public class SaxApplication_Admin_Tester
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Application.
|
||||
private class SaxApplication_Admin_Test : SaxApplication_Admin
|
||||
{
|
||||
public SaxApplication_Admin_Test(IConfiguration configuration, IWebHostEnvironment env) :
|
||||
base(configuration)
|
||||
{
|
||||
InitEnvironment(env);
|
||||
}
|
||||
}
|
||||
private static SaxApplication_Admin_Test app;
|
||||
public static SaxApplication_Admin App
|
||||
{
|
||||
get
|
||||
{
|
||||
if (app == null)
|
||||
{
|
||||
var mockConfiguration = new Mock<IConfiguration>();
|
||||
|
||||
var mockEnvironment = new Mock<IWebHostEnvironment>();
|
||||
mockEnvironment.Setup(m => m.EnvironmentName).Returns(Environments.Development);
|
||||
mockEnvironment.Setup(m => m.ContentRootPath).Returns(@"D:\Sarand\Projects\Users\AHoward\VS2019\Saxonite\Workspace\SaxoniteCoreWebAdmin");
|
||||
|
||||
app = new SaxApplication_Admin_Test(mockConfiguration.Object, mockEnvironment.Object);
|
||||
}
|
||||
return app;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Internals - component managers etc.
|
||||
private static SaxInternals internals;
|
||||
public static SaxInternals Internals { get { if (internals == null) internals = new SaxInternals(App.SessionContext); return internals; } }
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Newtonsoft.Json;
|
||||
using Sarand.Net.Standard.Base.Bus;
|
||||
using Saxonite.Net.Core.Web.Admin.App;
|
||||
using Saxonite.Net.Core.Web.Base.Controllers;
|
||||
using Saxonite.Net.Standard.Base.App;
|
||||
using Saxonite.Net.Standard.Base.Bus;
|
||||
using Saxonite.Net.Standard.Base.Mgr;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Saxonite.Net.Core.UnitTests.Web.Admin.App
|
||||
{
|
||||
//=======================================================================
|
||||
[TestClass]
|
||||
public class SaxSessionData_Tester
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Properties.
|
||||
public static SaxApplication_Admin App { get { return SaxApplication_Admin_Tester.App; } }
|
||||
public static SaxInternals Internals { get { return SaxApplication_Admin_Tester.Internals; } }
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
[TestMethod]
|
||||
public void Test_SmmSessionData_Serialization()
|
||||
{
|
||||
// User account.
|
||||
SaxAccountMgr accountMgr = Internals.AccountMgr;
|
||||
|
||||
// User account.
|
||||
SaxUserAccount userAccount = accountMgr.CreateUserAccount();
|
||||
userAccount.Id = new SysId(45678);
|
||||
userAccount.EmailAddress = "john.smith@acme.co.uk";
|
||||
userAccount.FirstName = "John";
|
||||
userAccount.LastName = "Smith";
|
||||
|
||||
// User Roles.
|
||||
List<SaxUserRole> userRoles = new List<SaxUserRole>();
|
||||
SaxUserRole userRole = accountMgr.CreateUserRole(userAccount.Id, Sax.UserRole.Journalist);
|
||||
userRoles.Add(userRole);
|
||||
|
||||
// Create user session and verify user id.
|
||||
SaxSessionData_Admin sessionData = new SaxSessionData_Admin(App.SessionContext, userAccount, userRoles);
|
||||
Assert.AreEqual(SysId.ToInt(sessionData.User.UserAccountId), SysId.ToInt(sessionData.User.UserAccountId));
|
||||
Assert.AreEqual(userAccount.FirstName, sessionData.User.FirstName);
|
||||
Assert.AreEqual(userAccount.LastName, sessionData.User.LastName);
|
||||
Assert.IsTrue(sessionData.ValidUserSession);
|
||||
Assert.AreNotEqual("", sessionData.User.DisplayName);
|
||||
|
||||
// Serialise session data using same method as base controller.
|
||||
String sessionDataJson = JsonConvert.SerializeObject(sessionData);
|
||||
Assert.AreNotEqual("", sessionDataJson);
|
||||
|
||||
// Deserialise session data using same method as base controller.
|
||||
SaxSessionData_Admin sessionDataDeserialized =
|
||||
JsonConvert.DeserializeObject<SaxSessionData_Admin>(
|
||||
sessionDataJson,
|
||||
new JsonSerializerSettings { ContractResolver = new SaxController_Base.SessionStorage.SaxSessionDataContractResolver() }
|
||||
);
|
||||
Assert.IsNotNull(sessionDataDeserialized);
|
||||
|
||||
// Validate deserialised values.
|
||||
Assert.AreEqual(App.SessionContext.Index, sessionDataDeserialized.SessionContext.Index);
|
||||
Assert.AreEqual(Sax.Application.Admin, sessionDataDeserialized.SessionContext.Application);
|
||||
Assert.AreEqual(SysId.ToInt(sessionDataDeserialized.User.UserAccountId), SysId.ToInt(sessionDataDeserialized.User.UserAccountId));
|
||||
Assert.AreEqual(SysId.ToInt(sessionData.User.UserAccountId), SysId.ToInt(sessionDataDeserialized.User.UserAccountId));
|
||||
Assert.AreEqual(sessionData.User.EmailAddress, sessionDataDeserialized.User.EmailAddress);
|
||||
Assert.AreEqual(sessionData.User.FirstName, sessionDataDeserialized.User.FirstName);
|
||||
Assert.AreEqual(sessionData.User.LastName, sessionDataDeserialized.User.LastName);
|
||||
Assert.AreEqual(sessionData.User.DisplayName, sessionDataDeserialized.User.DisplayName);
|
||||
// User roles.
|
||||
Assert.AreEqual(userRoles.Count, sessionDataDeserialized.User.Roles.Count);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Sarand.Net.Standard.Base.App;
|
||||
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.Data;
|
||||
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.Text;
|
||||
|
||||
namespace Saxonite.Net.Core.Web.Base.App
|
||||
{
|
||||
//=======================================================================
|
||||
public abstract class SaxApplication_Web : SaxApplication_Base
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Data members.
|
||||
protected Sax.InstanceType instanceType;
|
||||
protected String environmentName = "";
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Protected construction.
|
||||
protected SaxApplication_Web()
|
||||
{
|
||||
WebApp = this;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Properties.
|
||||
public override SaxSystem System { get { return System_Web; } }
|
||||
public SaxSystem_Web System_Web { get; protected set; }
|
||||
public static SaxApplication_Web WebApp { get; private set; }
|
||||
// Application instance.
|
||||
public override Sax.InstanceType InstanceType { get { return instanceType; } }
|
||||
public override String EnvironmentName { get { return environmentName; } }
|
||||
public bool Development { get; protected set; }
|
||||
public String LoggingRootFolder { get; protected set; } = "";
|
||||
// Application components.
|
||||
public abstract String SessionCookieName { get; }
|
||||
public virtual SaxSessionContext SessionContext { get; protected set; }
|
||||
public abstract SaxSessionConfig SessionConfig { get; }
|
||||
public override SaxConsoleHelper ConsoleHelper { get { return ConsoleHelper_Web; } }
|
||||
public SaxConsoleHelper_Web ConsoleHelper_Web { get; protected set; }
|
||||
public override IDatabaseInfo CreateCoreDbInfo(SaxSessionContext sessionContext) { return new DbDataItem.DatabaseInfo(GetCoreDatabase(sessionContext)); }
|
||||
// Configuration.
|
||||
public IConfiguration Configuration { get; protected set; }
|
||||
public IConfiguration SaxConfiguration { get; protected set; }
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Logging.
|
||||
protected void LogApplicationStart(String fn)
|
||||
{
|
||||
// Log application start.
|
||||
List<StringPair> applicationParams = new List<StringPair>();
|
||||
applicationParams.Add("Host", SaxSystem.ComputerName);
|
||||
applicationParams.Add("Version", Sax.Version.GetFullVersion());
|
||||
applicationParams.Add("Instance", InstanceType.ToString() + (!StringUtil.IsEmpty(SessionConfig.Instance) ? " [" + SessionConfig.Instance + "]" : ""));
|
||||
applicationParams.Add("Configuration", SaxSystem.DebugMode ? "Debug" : "Release");
|
||||
applicationParams.Add("Environment", EnvironmentName);
|
||||
applicationParams.Add("Database", SessionConfig.CoreDatabase.Location);
|
||||
applicationParams.Add("Application root", SaxSystem.ApplicationFolder);
|
||||
if (!StringUtil.IsEmpty(SessionConfig.Documents.RootFolder))
|
||||
applicationParams.Add("Documents root", SessionConfig.Documents.RootFolder);
|
||||
applicationParams.Add("Email context", SessionConfig.Email.Context + (!StringUtil.IsEmpty(EmailConfig.SMTP.Server) ? " [" + EmailConfig.SMTP.Server + "]" : ""));
|
||||
SaLogger.Info(fn + " Application start." + applicationParams.Select(item => StringUtil.PadRight(item.Item1 + ":", 20, ' ') + item.Item2).Concat("", "\n\t"), true);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
protected void InitialiseTemporaryLoggingFolder()
|
||||
{
|
||||
// Default logging folder - in case of startup failure.
|
||||
try
|
||||
{
|
||||
foreach (String folder in new String[] { @"D:\Sarand\Projects\User\AHoward\VS2017\Saxonite\Logs" })
|
||||
{
|
||||
if (DirectoryUtil.DirectoryExists(folder))
|
||||
{
|
||||
SaLogger.LogFolder = folder;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using Saxonite.Net.Standard.Base.Consoles;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Saxonite.Net.Core.Web.Base.App
|
||||
{
|
||||
//=======================================================================
|
||||
public static class SaxGlobals_Web
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// System.
|
||||
public static SaxSystem_Web System { get { return SaxGlobals_Web.System as SaxSystem_Web; } }
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Application.
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
using Microsoft.AspNetCore.Html;
|
||||
using Microsoft.AspNetCore.Mvc.Localization;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Sarand.Net.Standard.Base.App;
|
||||
using Sarand.Net.Standard.Base.Core;
|
||||
using Saxonite.Net.Standard.Base.App;
|
||||
using Saxonite.Net.Standard.Base.Bus;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Resources;
|
||||
using System.Web;
|
||||
|
||||
namespace Saxonite.Net.Core.Web.Base.App
|
||||
{
|
||||
//=======================================================================
|
||||
public abstract class SaxLocaliser
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Data members.
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>
|
||||
/// Localisers are supplied by derived object.
|
||||
/// </summary>
|
||||
protected SaxLocaliser()
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Properties.
|
||||
public abstract IStringLocalizer MainProperties { get; }
|
||||
public virtual List<IStringLocalizer> AlternativeProperties { get; } = new List<IStringLocalizer>();
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Generic phrase translation.
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>Retrieve localised text.</summary>
|
||||
public LocalizedString GetText(string key)
|
||||
{
|
||||
return GetText(key, new object[] { });
|
||||
}
|
||||
public LocalizedString GetText(string key, object param)
|
||||
{
|
||||
return GetText(key, new object[] { param });
|
||||
}
|
||||
public LocalizedString GetText(string key, object param1, object param2)
|
||||
{
|
||||
return GetText(key, new object[] { param1, param2 });
|
||||
}
|
||||
public LocalizedString GetText(string key, object[] parameters)
|
||||
{
|
||||
LocalizedString text = MainProperties.GetString(key, parameters);
|
||||
if (text.ResourceNotFound)
|
||||
{
|
||||
SaLogger.Debug(GetType().Name + ".GetText Key not found: " + key);
|
||||
// Look up key in alternative property files.
|
||||
foreach (IStringLocalizer properties in AlternativeProperties ?? new List<IStringLocalizer>())
|
||||
{
|
||||
text = properties.GetString(key, parameters);
|
||||
if (!text.ResourceNotFound)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
// Square bracket indexing.
|
||||
public LocalizedString this[String key]
|
||||
{
|
||||
get { return GetText(key); }
|
||||
}
|
||||
public LocalizedString this[String key, object param]
|
||||
{
|
||||
get { return GetText(key, param); }
|
||||
}
|
||||
public LocalizedString this[String key, object param1, object param2]
|
||||
{
|
||||
get { return GetText(key, param1, param2); }
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>
|
||||
/// Localised HTML text.
|
||||
/// <para>Line delimiters changed to '<br/>'.</para>
|
||||
/// </summary>
|
||||
public HtmlString GetHtmlText(string key, String param1 = "", String param2 = "")
|
||||
{
|
||||
return GetHtmlText(key, new Object[] { param1, param2 });
|
||||
}
|
||||
/// <summary>
|
||||
/// Localised HTML text.
|
||||
/// <para>Line delimiters changed to '<br/>'.</para>
|
||||
/// </summary>
|
||||
public HtmlString GetHtmlText(string key, object[] parameters)
|
||||
{
|
||||
LocalizedString localisedText = GetText(key, parameters);
|
||||
String value = localisedText.Value;
|
||||
// Replace all line delimiters.
|
||||
value = value.Replace("\r\n", "\n").Replace("\n", "<br/>").Replace("\r", "<br/>");
|
||||
return new HtmlString(value);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>
|
||||
/// Localised javascript string.
|
||||
/// <para>All quotes are escaped.</para>
|
||||
/// </summary>
|
||||
public LocalizedString GetJavascriptText(string key, String param1 = "", String param2 = "")
|
||||
{
|
||||
LocalizedString localisedText = GetText(key, param1, param2);
|
||||
return new LocalizedString(localisedText, EscapeJavascriptText(localisedText.Value), localisedText.ResourceNotFound);
|
||||
}
|
||||
public String EscapeJavascriptText(string value)
|
||||
{
|
||||
// Escape all quotes.
|
||||
value = value.Replace("'", "\\'").Replace("\"", "\\\"");
|
||||
return value;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Specific phrase translation.
|
||||
//-------------------------------------------------------------------
|
||||
// Status.
|
||||
public String GetStatusCaption(SaStatus status)
|
||||
{
|
||||
// ToDo Status description localisation.
|
||||
return SacsSystem.GetStatusDesc(status);
|
||||
}
|
||||
|
||||
// Region type.
|
||||
private List<TypeCodeDesc> regionTypes;
|
||||
public String GetRegionTypeCaption(Sax.RegionType regionType)
|
||||
{
|
||||
if ((int)regionType < 1) return "";
|
||||
if (regionTypes == null)
|
||||
regionTypes = SaxSystem.GetCodesOfType(Sax.CodeType.RegionType);
|
||||
// ToDo Region type localisation.
|
||||
return regionTypes.GetDescForType(regionType);
|
||||
}
|
||||
|
||||
// User role.
|
||||
private List<TypeCodeDesc> userRoles;
|
||||
public String GetUserRoleCaption(Sax.UserRole userRole)
|
||||
{
|
||||
if ((int)userRole < 1) return "";
|
||||
if (userRoles == null)
|
||||
userRoles = SaxSystem.GetCodesOfType(Sax.CodeType.UserRole);
|
||||
// ToDo User Role localisation.
|
||||
return userRoles.GetDescForType(userRole);
|
||||
}
|
||||
|
||||
// Organisation role.
|
||||
private List<TypeCodeDesc> organisationRoles;
|
||||
public String GetOrganisationRoleCaption(Sax.OrganisationRole organisationRole)
|
||||
{
|
||||
if ((int)organisationRole < 1) return "";
|
||||
if (organisationRoles == null)
|
||||
organisationRoles = SaxSystem.GetCodesOfType(Sax.CodeType.OrganisationRole);
|
||||
// ToDo Organisation Role localisation.
|
||||
return organisationRoles.GetDescForType(organisationRole);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Region.
|
||||
public String GetRegionName(SaxRegion region)
|
||||
{
|
||||
if (region == null) return "";
|
||||
// ToDo Region name localisation.
|
||||
return region.Name;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Sport.
|
||||
public String GetSportName(SaxSport sport)
|
||||
{
|
||||
if (sport == null) return "";
|
||||
// ToDo Sport name localisation.
|
||||
return sport.Name;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Sarand.Net.Standard.Base.Core;
|
||||
using Sarand.Net.Standard.Base.Utils;
|
||||
using Saxonite.Net.Standard.Base.App;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Saxonite.Net.Core.Web.Base.App
|
||||
{
|
||||
//=======================================================================
|
||||
public class SaxSessionConfig
|
||||
{
|
||||
//---------------------------------------------------------------
|
||||
// Data members.
|
||||
private readonly List<String> environments = new List<string>();
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Construction.
|
||||
public SaxSessionConfig (String context, ConfigurationSection configSection, SaxSessionConfig defaultConfig)
|
||||
{
|
||||
Context = context;
|
||||
populateDefaults(defaultConfig);
|
||||
PopulateConfig(configSection);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
private void populateDefaults(SaxSessionConfig defaultConfig)
|
||||
{
|
||||
if (defaultConfig == null) return;
|
||||
Security.Password.MinLength = defaultConfig.Security.Password.MinLength;
|
||||
Security.SignIn.KeepLoggedIn.Allowed = defaultConfig.Security.SignIn.KeepLoggedIn.Allowed;
|
||||
Security.SignIn.KeepLoggedIn.CookieExpiresDays = defaultConfig.Security.SignIn.KeepLoggedIn.CookieExpiresDays;
|
||||
}
|
||||
private void PopulateConfig(ConfigurationSection configSection)
|
||||
{
|
||||
if (configSection != null)
|
||||
{
|
||||
// Environments.
|
||||
foreach (ConfigurationSection environment in configSection.GetSection("Environments").GetChildren())
|
||||
environments.Add(StringUtil.TrimAll(environment.Value));
|
||||
Instance = StringUtil.OnEmpty(configSection["Instance"], Instance);
|
||||
// Security.
|
||||
IConfigurationSection security = configSection.GetSection("Security");
|
||||
if (security.Exists())
|
||||
{
|
||||
// Security - Password.
|
||||
IConfigurationSection securityPassword = security.GetSection("Password");
|
||||
if (securityPassword.Exists())
|
||||
{
|
||||
Security.Password.MinLength = StringUtil.ToInt(securityPassword["MinLength"], Security.Password.MinLength);
|
||||
}
|
||||
// Security - Sign-Up.
|
||||
IConfigurationSection securitySignUp = security.GetSection("SignUp");
|
||||
if (securitySignUp.Exists())
|
||||
{
|
||||
}
|
||||
// Security - Sign-In.
|
||||
IConfigurationSection securitySignIn = security.GetSection("SignIn");
|
||||
if (securitySignIn.Exists())
|
||||
{
|
||||
IConfigurationSection keepLoggedIn = securitySignIn.GetSection("SignIn");
|
||||
if (keepLoggedIn.Exists())
|
||||
{
|
||||
Security.SignIn.KeepLoggedIn.Allowed = StringUtil.ToBool(StringUtil.OnEmpty(keepLoggedIn["Allowed"], Security.SignIn.KeepLoggedIn.Allowed.ToString()));
|
||||
Security.SignIn.KeepLoggedIn.CookieExpiresDays = StringUtil.ToInt(keepLoggedIn["CookieExpiresDays"], Security.SignIn.KeepLoggedIn.CookieExpiresDays);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Email.
|
||||
IConfigurationSection email = configSection.GetSection("Email");
|
||||
if (email.Exists())
|
||||
{
|
||||
Email.Context = email["Context"];
|
||||
PopulateEmailConfig(Email.ContactUs, email.GetSection("ContactUs"));
|
||||
PopulateEmailConfig(Email.Signup_Activate, email.GetSection("Signup").GetSection("Activate"));
|
||||
PopulateEmailConfig(Email.Signup_Welcome, email.GetSection("Signup").GetSection("Welcome"));
|
||||
}
|
||||
// Documents.
|
||||
IConfigurationSection documents = configSection.GetSection("Documents");
|
||||
if (documents.Exists())
|
||||
{
|
||||
Documents.RootFolder = StringUtil.OnEmpty(documents["RootFolder"], Documents.RootFolder);
|
||||
}
|
||||
// Logging.
|
||||
IConfigurationSection logging = configSection.GetSection("Logging");
|
||||
if (logging.Exists())
|
||||
{
|
||||
Logging.RootFolder = StringUtil.OnEmpty(logging["RootFolder"], Logging.RootFolder);
|
||||
}
|
||||
// Database.
|
||||
extractDatabaseConfig(CoreDatabase, configSection.GetSection("CoreDatabase"));
|
||||
// Variants.
|
||||
foreach (ConfigurationSection variantSection in configSection.GetSection("Variants").GetChildren())
|
||||
{
|
||||
// Variants override selected values but must match the application root folder to identify a specific instance.
|
||||
String applicationRootPath = variantSection["ApplicationRootFolder"];
|
||||
if (StringUtil.IsEmpty(applicationRootPath)) continue;
|
||||
if (!SaxSystem.ApplicationFolder.EqualsCI(applicationRootPath)) continue;
|
||||
PopulateConfig(variantSection);
|
||||
}
|
||||
}
|
||||
}
|
||||
private void PopulateEmailConfig(EmailConfig.SpecificEmailConfig config, IConfigurationSection emailSection)
|
||||
{
|
||||
if (!emailSection.Exists()) return;
|
||||
foreach (var addressTo in emailSection.GetSection("AddressesTo").GetChildren())
|
||||
config.AddressesTo.Add(StringUtil.TrimAll(addressTo.Value));
|
||||
foreach (var addressCc in emailSection.GetSection("AddressesCc").GetChildren())
|
||||
config.AddressesCc.Add(StringUtil.TrimAll(addressCc.Value));
|
||||
foreach (var addressBcc in emailSection.GetSection("AddressesBcc").GetChildren())
|
||||
config.AddressesBcc.Add(StringUtil.TrimAll(addressBcc.Value));
|
||||
}
|
||||
private void extractDatabaseConfig(DatabaseConfig database, IConfigurationSection databaseSection)
|
||||
{
|
||||
if (!databaseSection.Exists()) return;
|
||||
database.Location = StringUtil.OnEmpty(databaseSection["Location"], database.Location);
|
||||
database.User = StringUtil.OnEmpty(databaseSection["User"], database.User);
|
||||
database.Password = StringUtil.OnEmpty(databaseSection["Password"], database.Password);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Properties.
|
||||
public String Context { get; }
|
||||
public List<String> Environments { get { return new List<String>(environments); } }
|
||||
public String Instance { get; set; } = "";
|
||||
// Security.
|
||||
public class SecurityConfig
|
||||
{
|
||||
public class PasswordConfig
|
||||
{
|
||||
public int MinLength { get; set; }
|
||||
}
|
||||
public PasswordConfig Password { get; } = new PasswordConfig();
|
||||
public class SignUpConfig
|
||||
{
|
||||
public class ActivationConfig
|
||||
{
|
||||
public int EmailLinkExpiresMins = Sax.Config.Security.Activation.EmailLinkExpiresMins_Default;
|
||||
}
|
||||
public ActivationConfig Activation { get; } = new ActivationConfig();
|
||||
}
|
||||
public SignUpConfig SignUp { get; } = new SignUpConfig();
|
||||
public class SignInConfig
|
||||
{
|
||||
public class PasswordRequestConfig
|
||||
{
|
||||
public int EmailLinkExpiresMins = Sax.Config.Security.Activation.EmailLinkExpiresMins_Default;
|
||||
}
|
||||
public PasswordRequestConfig PasswordRequest { get; } = new PasswordRequestConfig();
|
||||
public class KeepLoggedInConfig
|
||||
{
|
||||
public bool Allowed { get; set; } = Sax.Config.Security.SignIn.KeepLoggedIn.Allowed_Default;
|
||||
public int CookieExpiresDays { get; set; } = Sax.Config.Security.SignIn.KeepLoggedIn.CookieExpiresDays_Default;
|
||||
}
|
||||
public KeepLoggedInConfig KeepLoggedIn { get; } = new KeepLoggedInConfig();
|
||||
}
|
||||
public SignInConfig SignIn { get; } = new SignInConfig();
|
||||
}
|
||||
public SecurityConfig Security { get; } = new SecurityConfig();
|
||||
// Email.
|
||||
public class EmailConfig
|
||||
{
|
||||
public class SpecificEmailConfig
|
||||
{
|
||||
public List<String> AddressesTo { get; } = new List<string>();
|
||||
public List<String> AddressesCc { get; } = new List<string>();
|
||||
public List<String> AddressesBcc { get; } = new List<string>();
|
||||
}
|
||||
public SpecificEmailConfig ContactUs { get; } = new SpecificEmailConfig();
|
||||
public SpecificEmailConfig Signup_Activate { get; } = new SpecificEmailConfig();
|
||||
public SpecificEmailConfig Signup_Welcome { get; } = new SpecificEmailConfig();
|
||||
public String Context { get; internal set; }
|
||||
}
|
||||
public EmailConfig Email { get; } = new EmailConfig();
|
||||
// Databases.
|
||||
public class DatabaseConfig
|
||||
{
|
||||
public String Location { get; internal set; }
|
||||
public String User { get; internal set; }
|
||||
public String Password { get; internal set; }
|
||||
}
|
||||
public DatabaseConfig CoreDatabase { get; } = new DatabaseConfig();
|
||||
// Documents.
|
||||
public class DocumentsConfig
|
||||
{
|
||||
public String RootFolder { get; internal set; }
|
||||
}
|
||||
public DocumentsConfig Documents { get; } = new DocumentsConfig();
|
||||
// Logging.
|
||||
public class LoggingConfig
|
||||
{
|
||||
public String RootFolder { get; internal set; }
|
||||
}
|
||||
public LoggingConfig Logging { get; } = new LoggingConfig();
|
||||
|
||||
//---------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using Saxonite.Net.Standard.Base.App;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Saxonite.Net.Core.Web.Base.App
|
||||
{
|
||||
//=======================================================================
|
||||
public class SaxSessionContext_Web : SaxSessionContext
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>Blank construction - used for deserilisation of session data.</summary>
|
||||
protected SaxSessionContext_Web () : base() { }
|
||||
public SaxSessionContext_Web (ushort sesionContextIndex, Sax.Application application) :
|
||||
base(sesionContextIndex, application)
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using Saxonite.Net.Standard.Base.App;
|
||||
using Saxonite.Net.Standard.Base.Bus;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Saxonite.Net.Core.Web.Base.App
|
||||
{
|
||||
//=======================================================================
|
||||
public class SaxSessionData_Web : SaxSessionData
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Protected construction.
|
||||
protected SaxSessionData_Web(SaxSessionContext sessionContext, SaxUserAccount userAccount, List<SaxUserRole> userRoles) :
|
||||
base(sessionContext, userAccount, userRoles)
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Localization;
|
||||
using Microsoft.AspNetCore.Mvc.Razor;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Sarand.Net.Standard.Base.App;
|
||||
using Sarand.Net.Standard.Base.Core;
|
||||
using Saxonite.Net.Core.Web.Base.Middleware;
|
||||
using Saxonite.Net.Standard.Base.App;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
|
||||
namespace Saxonite.Net.Core.Web.Base.App
|
||||
{
|
||||
//=======================================================================
|
||||
public class SaxStartup
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Data members.
|
||||
// Localisation.
|
||||
private static RequestCulture defaultRequestCulture = new RequestCulture("en-GB", "en-GB");
|
||||
private static List<CultureInfo> supportedCultures = new List<CultureInfo>
|
||||
{
|
||||
new CultureInfo("en-GB"),
|
||||
new CultureInfo("en-US"),
|
||||
new CultureInfo("en"),
|
||||
new CultureInfo("fr-FR"),
|
||||
new CultureInfo("fr"),
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Protected construction.
|
||||
protected SaxStartup(IConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Properties.
|
||||
public IConfiguration Configuration { get; }
|
||||
public static RequestCulture DefaultRequestCulture { get { return defaultRequestCulture; } }
|
||||
public static List<CultureInfo> SupportedCultures { get { return new List<CultureInfo>(supportedCultures); } }
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
protected class ConfigureServicesConfig
|
||||
{
|
||||
public IServiceCollection services;
|
||||
public String sessionCookieName;
|
||||
public bool sessionCookieHttpOnly;
|
||||
public TimeSpan sessionIdleTimeout;
|
||||
}
|
||||
protected void ConfigureServices(ConfigureServicesConfig config)
|
||||
{
|
||||
// Added to support memory caching of sessions.
|
||||
config.services.AddMemoryCache();
|
||||
//config.services.AddDistributedMemoryCache(); - for multiple web servers.
|
||||
// Online web application cookie definition.
|
||||
config.services.AddSession
|
||||
(options =>
|
||||
{
|
||||
options.Cookie.Name = config.sessionCookieName;
|
||||
options.Cookie.HttpOnly = config.sessionCookieHttpOnly;
|
||||
options.IdleTimeout = config.sessionIdleTimeout;
|
||||
// Override cookie policy for session cookie. Ref. config.app.UseCookiePolicy()
|
||||
options.Cookie.IsEssential = true;
|
||||
}
|
||||
);
|
||||
|
||||
// Localisation properties folder.
|
||||
String resourcesPath = "Localisation";
|
||||
config.services.AddLocalization(opts => { opts.ResourcesPath = resourcesPath; });
|
||||
|
||||
// Modify pages during debug.
|
||||
#if DEBUG
|
||||
if (SaxSystem.DebugMode)
|
||||
// If the following line throws an error such as:
|
||||
// 'imvcbuilder' does not contain a definition for 'addrazorruntimecompilation'
|
||||
// Revert the NuGet Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation package
|
||||
// to a previous version and then reinstal the latest version.
|
||||
config.services.AddRazorPages().AddRazorRuntimeCompilation();
|
||||
#endif
|
||||
|
||||
config.services.AddMvc
|
||||
(options =>
|
||||
{
|
||||
// Model binding - intercepting form values being bound to models.
|
||||
options.ModelBinderProviders.Insert(0, new SaxModelBinderProvider());
|
||||
// Blank form values to be populated to model as empty strings rather than null values.
|
||||
options.ModelMetadataDetailsProviders.Add(new SaxMetadataProvider());
|
||||
}
|
||||
)
|
||||
// View and data annotation localisations.
|
||||
.AddViewLocalization(
|
||||
LanguageViewLocationExpanderFormat.Suffix,
|
||||
opts => { opts.ResourcesPath = resourcesPath; })
|
||||
.AddDataAnnotationsLocalization();
|
||||
|
||||
// Configure localisation middleware.
|
||||
config.services.Configure<RequestLocalizationOptions>
|
||||
(opts =>
|
||||
{
|
||||
// Default culture to use for requests.
|
||||
opts.DefaultRequestCulture = DefaultRequestCulture;
|
||||
// Cultures supported by the application. Formatting numbers, dates, etc.
|
||||
opts.SupportedCultures = SupportedCultures;
|
||||
// UI cultures supported by the application. UI strings that have been localized.
|
||||
opts.SupportedUICultures = SupportedCultures;
|
||||
}
|
||||
);
|
||||
|
||||
// Allows access to the HttpContext inside a service.
|
||||
config.services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
protected class ConfigureConfig
|
||||
{
|
||||
public IApplicationBuilder app;
|
||||
public IHostApplicationLifetime hostApplicationLifetime;
|
||||
public IWebHostEnvironment env;
|
||||
}
|
||||
protected void Configure(ConfigureConfig config)
|
||||
{
|
||||
if (config.env.IsDevelopment())
|
||||
{
|
||||
config.app.UseDeveloperExceptionPage();
|
||||
}
|
||||
else
|
||||
{
|
||||
config.app.UseExceptionHandler("/Home/Error");
|
||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
||||
config.app.UseHsts();
|
||||
}
|
||||
|
||||
config.app.UseHttpsRedirection();
|
||||
config.app.UseStaticFiles();
|
||||
config.app.UseCookiePolicy();
|
||||
config.app.UseSession();
|
||||
config.app.UseRouting();
|
||||
//config.app.UseAuthorization();
|
||||
config.app.UseAntiforgeryTokens();
|
||||
|
||||
config.app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapControllerRoute(
|
||||
name: "default",
|
||||
pattern: "{controller=Home}/{action=Index}/{id?}");
|
||||
});
|
||||
|
||||
// Application events.
|
||||
// NB The shutdown events only fire if he application stops 'gracefully'.
|
||||
config.hostApplicationLifetime.ApplicationStarted.Register(OnApplicationStarted);
|
||||
config.hostApplicationLifetime.ApplicationStopping.Register(OnApplicationStopping);
|
||||
config.hostApplicationLifetime.ApplicationStopped.Register(OnApplicationStopped);
|
||||
|
||||
config.app.Use(async (context, next) =>
|
||||
{
|
||||
if (context.Request.Method.Equals(HttpMethod.Get.Method))
|
||||
{
|
||||
context.Response.GetTypedHeaders().CacheControl = new CacheControlHeaderValue()
|
||||
{
|
||||
Public = true,
|
||||
MaxAge = TimeSpan.FromSeconds(10)
|
||||
};
|
||||
//context.Response.Headers[HeaderNames.Vary] = new string[] { "Accept-Encoding" };
|
||||
}
|
||||
await next();
|
||||
});
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Application events.
|
||||
private readonly static String ApplicationEventLogPrefix = "### ";
|
||||
private void OnApplicationStarted()
|
||||
{
|
||||
SaLogger.Info(ApplicationEventLogPrefix + "Application started");
|
||||
}
|
||||
private void OnApplicationStopping()
|
||||
{
|
||||
SaLogger.Info(ApplicationEventLogPrefix + "Application stopping");
|
||||
}
|
||||
private void OnApplicationStopped()
|
||||
{
|
||||
SaLogger.Info(ApplicationEventLogPrefix + "Application stopped");
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
using Sarand.Net.Standard.Base.Core;
|
||||
using Saxonite.Net.Standard.Dbas.App;
|
||||
using Saxonite.Net.Standard.Base.App;
|
||||
using Saxonite.Net.Standard.Base.Mgr;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Sarand.Net.Standard.Base.App;
|
||||
using Sarand.Net.Standard.Base.Utils;
|
||||
|
||||
namespace Saxonite.Net.Core.Web.Base.App
|
||||
{
|
||||
//=======================================================================
|
||||
public class SaxSystem_Web : SaxSystem_Dbas
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Data members.
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>Static initialisation.</summary>
|
||||
static SaxSystem_Web()
|
||||
{
|
||||
InitComputerName();
|
||||
InitUserName();
|
||||
InitApplicationPath();
|
||||
InitApplicationFolder();
|
||||
InitAssemblyBuildAt();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Construction.
|
||||
public SaxSystem_Web() : base()
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>
|
||||
/// Computer name
|
||||
/// <para>Windows: System.Environment.MachineName</para>
|
||||
/// <para>Web: HttpContext.Current.Server.MachineName</para>
|
||||
/// <para>FQDN: System.Net.Dns.GetHostName()</para>
|
||||
/// </summary>
|
||||
private static void InitComputerName()
|
||||
{
|
||||
try
|
||||
{
|
||||
ComputerName = System.Environment.MachineName;
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
SaLogger.Warn("SaxSystem.InitComputerName Failed to initialise computer name from environment. " + ExceptionUtil.GetErrorStateMessage(exc));
|
||||
}
|
||||
try
|
||||
{
|
||||
if (StringUtil.IsEmpty(ComputerName))
|
||||
ComputerName = Environment.GetEnvironmentVariable("CUMPUTERNAME") ?? Environment.GetEnvironmentVariable("HOSTNAME");
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
SaLogger.Warn("SaxSystem.InitComputerName Failed to initialise computer name from environment variable. " + ExceptionUtil.GetErrorStateMessage(exc));
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>To be populated by application<para>Windows: Environment.UserName</para></summary>
|
||||
private static void InitUserName()
|
||||
{
|
||||
try
|
||||
{
|
||||
UserName = Environment.UserName;
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
SaLogger.Warn("SaxSystem.InitUserName Failed to initialise user name. " + ExceptionUtil.GetErrorStateMessage(exc));
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>
|
||||
/// Application Path.
|
||||
/// <para>Windows: System.Reflection.Assembly.GetEntryAssembly().Location;</para>
|
||||
/// </summary>
|
||||
private static void InitApplicationPath()
|
||||
{
|
||||
try
|
||||
{
|
||||
ApplicationPath = Assembly.GetEntryAssembly().Location;
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
SaLogger.Warn("SaxSystem.InitApplicationPath Failed to initialise application path. " + ExceptionUtil.GetErrorStateMessage(exc));
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>
|
||||
/// Application folder.
|
||||
/// <para>Windows: System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location);</para>
|
||||
/// </summary>
|
||||
private static void InitApplicationFolder()
|
||||
{
|
||||
try
|
||||
{
|
||||
ApplicationFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
SaLogger.Warn("SaxSystem.InitApplicationFolder Failed to initialise application folder. " + ExceptionUtil.GetErrorStateMessage(exc));
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>
|
||||
/// DateTime when the executing assembly was built.
|
||||
/// </summary>
|
||||
private static void InitAssemblyBuildAt()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Assembly build date.
|
||||
Assembly assembly = Assembly.GetExecutingAssembly();
|
||||
AssemblyBuildAt = File.GetLastWriteTime(assembly.Location);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
SaLogger.Warn("SaxSystem.InitAssemblyBuildAt Failed to initialise assembly build date. " + ExceptionUtil.GetErrorStateMessage(exc));
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using Saxonite.Net.Standard.Base.Consoles;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Saxonite.Net.Core.Web.Base.Consoles
|
||||
{
|
||||
//=======================================================================
|
||||
public abstract class SaxConsoleHelper_Web : SaxConsoleHelper
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Protected construction.
|
||||
protected SaxConsoleHelper_Web()
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using Sarand.Net.Standard.Base.App;
|
||||
using Sarand.Net.Standard.Base.Bus;
|
||||
using Sarand.Net.Standard.Base.Utils;
|
||||
using Saxonite.Net.Standard.Base.App;
|
||||
using Saxonite.Net.Standard.Base.Bus;
|
||||
using Saxonite.Net.Standard.Base.Consoles;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Saxonite.Net.Core.Web.Base.Consoles
|
||||
{
|
||||
//=======================================================================
|
||||
public class SaxDataCacheConsole_Web : SaxDataCacheConsole
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Data members.
|
||||
private readonly SaxInternals internals;
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Protected construction.
|
||||
protected SaxDataCacheConsole_Web(SaxSessionContext sessionContext) :
|
||||
base(sessionContext)
|
||||
{
|
||||
internals = new SaxInternals(sessionContext);
|
||||
}
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
internals.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Regions.
|
||||
public bool RegionsSupported { get; protected set; } = false;
|
||||
private class RegionsData
|
||||
{
|
||||
public Dictionary<SysId, SaxRegion> regions = null;
|
||||
public List<SaxRegion> activeRegions = null;
|
||||
}
|
||||
private readonly RegionsData regionsData = new RegionsData();
|
||||
public override List<SaxRegion> GetActiveRegions()
|
||||
{
|
||||
String fn = GetType().Name + ".ActiveRegions";
|
||||
if (!RegionsSupported) throw new NotSupportedException(fn + " Regions not supported.");
|
||||
List<SaxRegion> activeRegions = new List<SaxRegion>();
|
||||
lock (regionsData)
|
||||
{
|
||||
if (regionsData.regions == null) throw new NotSupportedException(fn + " Regions not populated.");
|
||||
activeRegions = new List<SaxRegion>(regionsData.activeRegions);
|
||||
}
|
||||
return activeRegions;
|
||||
}
|
||||
private bool IsRegionActive(SaxRegion region)
|
||||
{
|
||||
if (region == null) return false;
|
||||
if (!region.IsStatusActive) return false;
|
||||
if (SysId.IsValid(region.RegionIdParent))
|
||||
return IsRegionActive(regionsData.regions.GetValue(region.RegionIdParent, null));
|
||||
return true;
|
||||
}
|
||||
protected void RefreshRegions()
|
||||
{
|
||||
String fn = GetType().Name + ".RefreshRegions";
|
||||
if (!RegionsSupported) throw new NotSupportedException(fn + " Regions not supported.");
|
||||
SaLogger.Info(fn + " START.", false);
|
||||
DateTime startedAt = DateTime.Now;
|
||||
List<SaxRegion> oldRegions = null;
|
||||
Dictionary<SysId, SaxRegion> newRegions = new Dictionary<SysId, SaxRegion>();
|
||||
|
||||
// Query Regions.
|
||||
SaxRegion.Params prmRegion = new SaxRegion.Params();
|
||||
prmRegion.StatusCat = SaxRegion.StatusCat.NotDeleted;
|
||||
foreach (SaxRegion region in internals.ReferenceMgr.GetRegionList(prmRegion))
|
||||
newRegions[region.Id] = region;
|
||||
|
||||
// Replace the list of Regions.
|
||||
lock (regionsData)
|
||||
{
|
||||
oldRegions = regionsData.regions?.GetValues() ?? new List<SaxRegion>();
|
||||
regionsData.regions = newRegions;
|
||||
|
||||
// Populate list of active regions.
|
||||
regionsData.activeRegions = new List<SaxRegion>();
|
||||
foreach (SaxRegion region in regionsData.regions.GetValues())
|
||||
{
|
||||
if (IsRegionActive(region))
|
||||
{
|
||||
// Clone region to ensure internal list isn't modified.
|
||||
SaxRegion activeRegion = internals.ReferenceMgr.CreateRegion();
|
||||
activeRegion.SetData(region.GetData());
|
||||
regionsData.activeRegions.Add(activeRegion);
|
||||
}
|
||||
}
|
||||
|
||||
SaLogger.Info(fn + " END. Regions found: " + regionsData.regions.Count + ". Elapsed: " + DateUtil.FormatNumberOfSeconds((DateTime.Now - startedAt).TotalSeconds), true);
|
||||
}
|
||||
|
||||
// Dispose old regions.
|
||||
oldRegions.ClearAndDispose<SaxRegion>();
|
||||
oldRegions = null;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Sports.
|
||||
public bool SportsSupported { get; protected set; } = false;
|
||||
private class SportsData
|
||||
{
|
||||
public Dictionary<SysId, SaxSport> sports = null;
|
||||
public List<SaxSport> activeSports = null;
|
||||
}
|
||||
private readonly SportsData sportsData = new SportsData();
|
||||
public override List<SaxSport> GetActiveSports()
|
||||
{
|
||||
String fn = GetType().Name + ".ActiveSports";
|
||||
if (!SportsSupported) throw new NotSupportedException(fn + " Sports not supported.");
|
||||
List<SaxSport> activeSports = new List<SaxSport>();
|
||||
lock (sportsData)
|
||||
{
|
||||
if (sportsData.sports == null) throw new NotSupportedException(fn + " Sports not populated.");
|
||||
foreach (SaxSport sport in sportsData.sports.GetValues())
|
||||
{
|
||||
if (IsSportActive(sport))
|
||||
{
|
||||
// Clone sport to ensure internal list isn't modified.
|
||||
SaxSport activeSport = internals.ReferenceMgr.CreateSport();
|
||||
activeSport.SetData(sport.GetData());
|
||||
activeSports.Add(activeSport);
|
||||
}
|
||||
}
|
||||
}
|
||||
return activeSports;
|
||||
}
|
||||
private bool IsSportActive(SaxSport sport)
|
||||
{
|
||||
if (sport == null) return false;
|
||||
if (!sport.IsStatusActive) return false;
|
||||
return true;
|
||||
}
|
||||
protected void RefreshSports()
|
||||
{
|
||||
String fn = GetType().Name + ".RefreshSports";
|
||||
if (!SportsSupported) throw new NotSupportedException(fn + " Sports not supported.");
|
||||
SaLogger.Info(fn + " START.", false);
|
||||
DateTime startedAt = DateTime.Now;
|
||||
List<SaxSport> oldSports = null;
|
||||
Dictionary<SysId, SaxSport> newSports = new Dictionary<SysId, SaxSport>();
|
||||
|
||||
// Query Sports.
|
||||
SaxSport.Params prmSport = new SaxSport.Params();
|
||||
prmSport.StatusCat = SaxSport.StatusCat.NotDeleted;
|
||||
foreach (SaxSport sport in internals.ReferenceMgr.GetSportList(prmSport))
|
||||
newSports[sport.Id] = sport;
|
||||
|
||||
// Replace the list of Sports.
|
||||
lock (sportsData)
|
||||
{
|
||||
oldSports = sportsData.sports?.GetValues() ?? new List<SaxSport>();
|
||||
sportsData.sports = newSports;
|
||||
|
||||
// Populate list of active sports.
|
||||
sportsData.activeSports = new List<SaxSport>();
|
||||
foreach (SaxSport sport in sportsData.sports.GetValues())
|
||||
{
|
||||
if (IsSportActive(sport))
|
||||
sportsData.activeSports.Add(sport);
|
||||
}
|
||||
|
||||
SaLogger.Info(fn + " END. Sports found: " + sportsData.sports.Count + ". Elapsed: " + DateUtil.FormatNumberOfSeconds((DateTime.Now - startedAt).TotalSeconds), true);
|
||||
}
|
||||
|
||||
// Dispose old sports.
|
||||
oldSports.ClearAndDispose<SaxSport>();
|
||||
oldSports = null;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using Saxonite.Net.Standard.Base.Consoles;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Saxonite.Net.Core.Web.Base.Consoles
|
||||
{
|
||||
//=======================================================================
|
||||
public class SaxEmailConsole_Web : SaxEmailConsole
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Protected construction.
|
||||
public SaxEmailConsole_Web(SaxEmailConfig emailConfig) :
|
||||
base(emailConfig)
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,497 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Localization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
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.Base.App;
|
||||
using Saxonite.Net.Core.Web.Base.Consoles;
|
||||
using Saxonite.Net.Core.Web.Base.Models;
|
||||
using Saxonite.Net.Standard.Base.App;
|
||||
using Saxonite.Net.Standard.Base.Bus;
|
||||
using Saxonite.Net.Standard.Base.Consoles;
|
||||
using Saxonite.Net.Standard.Base.Mgr;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web;
|
||||
|
||||
namespace Saxonite.Net.Core.Web.Base.Controllers
|
||||
{
|
||||
//=======================================================================
|
||||
public abstract class SaxController_Base : Controller
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Data members.
|
||||
private SaxInternals internals;
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Protected construction.
|
||||
protected SaxController_Base(SaxLocaliser localiser)
|
||||
{
|
||||
Localiser = localiser;
|
||||
|
||||
// Initialise console (debug) logging listener.
|
||||
//if (Directory.Exists(App.LoggingRootFolder) && SaxSystem.DebugMode)
|
||||
// SaxConsoleListener.ConfigureDebugListener(App.LoggingRootFolder);
|
||||
if (DebugMode) SaLogger.Trace(GetType().Name + ".CTR");
|
||||
}
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Properties.
|
||||
public bool DebugMode { get; } = SacsSystem.DebugMode;
|
||||
protected SaxApplication_Web App { get { return SaxApplication_Web.WebApp; } }
|
||||
public SaxSessionConfig SessionConfig { get { return App.SessionConfig; } }
|
||||
public SaxSessionContext SessionContext { get { return App.SessionContext; } }
|
||||
protected SaxLocaliser Localiser { get; }
|
||||
/// <summary>
|
||||
/// Indicate whether session data should be stored by action.
|
||||
/// <para>Any action that might modify the session data must store it.</para>
|
||||
/// <para>Some actions such as scoring a password's complexity don't need to.</para>
|
||||
/// </summary>
|
||||
public virtual bool StoreSessionData { get; protected set; } = true;
|
||||
// Culture.
|
||||
private string LanguageIso2
|
||||
{
|
||||
get
|
||||
{
|
||||
var feature = HttpContext.Features.Get<IRequestCultureFeature>();
|
||||
return feature.RequestCulture.UICulture.TwoLetterISOLanguageName.ToLower();
|
||||
}
|
||||
}
|
||||
// Business components.
|
||||
public SaxInternals Internals { get { if (internals == null) internals = new SaxInternals(SessionContext); return internals; } }
|
||||
// Consoles.
|
||||
public SaxDataCacheConsole DataCacheConsole { get { return Internals.DataCacheConsole; } }
|
||||
// Managers.
|
||||
public SaxReferenceMgr ReferenceMgr { get { return Internals.ReferenceMgr; } }
|
||||
public SaxAccountMgr AccountMgr { get { return Internals.AccountMgr; } }
|
||||
public SaxActivityMgr ActivityMgr { get { return Internals.ActivityMgr; } }
|
||||
public SaxItemDataMgr ItemDataMgr { get { return Internals.ItemDataMgr; } }
|
||||
// System codes.
|
||||
public List<TypeCodeDesc> RegionTypes { get { return Internals.RegionTypes; } }
|
||||
public List<TypeCodeDesc> UserRoles { get { return Internals.UserRoles; } }
|
||||
public List<TypeCodeDesc> OrganisationTypes { get { return Internals.OrganisationTypes; } }
|
||||
public List<TypeCodeDesc> OrganisationCategories { get { return Internals.OrganisationCategories; } }
|
||||
public List<TypeCodeDesc> OrganisationRoles { get { return Internals.OrganisationRoles; } }
|
||||
// Email.
|
||||
public SaEmailConfig EmailConfig { get { return App.EmailConfig; } }
|
||||
public SaxEmailConsole EmailConsole { get { return Internals.EmailConsole; } }
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Redirection locations.
|
||||
//-------------------------------------------------------------------
|
||||
public static readonly String ParamName_SessionExpired = "SessionExpired";
|
||||
protected enum RedirectLocation
|
||||
{
|
||||
WebAppBase,
|
||||
WebAppHome,
|
||||
WebAppHome_SessionExpired,
|
||||
}
|
||||
protected String GetRedirectLocation(RedirectLocation redirectLocation)
|
||||
{
|
||||
string protocol = Request.Scheme;
|
||||
var parameters = new RouteValueDictionary();
|
||||
String homeAction = Url.Action("Index", "Home", parameters, protocol);
|
||||
switch (redirectLocation)
|
||||
{
|
||||
case RedirectLocation.WebAppBase:
|
||||
return Regex.Replace(homeAction, "\\/$", "") + "/";
|
||||
case RedirectLocation.WebAppHome:
|
||||
return homeAction;
|
||||
case RedirectLocation.WebAppHome_SessionExpired:
|
||||
// Set the session expired parameter so that the page can report it.
|
||||
// NB Paramater must have a value to be included in the URL query string.
|
||||
parameters.Add(ParamName_SessionExpired, "1");
|
||||
return homeAction;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Action execution events.
|
||||
//-------------------------------------------------------------------
|
||||
public override void OnActionExecuting(ActionExecutingContext filterContext)
|
||||
{
|
||||
if (DebugMode) SaLogger.Debug(GetType().Name + ".OnActionExecuting" + (ViewData.Model != null ? " Model: " + ViewData.Model : ""));
|
||||
|
||||
// Refreh model properties.
|
||||
SaxModel_Base baseModel = ViewData.Model as SaxModel_Base;
|
||||
RefreshModel(baseModel);
|
||||
|
||||
base.OnActionExecuting(filterContext);
|
||||
}
|
||||
public override void OnActionExecuted(ActionExecutedContext filterContext)
|
||||
{
|
||||
if (DebugMode) SaLogger.Debug(GetType().Name + ".OnActionExecuted" + (ViewData.Model != null ? " Model: " + ViewData.Model : ""));
|
||||
|
||||
// Refreh model properties.
|
||||
SaxModel_Base baseModel = ViewData.Model as SaxModel_Base;
|
||||
RefreshModel(baseModel);
|
||||
|
||||
// Store session data if present - as potentially modfied.
|
||||
if (SessionData != null)
|
||||
{
|
||||
// Most actions will store the session data in case it's been modified.
|
||||
// Some actions (eg. score password) don't need to.
|
||||
if (StoreSessionData)
|
||||
{
|
||||
String sessionDataJson = JsonConvert.SerializeObject(SessionData);
|
||||
HttpContext.Session.Set(SessionStorage.SessionKey_SessionConfig, SessionStorage.sessionDataEncoding.GetBytes(sessionDataJson));
|
||||
}
|
||||
}
|
||||
else HttpContext.Session.Remove(SessionStorage.SessionKey_SessionConfig);
|
||||
|
||||
// Response headers.
|
||||
if (baseModel != null && baseModel.ProhibitCache)
|
||||
{
|
||||
if (Response.Headers.ContainsKey(HeaderNames.CacheControl))
|
||||
Response.Headers.Remove(HeaderNames.CacheControl);
|
||||
Response.Headers.Add(HeaderNames.CacheControl, "no-cache, no-store, must-revalidate");
|
||||
Response.Headers.Add(HeaderNames.Pragma, "no-cache");
|
||||
Response.Headers.Add(HeaderNames.Expires, "0");
|
||||
}
|
||||
|
||||
|
||||
base.OnActionExecuted(filterContext);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Model.
|
||||
//-------------------------------------------------------------------
|
||||
protected void RefreshModel(SaxModel_Base model)
|
||||
{
|
||||
if (model == null) return;
|
||||
|
||||
// Session data.
|
||||
model.SessionData = SessionData;
|
||||
|
||||
// Redirect locations.
|
||||
model.RedirectLocation.Base = GetRedirectLocation(RedirectLocation.WebAppBase);
|
||||
model.RedirectLocation.Home = GetRedirectLocation(RedirectLocation.WebAppHome);
|
||||
model.RedirectLocation.Home_SessionExpired = GetRedirectLocation(RedirectLocation.WebAppHome_SessionExpired);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Parameters.
|
||||
//-------------------------------------------------------------------
|
||||
public String EncryptParameters(NameValueCollection parameters, String base64IV, String base64Key)
|
||||
{
|
||||
String fn = nameof(ControllerBase) + ".EncryptParameters";
|
||||
try
|
||||
{
|
||||
// Copy the list of parameters, adding a random name/value pair to randomise the encryption.
|
||||
NameValueCollection parametersToEncrypt = new NameValueCollection(parameters);
|
||||
// NB Range 10-189 intended to have equal chance of 2 or 3 digit number.
|
||||
parametersToEncrypt.Add("!" + SacsRandomHelper.GetInt32(10, 190).ToString(), SacsRandomHelper.GetInt32(10, 190).ToString());
|
||||
|
||||
// Sort the keys to ensure that the random '!' param is first in the query string.
|
||||
String[] paramKeys = parametersToEncrypt.AllKeys;
|
||||
Array.Sort(paramKeys);
|
||||
|
||||
// Serialise parameters as if in a URL query string - so that they can be deserialised in DecryptParameters() below.
|
||||
string queryString = String.Join("&", paramKeys.Select(a => a + "=" + HttpUtility.UrlEncode(parametersToEncrypt[a])));
|
||||
String encrypted = SacsEncryptionHelper.Encrypt(queryString, base64IV, base64Key);
|
||||
if (DebugMode)
|
||||
{
|
||||
SaLogger.Debug(fn + " Encrypted: " + encrypted);
|
||||
NameValueCollection decryptedParams = DecryptParameters(encrypted, base64IV, base64Key);
|
||||
}
|
||||
return encrypted;
|
||||
}
|
||||
catch (Exception exc) { SaLogger.Warn(fn + " " + ExceptionUtil.GetErrorStateMessage(exc)); }
|
||||
return "";
|
||||
}
|
||||
public NameValueCollection DecryptParameters(String encrypted, String base64IV, String base64Key)
|
||||
{
|
||||
String fn = nameof(ControllerBase) + ".DecryptParameters";
|
||||
if (DebugMode) SaLogger.Debug(fn + " \n\tEncrypted: " + encrypted + "\n\tIV: " + base64IV + "\n\tKey: " + base64Key);
|
||||
try
|
||||
{
|
||||
String queryString = SacsEncryptionHelper.Decrypt(encrypted, base64IV, base64Key);
|
||||
return HttpUtility.ParseQueryString(queryString);
|
||||
}
|
||||
catch (Exception exc) { SaLogger.Warn(fn + " " + ExceptionUtil.GetErrorStateMessage(exc)); }
|
||||
return new NameValueCollection();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Cookies.
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>Retrieve the value from the named request cookie.</summary>
|
||||
public String GetRequestCookie(String key)
|
||||
{
|
||||
if (Request.Cookies.ContainsKey(key))
|
||||
return Request.Cookies[key];
|
||||
return null;
|
||||
}
|
||||
/// <summary>Set the value against the named response cookie.</summary>
|
||||
public void SetResponseCookie(string key, string value, DateTime? expiresAt = null, bool isEssential = false)
|
||||
{
|
||||
CookieOptions options = new CookieOptions();
|
||||
options.HttpOnly = true;
|
||||
options.Secure = true;
|
||||
options.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Strict;
|
||||
if (expiresAt.HasValue)
|
||||
options.Expires = expiresAt;
|
||||
if (isEssential)
|
||||
options.IsEssential = true;
|
||||
Response.Cookies.Append(key, value, options);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Session.
|
||||
//-------------------------------------------------------------------
|
||||
public bool ValidUserSession { get { return SessionData?.ValidUserSession ?? false; } }
|
||||
public DbUpdateInfo DbUpdateInfo { get { return SessionData?.DbUpdateInfo ?? new DbUpdateInfo(null, "", SaxSystem.ComputerName); } }
|
||||
// Referrer.
|
||||
public String Referer { get { return Request.Headers["Referer"].ToString(); } }
|
||||
public bool ValidReferer { get { return !StringUtil.IsEmpty(Referer); } }
|
||||
/// <summary>Store session data, eg. to start a new user session on sign-in.</summary>
|
||||
protected bool SetSessionData(SaxSessionData sessionData)
|
||||
{
|
||||
if (sessionStorage.SessionData != null) return false;
|
||||
sessionStorage.SessionData = sessionData;
|
||||
return true;
|
||||
}
|
||||
protected void ClearSessionData()
|
||||
{
|
||||
if (HttpContext.Session.IsAvailable)
|
||||
HttpContext.Session.Clear();
|
||||
sessionStorage.SessionData = null;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Session storage.
|
||||
public class SessionStorage
|
||||
{
|
||||
public SaxSessionData SessionData { get; set; }
|
||||
public static String SessionKey_SessionConfig { get; } = "SaxSessionData";
|
||||
public static Encoding sessionDataEncoding { get; } = Encoding.UTF8;
|
||||
//JSON Serialisation.
|
||||
public class SaxSessionDataContractResolver : DefaultContractResolver
|
||||
{
|
||||
// To allow private properties to be deserialised.
|
||||
// NB Only applies to top level properties in the serialised object.
|
||||
protected override List<MemberInfo> GetSerializableMembers(Type objectType)
|
||||
{
|
||||
var flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
|
||||
MemberInfo[] fields = objectType.GetFields(flags);
|
||||
return fields
|
||||
.Concat(objectType.GetProperties(flags).Where(propInfo => propInfo.CanWrite))
|
||||
.ToList();
|
||||
}
|
||||
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
|
||||
{
|
||||
return base.CreateProperties(type, MemberSerialization.Fields);
|
||||
}
|
||||
};
|
||||
public JsonSerializerSettings SaxSessionDataJsonSerializerSettings = new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new SaxSessionDataContractResolver()
|
||||
};
|
||||
}
|
||||
private readonly SessionStorage sessionStorage = new SessionStorage();
|
||||
protected JsonSerializerSettings SaxSessionDataJsonSerializerSettings { get { return sessionStorage.SaxSessionDataJsonSerializerSettings; } }
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
public SaxSessionData SessionData
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
{
|
||||
// Retrieve session data if not already in controller.
|
||||
if (sessionStorage.SessionData == null)
|
||||
{
|
||||
byte[] sessionDataBytes = null;
|
||||
HttpContext.Session.TryGetValue(SessionStorage.SessionKey_SessionConfig, out sessionDataBytes);
|
||||
if (sessionDataBytes != null)
|
||||
{
|
||||
String sessionDataJson = SessionStorage.sessionDataEncoding.GetString(sessionDataBytes);
|
||||
sessionStorage.SessionData = DeserializeSessionData(sessionDataJson);
|
||||
}
|
||||
}
|
||||
return sessionStorage.SessionData;
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
SaLogger.Warn(GetType().Name + ".SessionData " + exc.GetType().Name + " - " + exc.Message + exc.GetStackEntries().Concat("", "\n\t"));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
protected abstract SaxSessionData DeserializeSessionData(String sessionDataJson);
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Ajax actions.
|
||||
//-------------------------------------------------------------------
|
||||
protected static readonly Encoding ajaxResponseEncoding = Encoding.UTF8;
|
||||
protected IActionResult GetAjaxResult_GoHome(AjaxResponse_Base responseData)
|
||||
{
|
||||
responseData.SetGoHome();
|
||||
return Json(responseData);
|
||||
}
|
||||
protected IActionResult GetAjaxResult_InvalidSession(AjaxResponse_Base responseData)
|
||||
{
|
||||
responseData.SetGoHome();
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
public class AjaxResponse_Base
|
||||
{
|
||||
// Response status.
|
||||
public bool Success { get; set; } = false;
|
||||
public bool Error { get; set; } = false;
|
||||
public bool SessionExpired { get; protected set; } = false;
|
||||
public bool GoHome { get; protected set; } = false;
|
||||
public AjaxResponse_Base SetSessionExpired(String title, String text)
|
||||
{
|
||||
SessionExpired = true;
|
||||
return SetMessage(title, text);
|
||||
}
|
||||
public AjaxResponse_Base SetGoHome()
|
||||
{
|
||||
GoHome = true;
|
||||
return this;
|
||||
}
|
||||
public AjaxResponse_Base SetGoHome(String title, String text)
|
||||
{
|
||||
GoHome = true;
|
||||
return SetMessage(title, text);
|
||||
}
|
||||
// Message - displays alert popup.
|
||||
public class MessageData
|
||||
{
|
||||
public String Title { get; set; }
|
||||
public String Text { get; set; }
|
||||
}
|
||||
public MessageData Message { get; private set; }
|
||||
public AjaxResponse_Base SetMessage(String title, String text)
|
||||
{
|
||||
if (!StringUtil.IsEmpty(text))
|
||||
Message = new MessageData() { Title = title, Text = text };
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
public class AjaxResponse_Data : AjaxResponse_Base
|
||||
{
|
||||
// Form data.
|
||||
public class FormControlValue
|
||||
{
|
||||
public FormControlValue(String controlName, String value, String error)
|
||||
{
|
||||
ControlName = controlName;
|
||||
Value = value;
|
||||
Error = error;
|
||||
}
|
||||
public String ControlName { get; }
|
||||
public String Value { get; set; }
|
||||
public String Error { get; set; }
|
||||
}
|
||||
private readonly Dictionary<String, FormControlValue> controlValues = new Dictionary<String, FormControlValue>();
|
||||
public List<FormControlValue> ControlValues { get { return controlValues.Values.ToList(); } }
|
||||
public void SetControlValue(String controlName, String value, String error = null)
|
||||
{
|
||||
if (controlValues.ContainsKey(controlName))
|
||||
{
|
||||
// Modify existing entry in the list.
|
||||
FormControlValue controlValue = controlValues[controlName];
|
||||
if (value != null) controlValue.Value = value;
|
||||
if (error != null) controlValue.Error = error;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add new control to list.
|
||||
controlValues.Add(controlName, new FormControlValue(controlName, value, error));
|
||||
}
|
||||
}
|
||||
/// <summary>Assign the error to the control by name and return this response data.</summary>
|
||||
public AjaxResponse_Data SetControlError(String controlName, String error)
|
||||
{
|
||||
return SetControlError(controlName, error, this);
|
||||
}
|
||||
/// <summary>
|
||||
/// Assign the error to the control by name and return the specified value.
|
||||
/// <para>- Allows alternative return values to be specified (eg. 'false') to avoid separate return statement in calling routine.</para>
|
||||
/// </summary>
|
||||
public T SetControlError<T>(String controlName, String error, T returnValue)
|
||||
{
|
||||
SetControlValue(controlName, null, error);
|
||||
return returnValue;
|
||||
}
|
||||
public bool HasControlErrors()
|
||||
{
|
||||
foreach (FormControlValue controlValue in controlValues.Values)
|
||||
{
|
||||
if (!StringUtil.IsEmpty(controlValue.Error)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// Notification - briefly displays after response received.
|
||||
public class NotificationData
|
||||
{
|
||||
public String Title { get; set; }
|
||||
public String Message { get; set; }
|
||||
}
|
||||
public NotificationData Notification { get; private set; }
|
||||
public bool HasNotification { get { return Notification != null; } }
|
||||
public AjaxResponse_Data SetNotification(String title, String message)
|
||||
{
|
||||
Notification = new NotificationData() { Title = title, Message = message };
|
||||
return this;
|
||||
}
|
||||
// Paged data.
|
||||
public class PagingData
|
||||
{
|
||||
public PagingData(SaxModel_Base model) :
|
||||
this(model.DataPageNo, model.DataPageLength, model.DataItemCount, model.DataItemTotal)
|
||||
{ }
|
||||
public PagingData(uint pageNo, ushort pageLength, ushort itemCount, ulong itemTotal)
|
||||
{
|
||||
PageNo = pageNo;
|
||||
PageLength = pageLength;
|
||||
ItemCount = itemCount;
|
||||
ItemTotal = itemTotal;
|
||||
}
|
||||
public bool Paged { get { return (PageNo > 0 && PageLength > 0 && ItemTotal > PageLength); } }
|
||||
public uint PageNo { get; } = 0;
|
||||
public ushort PageLength { get; } = 0;
|
||||
public uint PageCount { get { return (PageLength > 0 ? (uint)Math.Ceiling((decimal)ItemTotal / PageLength) : 0); } }
|
||||
public ushort ItemCount { get; } = 0;
|
||||
public ulong ItemTotal { get; } = 0;
|
||||
}
|
||||
public PagingData Paging { get; private set; }
|
||||
public void SetPaging(SaxModel_Base model)
|
||||
{
|
||||
Paging = new PagingData(model);
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Sarand.Net.Standard.Base.Core;
|
||||
using Sarand.Net.Standard.Base.Email;
|
||||
using Saxonite.Net.Standard.Base.App;
|
||||
using Saxonite.Net.Standard.Base.Bus;
|
||||
using Saxonite.Net.Core.Web.Base.App;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Saxonite.Net.Standard.Base.Consoles;
|
||||
using Sarand.Net.Standard.Base.Utils;
|
||||
|
||||
namespace Saxonite.Net.Core.Web.Base.Helpers
|
||||
{
|
||||
//=======================================================================
|
||||
public static class SaxEmailConfigHelper
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>Extract the email configurations from the application settings.</summary>
|
||||
public static Dictionary<String, SaxEmailConfig> ExtractEmailConfigs(IConfigurationSection emailConfigsSection)
|
||||
{
|
||||
Dictionary<String, SaxEmailConfig> emailConfigs = new Dictionary<String, SaxEmailConfig>();
|
||||
foreach (ConfigurationSection emailConfigSection in emailConfigsSection.GetChildren())
|
||||
{
|
||||
if (!StringUtil.IsEmpty(emailConfigSection.Key))
|
||||
emailConfigs.Add(emailConfigSection.Key, CreateEmailConfig(emailConfigSection.Key, emailConfigSection));
|
||||
}
|
||||
return emailConfigs;
|
||||
}
|
||||
private static SaxEmailConfig CreateEmailConfig(String context, ConfigurationSection emailConfigSection)
|
||||
{
|
||||
SaxEmailConfig emailConfig = new SaxEmailConfig(context);
|
||||
if (emailConfigSection != null)
|
||||
{
|
||||
IConfigurationSection message = emailConfigSection.GetSection("Message");
|
||||
emailConfig.Message.AddressFrom = message["AddressFrom"];
|
||||
foreach (var addressBcc in message.GetSection("AddressesBcc").GetChildren())
|
||||
emailConfig.Message.AddressesBcc.Add(StringUtil.TrimAll(addressBcc.Value));
|
||||
IConfigurationSection smtp = emailConfigSection.GetSection("SMTP");
|
||||
emailConfig.SMTP.Server = smtp["Server"];
|
||||
emailConfig.SMTP.ServerPort = StringUtil.ToInt(smtp["ServerPort"]);
|
||||
emailConfig.SMTP.UseSSL = StringUtil.ToBool(smtp["UseSSL"]);
|
||||
IConfigurationSection send = emailConfigSection.GetSection("Send");
|
||||
emailConfig.Send.Using = send["Using"];
|
||||
emailConfig.Send.UserName = send["UserName"];
|
||||
emailConfig.Send.Password = send["Password"];
|
||||
}
|
||||
return emailConfig;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Send email.
|
||||
public static bool SendEmail(SaEmailData emailData)
|
||||
{
|
||||
// ToDo return EmailConsole.SendEmail(emailData);
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>
|
||||
/// Replace dynamic tags in email message body.
|
||||
/// <para></para>
|
||||
/// </summary>
|
||||
public static void ReplaceDynamicEmailBodyTags(SaEmailData emailData, SaxSessionData sessionData, SaxUserAccount userAccount)
|
||||
{
|
||||
// Replace core tags, eg. main stylesheet.
|
||||
// ToDo. EmailConsole.ReplaceDynamicEmailTags(emailData);
|
||||
|
||||
// Replace tags known to the web application.
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
|
||||
//=======================================================================
|
||||
public static class EmailConfigList
|
||||
{
|
||||
public static SaxEmailConfig getConfigForSession(this List<SaxEmailConfig> emailConfigs, SaxSessionConfig sessionConfig)
|
||||
{
|
||||
if (sessionConfig != null && !StringUtil.IsEmpty(sessionConfig.Email.Context))
|
||||
{
|
||||
foreach (SaxEmailConfig emailConfig in emailConfigs)
|
||||
{
|
||||
if (emailConfig.Context.EqualsCI(sessionConfig.Email.Context))
|
||||
return emailConfig;
|
||||
}
|
||||
}
|
||||
return new SaxEmailConfig("");
|
||||
}
|
||||
}
|
||||
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Sarand.Net.Standard.Base.Core;
|
||||
using Saxonite.Net.Standard.Base.App;
|
||||
using Saxonite.Net.Core.Web.Base.App;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Sarand.Net.Standard.Base.Utils;
|
||||
|
||||
namespace Saxonite.Net.Core.Web.Base.Helpers
|
||||
{
|
||||
//=======================================================================
|
||||
public static class SaxSessionConfigHelper
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
public static Dictionary<String, SaxSessionConfig> ExtractSessionConfigs(IConfigurationSection sessionConfigsSection)
|
||||
{
|
||||
// NB The set of configurations will only allow one per context.
|
||||
// Only the last configuration found for each context will be stored (NB usually only one of each apart from 'Defaults').
|
||||
Dictionary<String, SaxSessionConfig> sessionConfigs = new Dictionary<String, SaxSessionConfig>();
|
||||
|
||||
// Start the list with a default 'Defaults' configuration - values set to application defaults (defined in Sax).
|
||||
// NB This can be overridden in each level of configuration.
|
||||
// Settings from the 'Defaults' configuration provide values where they are missing from other configurations.
|
||||
// NB Coverage may not be 100% - just where it's proven needed.
|
||||
SaxSessionConfig defaultsSessionConfig = new SaxSessionConfig(Sax.Config.Context_Defaults, null, null);
|
||||
sessionConfigs.Add(defaultsSessionConfig.Context, defaultsSessionConfig);
|
||||
|
||||
// Load session configurations.
|
||||
foreach (ConfigurationSection sessionConfigSection in sessionConfigsSection.GetChildren())
|
||||
{
|
||||
if (StringUtil.IsEmpty(sessionConfigSection.Key)) continue;
|
||||
|
||||
// Extract the configuration, supplying current 'Defaults' to supply missing values, and add to the list.
|
||||
SaxSessionConfig sessionConfig = new SaxSessionConfig(sessionConfigSection.Key, sessionConfigSection, defaultsSessionConfig);
|
||||
sessionConfigs.Remove(sessionConfigSection.Key);
|
||||
sessionConfigs.Add(sessionConfigSection.Key, sessionConfig);
|
||||
|
||||
// Replace the 'Defaults' configuration if this one matches the context.
|
||||
if (sessionConfig.Context.EqualsCI(Sax.Config.Context_Defaults))
|
||||
defaultsSessionConfig = sessionConfig;
|
||||
}
|
||||
return sessionConfigs;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
|
||||
//=======================================================================
|
||||
public static class SessionConfigList
|
||||
{
|
||||
public static SaxSessionConfig getConfigForApplication(this List<SaxSessionConfig> sessionConfigs)
|
||||
{
|
||||
SaxSessionConfig defaultsSessionConfig = new SaxSessionConfig("", null, null);
|
||||
SaxApplication_Base app = SaxGlobals.App;
|
||||
if (app != null)
|
||||
{
|
||||
foreach (SaxSessionConfig sessionConfig in sessionConfigs)
|
||||
{
|
||||
if (sessionConfig.Environments.ContainsCI(app.EnvironmentName))
|
||||
return sessionConfig;
|
||||
if (sessionConfig.Context.EqualsCI(Sax.Config.Context_Defaults))
|
||||
defaultsSessionConfig = sessionConfig;
|
||||
}
|
||||
}
|
||||
return defaultsSessionConfig;
|
||||
}
|
||||
}
|
||||
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Saxonite.Net.Core.Web.Base.Middleware
|
||||
{
|
||||
//=======================================================================
|
||||
/// <summary>
|
||||
/// Force blank form values to be populated to the model as empty strings rather than null values.
|
||||
/// </summary>
|
||||
public class SaxMetadataProvider : IMetadataDetailsProvider, IDisplayMetadataProvider
|
||||
{
|
||||
public void CreateDisplayMetadata(DisplayMetadataProviderContext context)
|
||||
{
|
||||
if (context.Key.MetadataKind == ModelMetadataKind.Property)
|
||||
{
|
||||
context.DisplayMetadata.ConvertEmptyStringToNull = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;
|
||||
using Sarand.Net.Standard.Base.App;
|
||||
using Sarand.Net.Standard.Base.Core;
|
||||
using Sarand.Net.Standard.Base.Utils;
|
||||
using Saxonite.Net.Core.Web.Base.App;
|
||||
using Saxonite.Net.Standard.Base.App;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Saxonite.Net.Core.Web.Base.Middleware
|
||||
{
|
||||
//=======================================================================
|
||||
/// <summary>
|
||||
/// Maps a browser request to a data object. Default class provides a base implementation of a model binder.
|
||||
/// <para>Overridden to trim whitespace from input values.</para>
|
||||
/// </summary>
|
||||
public class SaxModelBinder : ComplexTypeModelBinder
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Construction.
|
||||
public SaxModelBinder(IDictionary<ModelMetadata, IModelBinder> propertyBinders, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) :
|
||||
base(propertyBinders, loggerFactory)
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>
|
||||
/// Creates the model with one (or more) injected service parameters.
|
||||
/// </summary>
|
||||
protected override object CreateModel(ModelBindingContext bindingContext)
|
||||
{
|
||||
var services = bindingContext.HttpContext.RequestServices;
|
||||
var modelType = bindingContext.ModelType;
|
||||
var ctors = modelType.GetConstructors();
|
||||
foreach (var ctor in ctors)
|
||||
{
|
||||
var paramTypes = ctor.GetParameters().Select(p => p.ParameterType).ToList();
|
||||
var parameters = paramTypes.Select(p => services.GetService(p)).ToArray();
|
||||
if (parameters.All(p => p != null))
|
||||
{
|
||||
var model = ctor.Invoke(parameters);
|
||||
return model;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>
|
||||
/// Intercept String properties and trim whitespace.
|
||||
/// </summary>
|
||||
protected override void SetProperty(ModelBindingContext bindingContext, string modelName, ModelMetadata propertyMetadata, ModelBindingResult result)
|
||||
{
|
||||
string fn = GetType().Name + ".SetProperty";
|
||||
//if (SaxSystem.DebugMode) Logger.Debug(fn + " Form property " + modelName + ": '" + result.Model.ToString() + ".");
|
||||
object originalValue = result.Model;
|
||||
// Exclude properties that retain leading/trailing whitespace.
|
||||
if (result.Model is string)
|
||||
{
|
||||
if (!modelName.ContainsCI("Password"))
|
||||
{
|
||||
string value = result.Model as string;
|
||||
|
||||
// Trim leading and trailing whitespace.
|
||||
value = StringUtil.TrimAll(value);
|
||||
|
||||
// Check for multi-line.
|
||||
if (value.Contains('\n'))
|
||||
{
|
||||
// Simplify line delimiters.
|
||||
value = Regex.Replace(value, "\\r\\n", "\n");
|
||||
// Remove white space in front of newlines.
|
||||
value = Regex.Replace(value, "\\s+\\n", "\n");
|
||||
// Remove multiple newlines.
|
||||
value = Regex.Replace(value, "\\n+", "\n");
|
||||
}
|
||||
|
||||
// Email addresses must be lowercase to ensure match to encrypted data.
|
||||
if (modelName.EqualsCI("EmailAddress") || modelName.EqualsCI("AccountEmailAddress"))
|
||||
value = value.ToLower();
|
||||
|
||||
// Uppercase selected values.
|
||||
if (modelName.EqualsCI("Postcode") || modelName.EqualsCI("AccountPostcode") || modelName.EqualsCI("RegisteredAddressPostcode"))
|
||||
value = value.ToUpper();
|
||||
|
||||
// Assign resulting value.
|
||||
result = ModelBindingResult.Success(value);
|
||||
}
|
||||
}
|
||||
if (result.Model != null && SaxSystem.DebugMode)
|
||||
SaLogger.Debug(fn + " Form property " + modelName + ": '" + result.Model.ToString() + "'." + (originalValue != null && !originalValue.Equals(result.Model) ? " Original value: '" + originalValue.ToString() + "'" : ""));
|
||||
base.SetProperty(bindingContext, modelName, propertyMetadata, result);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Saxonite.Net.Core.Web.Base.Middleware
|
||||
{
|
||||
//=======================================================================
|
||||
/// <summary>
|
||||
/// Maps a browser request to a data object. Default class provides a base implementation of a model binder.
|
||||
/// <para>Overridden to trim whitespace from input values.</para>
|
||||
/// </summary>
|
||||
public class SaxModelBinderProvider : IModelBinderProvider
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Data members.
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Construction.
|
||||
public SaxModelBinderProvider()
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
public IModelBinder GetBinder(ModelBinderProviderContext context)
|
||||
{
|
||||
if (context == null) throw new ArgumentNullException(nameof(context));
|
||||
|
||||
if (context.Metadata.IsComplexType && !context.Metadata.IsCollectionType)
|
||||
{
|
||||
var propertyBinders = context.Metadata.Properties.ToDictionary(property => property, context.CreateBinder);
|
||||
ILoggerFactory loggerFactory = (ILoggerFactory)context.Services.GetService(typeof(ILoggerFactory));
|
||||
return new SaxModelBinder(propertyBinders, loggerFactory);
|
||||
}
|
||||
|
||||
// Default - return null so that default binding provider is used.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
using Microsoft.AspNetCore.Antiforgery;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Sarand.Net.Standard.Base.App;
|
||||
using Sarand.Net.Standard.Base.Core;
|
||||
using Sarand.Net.Standard.Base.Utils;
|
||||
using Saxonite.Net.Standard.Base.App;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Saxonite.Net.Core.Web.Base.Middleware
|
||||
{
|
||||
//=======================================================================
|
||||
/// <summary>
|
||||
/// Extend IApplicationBuilder class to use the anit-forgery token validation middleware.
|
||||
/// </summary>
|
||||
public static class ApplicationBuilderExtensions
|
||||
{
|
||||
public static IApplicationBuilder UseAntiforgeryTokens(this IApplicationBuilder app)
|
||||
{
|
||||
return app.UseMiddleware<ValidateAntiForgeryTokenMiddleware>();
|
||||
}
|
||||
}
|
||||
|
||||
//=======================================================================
|
||||
/// <summary>
|
||||
/// Ensure relevant actions validate the anti-forgery token, eg. post requests.
|
||||
/// </summary>
|
||||
public class ValidateAntiForgeryTokenMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly IAntiforgery _antiforgery;
|
||||
|
||||
public ValidateAntiForgeryTokenMiddleware(RequestDelegate next, IAntiforgery antiforgery)
|
||||
{
|
||||
_next = next;
|
||||
_antiforgery = antiforgery;
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext context)
|
||||
{
|
||||
String fn = GetType().Name + ".Invoke";
|
||||
|
||||
// Log action.
|
||||
//String rawTarget = context.Features.Get<IHttpRequestFeature>().RawTarget;
|
||||
if (SaxSystem.DebugMode) SaLogger.Debug(fn + " Action: '" + Regex.Replace(context.Request.Path.ToString(), "^/", "") + "'. Method: " + context.Request.Method);
|
||||
|
||||
// Intercept POST requests and apply anti-forgery test.
|
||||
if (HttpMethods.IsPost(context.Request.Method))
|
||||
{
|
||||
await _antiforgery.ValidateRequestAsync(context);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await _next(context);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
SaLogger.Warn(fn + " " + ExceptionUtil.GetLogMessage(exc));
|
||||
throw exc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
using Microsoft.AspNetCore.Html;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
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.Base.App;
|
||||
using Saxonite.Net.Core.Web.Base.App;
|
||||
using Saxonite.Net.Standard.Base.App;
|
||||
using Saxonite.Net.Standard.Base.Consoles;
|
||||
using Saxonite.Net.Standard.Base.Mgr;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Saxonite.Net.Core.Web.Base.Models
|
||||
{
|
||||
//=======================================================================
|
||||
public class SaxModel_Base
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Data members.
|
||||
private SaxInternals internals;
|
||||
private readonly List<String> infoMessages = new List<string>();
|
||||
private readonly List<String> warningMessages = new List<string>();
|
||||
private readonly List<String> errorMessages = new List<string>();
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Protected construction.
|
||||
protected SaxModel_Base(SaxLocaliser localiser)
|
||||
{
|
||||
Localiser = localiser;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Application properties.
|
||||
private SaxApplication_Web App { get { return SaxApplication_Web.WebApp; } }
|
||||
protected SaxSessionConfig SessionConfig { get { return App.SessionConfig; } }
|
||||
protected SaxSystem_Web System { get { return App.System_Web; } }
|
||||
public String SessionCookieName { get { return App.SessionCookieName; } }
|
||||
public SaxSessionData SessionData { get; set; }
|
||||
public SaxSessionContext SessionContext { get { return SessionData?.SessionContext ?? App.SessionContext; } }
|
||||
public DbUpdateInfo DbUpdateInfo { get { return SessionData?.DbUpdateInfo ?? new DbUpdateInfo(null, "", SaxSystem.ComputerName); } }
|
||||
public bool DebugMode { get { return SaxSystem.DebugMode; } }
|
||||
public SaxLocaliser Localiser { get; }
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Business components.
|
||||
protected SaxInternals Internals { get { if (internals == null) internals = new SaxInternals(SessionContext); return internals; } }
|
||||
// Consoles.
|
||||
protected SaxDataCacheConsole DataCacheConsole { get { return Internals.DataCacheConsole; } }
|
||||
// Managers.
|
||||
protected SaxReferenceMgr ReferenceMgr { get { return Internals.ReferenceMgr; } }
|
||||
protected SaxAccountMgr AccountMgr { get { return Internals.AccountMgr; } }
|
||||
protected SaxActivityMgr ActivityMgr { get { return Internals.ActivityMgr; } }
|
||||
protected SaxItemDataMgr ItemDataMgr { get { return Internals.ItemDataMgr; } }
|
||||
// System codes.
|
||||
public List<TypeCodeDesc> RegionTypes { get { return Internals.RegionTypes; } }
|
||||
public List<TypeCodeDesc> UserRoles { get { return Internals.UserRoles; } }
|
||||
public List<TypeCodeDesc> OrganisationTypes { get { return Internals.OrganisationTypes; } }
|
||||
public List<TypeCodeDesc> OrganisationCategories { get { return Internals.OrganisationCategories; } }
|
||||
public List<TypeCodeDesc> OrganisationRoles { get { return Internals.OrganisationRoles; } }
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Page configuration.
|
||||
public bool OK { get { return !HasMessages(); } }
|
||||
public virtual bool ProhibitCache { get; set; } = true;
|
||||
public String PageCaption { get; set; }
|
||||
public String InstanceVersion { get { return !StringUtil.IsEmpty(App.SessionConfig.Instance) ? App.SessionConfig.Instance + " v" + Sax.Version.GetFullVersion() : "v" + Sax.Version.GetAppVersion(); } }
|
||||
public bool IncludeGoogleAnalytics { get; set; } = false;
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Security.
|
||||
public bool ValidUserSession { get { return SessionData?.ValidUserSession ?? false; } }
|
||||
public bool AwaitingLogin { get { return !ValidUserSession; } }
|
||||
public bool KeepLoggedIn { get { return !AwaitingLogin && SessionData.User.KeepLoggedIn; } }
|
||||
/// <summary>Maximum length allowed for password entry: sign-up, change password etc.</summary>
|
||||
public int PasswordMaxLength { get; } = 32;
|
||||
|
||||
// Account actions.
|
||||
// NB The form proerty will never be used - account actions processed within page.
|
||||
public String AccountAction { get; set; }
|
||||
public IEnumerable<SelectListItem> GetAccountActions()
|
||||
{
|
||||
List<CodeDesc> items = new List<CodeDesc>();
|
||||
if (SessionData?.ValidUserSession ?? false)
|
||||
{
|
||||
items.Add("Profile", Localiser["label.view.profile"]);
|
||||
items.Add("ChangePassword", Localiser["label.change.password"]);
|
||||
items.Add("SignOut", Localiser["label.sign.out"]);
|
||||
}
|
||||
return new SelectList(items, "Code", "Desc");
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>
|
||||
/// Form control value max length.
|
||||
/// <para>In many cases the database columns allow plenty of extra space for the value, in case they need to be encrypted.</para>
|
||||
/// <para>Hence, several cases where the value entered in the control isn't allowed the full database length.</para>
|
||||
/// </summary>
|
||||
public int GetControlMaxLength(SaxDbDfn.ColumnIdx column)
|
||||
{
|
||||
int maxLength = (int)SaxDbDfn.GetColTextLen(column);
|
||||
switch (column)
|
||||
{
|
||||
case SaxDbDfn.ColumnIdx.UserAccount_EmailAddress:
|
||||
return Math.Min(maxLength, 60);
|
||||
case SaxDbDfn.ColumnIdx.UserAccount_FirstName:
|
||||
case SaxDbDfn.ColumnIdx.UserAccount_LastName:
|
||||
return Math.Min(maxLength, 40);
|
||||
case SaxDbDfn.ColumnIdx.Organisation_Name:
|
||||
return Math.Min(maxLength, 80);
|
||||
case SaxDbDfn.ColumnIdx.UserAccount_PhoneMain:
|
||||
case SaxDbDfn.ColumnIdx.UserAccount_PhoneMobile:
|
||||
case SaxDbDfn.ColumnIdx.Organisation_PhoneMain:
|
||||
case SaxDbDfn.ColumnIdx.Organisation_PhoneAlt:
|
||||
return Math.Min(maxLength, 20);
|
||||
//case SaxDbDfn.ColumnIdx.UserAccount_Postcode:
|
||||
//case SaxDbDfn.ColumnIdx.ItemAddress_Postcode:
|
||||
// return Math.Min(maxLength, 16);
|
||||
}
|
||||
return maxLength;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Copyright notice.
|
||||
public HtmlString CopyrightNotice
|
||||
{
|
||||
get
|
||||
{
|
||||
int initialYear = 2020;
|
||||
int currentYear = DateTime.Today.Year;
|
||||
return Localiser.GetHtmlText("copyright.symbol.year.html", (initialYear != currentYear ? initialYear + "-" : "") + currentYear.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Link URLs.
|
||||
public class LinksData
|
||||
{
|
||||
public String Facebook { get; } = "https://www.facebook.com/saxonite/";
|
||||
public String LinkedIn { get; } = "https://www.linkedin.com/company/saxonite/about/";
|
||||
}
|
||||
public LinksData Links { get; } = new LinksData();
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Form data.
|
||||
/// <summary>List of Yes/No options for dropdown.</summary>
|
||||
public IEnumerable<SelectListItem> YesNoOptionsForSelect
|
||||
{
|
||||
get { return new SelectList(SaxSystem.GetCodesOfType(Sax.CodeType.YesNo), "Code", "Desc"); }
|
||||
}
|
||||
|
||||
/// <summary>List of titles for dropdown.</summary>
|
||||
public IEnumerable<SelectListItem> TitlesForSelect
|
||||
{
|
||||
get { return new SelectList(SaxSystem.GetCodesOfType(Sax.CodeType.Title), "Code", "Desc"); }
|
||||
}
|
||||
|
||||
/// <summary>List of Region Types for dropdown.</summary>
|
||||
public IEnumerable<SelectListItem> RegionTypesForSelect
|
||||
{
|
||||
get { return new SelectList(RegionTypes, "Code", "Desc"); }
|
||||
}
|
||||
|
||||
/// <summary>List of Organisation Types for dropdown.</summary>
|
||||
public IEnumerable<SelectListItem> OrganisationTypesForSelect
|
||||
{
|
||||
get { return new SelectList(OrganisationTypes, "Code", "Desc"); }
|
||||
}
|
||||
|
||||
/// <summary>List of Organisation Categories for dropdown.</summary>
|
||||
public IEnumerable<SelectListItem> OrganisationCategoriesForSelect
|
||||
{
|
||||
get { return new SelectList(OrganisationCategories, "Code", "Desc"); }
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Version parameter for includes.
|
||||
public String HrefVersion
|
||||
{
|
||||
get
|
||||
{
|
||||
DateTime versionDate = SaxSystem.AssemblyBuildAt;
|
||||
if (!DateUtil.IsValid(versionDate) || SaxSystem.DebugMode)
|
||||
versionDate = DateTime.Now;
|
||||
return DateUtil.Format(versionDate, "yyyyMMdd" /*+ (SacsSystem.Debug ? "HHmm" : "")*/);
|
||||
}
|
||||
}
|
||||
// Additional includes.
|
||||
public List<String> AdditionalFiles_CSS { get; set; } = new List<string>();
|
||||
public List<String> AdditionalFiles_JS { get; set; } = new List<string>();
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Redirection locations.
|
||||
public class RedirectLocationData
|
||||
{
|
||||
// Properties populated in Controller_Base.OnActionExecuted.
|
||||
public String Base { get; internal set; }
|
||||
public String Home { get; internal set; }
|
||||
public String Home_SessionExpired { get; internal set; }
|
||||
}
|
||||
public RedirectLocationData RedirectLocation { get; } = new RedirectLocationData();
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Paging properties.
|
||||
private class DataPaging
|
||||
{
|
||||
private DbQuery.Paging paging { get; } = new DbQuery.Paging();
|
||||
public void PopulatePagingTo(DbQuery.Paging resultsPaging)
|
||||
{
|
||||
resultsPaging.PageLength = paging.PageLength;
|
||||
resultsPaging.PageNo = paging.PageNo;
|
||||
}
|
||||
public bool IsPaged { get { return paging.Paged; } }
|
||||
public uint PageNo { get { return paging.PageNo; } set { paging.PageNo = value; } }
|
||||
public ushort PageLength { get { return paging.PageLength; } set { paging.PageLength = value; } }
|
||||
public uint PageCount { get { return (PageLength > 0 ? (uint)Math.Ceiling((decimal)ItemTotal / PageLength) : 0); } }
|
||||
public ushort ItemCount { get; set; }
|
||||
public ulong ItemTotal { get; set; }
|
||||
}
|
||||
private DataPaging Paging { get; } = new DataPaging();
|
||||
public bool DataPaged { get { return Paging.IsPaged; } }
|
||||
public uint DataPageNo { get { return Paging.PageNo; } set { Paging.PageNo = value; } }
|
||||
public ushort DataPageLength { get { return Paging.PageLength; } set { Paging.PageLength = value; } }
|
||||
public uint DataPageCount { get { return (DataPageLength > 0 ? (uint)Math.Ceiling((decimal)DataItemTotal / DataPageLength) : 0); } }
|
||||
public ushort DataItemCount { get; set; }
|
||||
public ulong DataItemTotal { get; set; }
|
||||
public void PopulatePagingTo(DbQuery.Paging resultsPaging)
|
||||
{
|
||||
Paging.PopulatePagingTo(resultsPaging);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Form properties.
|
||||
/// <summary>
|
||||
/// User session index to identify the site resources, eg. database.
|
||||
/// <para>Equivalent to the Site index on the login form.</para>
|
||||
/// <para>Populates value on logout form to identify default site for next login.</para>
|
||||
/// </summary>
|
||||
public ushort SessionIdx { get { return SessionContext.Index; } }
|
||||
public String FormAction { get; set; }
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Messages.
|
||||
//-------------------------------------------------------------------
|
||||
public bool HasMessages()
|
||||
{
|
||||
return infoMessages.Count > 0 || warningMessages.Count > 0 || errorMessages.Count > 0;
|
||||
}
|
||||
public List<String> InfoMessages { get { return new List<string>(infoMessages); } }
|
||||
public List<String> WarningMessages { get { return new List<string>(warningMessages); } }
|
||||
public List<String> ErrorMessages { get { return new List<string>(errorMessages); } }
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Form messages.
|
||||
public void AddInfoMessage(String message)
|
||||
{
|
||||
if (StringUtil.IsEmpty(StringUtil.TrimAll(message))) return;
|
||||
infoMessages.Add(message);
|
||||
}
|
||||
public void AddWarningMessage(String message)
|
||||
{
|
||||
if (StringUtil.IsEmpty(StringUtil.TrimAll(message))) return;
|
||||
warningMessages.Add(message);
|
||||
}
|
||||
public void AddErrorMessage(String message)
|
||||
{
|
||||
if (StringUtil.IsEmpty(StringUtil.TrimAll(message))) return;
|
||||
errorMessages.Add(message);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<RootNamespace>Saxonite.Net.Core.Web</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<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="..\SaxoniteStandardBase\SaxoniteStandardBase.csproj" />
|
||||
<ProjectReference Include="..\SaxoniteStandardDbas\SaxoniteStandardDbas.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Base\Core\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="3.1.13" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"version": 1,
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"dotnet-ef": {
|
||||
"version": "5.0.5",
|
||||
"commands": [
|
||||
"dotnet-ef"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>&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"]: <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"]: <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"] </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" })
|
||||
|
||||
<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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
@@ -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 |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user