Add project files.
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Saxonite.Net.Core.Web.Online.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_Online>();
|
||||
});
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Sarand.Net.Standard.Dbas.Base;
|
||||
using Sarand.Net.Standard.Dbas.Data;
|
||||
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 Saxonite.Net.Standard.Base.App;
|
||||
using Saxonite.Net.Standard.Base.Consoles;
|
||||
using Saxonite.Net.Core.Web.Online.Consoles;
|
||||
using Saxonite.Net.Core.Web.Base.App;
|
||||
using Saxonite.Net.Core.Web.Base.Helpers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Saxonite.Net.Standard.Dbas.App;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Sarand.Net.Standard.Base.App;
|
||||
using Sarand.Net.Standard.Base.Utils;
|
||||
|
||||
namespace Saxonite.Net.Core.Web.Online.App
|
||||
{
|
||||
//=======================================================================
|
||||
public class SaxApplication_Online : SaxApplication_Web
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Data members.
|
||||
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_Online(IConfiguration configuration)
|
||||
{
|
||||
InitialiseTemporaryLoggingFolder();
|
||||
try
|
||||
{
|
||||
AppInstance = this;
|
||||
SaxGlobals.App = this;
|
||||
SaxGlobals.System = (System_Web = new SaxSystem_Web());
|
||||
Configuration = configuration;
|
||||
ConsoleHelper_Web = new SaxConsoleHelper_WebOnline(this);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
SaLogger.Error(GetType().Name + ".CTR " + ExceptionUtil.GetErrorStateMessage(exc));
|
||||
throw exc;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Properties.
|
||||
public static SaxApplication_Online 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.Online.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-Onl";
|
||||
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.Online));
|
||||
sessionContextData.CoreDatabase = coreDatabase;
|
||||
sessionContexts.Add(sessionContextData.SessionContext.Index, sessionContextData);
|
||||
SessionContext = sessionContextData.SessionContext;
|
||||
|
||||
// Data Cache console - to initialise refresh.
|
||||
SaxDataCacheConsole dataCacheConsole = SaxGlobals.ConsoleHelper.GetDataCacheConsole(SessionContext);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
SaLogger.Error(fn + " " + ExceptionUtil.GetLogMessage(exc));
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using Microsoft.AspNetCore.Html;
|
||||
using Microsoft.AspNetCore.Mvc.Localization;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Saxonite.Net.Core.Web.Base.App;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Resources;
|
||||
using System.Web;
|
||||
|
||||
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 SaxOnlineProperties { }
|
||||
// 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.Online.App
|
||||
{
|
||||
//=======================================================================
|
||||
public class SaxLocaliser_Online : SaxLocaliser
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Data members.
|
||||
private readonly IStringLocalizer<SaxOnlineProperties> webAppProperties;
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>
|
||||
/// Localisers are supplied using construction injection.
|
||||
/// </summary>
|
||||
public SaxLocaliser_Online(IStringLocalizer<SaxOnlineProperties> webAppProperties)
|
||||
{
|
||||
this.webAppProperties = webAppProperties;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Properties.
|
||||
public override IStringLocalizer MainProperties { get { return webAppProperties; } }
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -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.Online.App
|
||||
{
|
||||
//=======================================================================
|
||||
public class SaxSessionData_Online : SaxSessionData_Web
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Construction.
|
||||
[JsonConstructor]
|
||||
private SaxSessionData_Online(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_Online(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,81 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.HttpsPolicy;
|
||||
using Microsoft.AspNetCore.Localization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Razor;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Saxonite.Net.Standard.Base.App;
|
||||
using Saxonite.Net.Core.Web.Base.Middleware;
|
||||
using Microsoft.AspNetCore.Internal;
|
||||
using Saxonite.Net.Core.Web.Base.App;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Sarand.Net.Standard.Base.Core;
|
||||
|
||||
namespace Saxonite.Net.Core.Web.Online.App
|
||||
{
|
||||
//=======================================================================
|
||||
public class SaxStartup_Online : SaxStartup
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Data members.
|
||||
private SaxApplication_Online application = null;
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Construction.
|
||||
public SaxStartup_Online(IConfiguration configuration) : base(configuration)
|
||||
{
|
||||
// Initialise application.
|
||||
application = new SaxApplication_Online(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.Online.Session.Cookie.Name;
|
||||
configureServicesConfig.sessionCookieHttpOnly = Sax.Config.Online.Session.Cookie.HttpOnly;
|
||||
configureServicesConfig.sessionIdleTimeout = Sax.Config.Online.Session.Cookie.IdleTimeout;
|
||||
ConfigureServices(configureServicesConfig);
|
||||
|
||||
// Application localisation.
|
||||
services.AddSingleton<SaxLocaliser, SaxLocaliser_Online>();
|
||||
|
||||
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,51 @@
|
||||
using Saxonite.Net.Core.Web.Base.Consoles;
|
||||
using Saxonite.Net.Core.Web.Online.App;
|
||||
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.Online.Consoles
|
||||
{
|
||||
//=======================================================================
|
||||
public class SaxConsoleHelper_WebOnline : SaxConsoleHelper_Web
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Data members.
|
||||
private readonly SaxApplication_Online app;
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// construction.
|
||||
public SaxConsoleHelper_WebOnline(SaxApplication_Online app)
|
||||
{
|
||||
this.app = app;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Data cache console.
|
||||
private readonly Dictionary<ushort, SaxDataCacheConsole_Online> dataCacheConsoles = new Dictionary<ushort, SaxDataCacheConsole_Online>();
|
||||
public override SaxDataCacheConsole GetDataCacheConsole(SaxSessionContext sessionContext)
|
||||
{
|
||||
if (sessionContext == null) throw new NotSupportedException(GetType().Name + ".GetDataCacheConsole Session Context must be specified");
|
||||
SaxDataCacheConsole_Online dataCacheConsole = dataCacheConsoles.GetValueOrDefault(sessionContext.Index, null);
|
||||
if (dataCacheConsole == null)
|
||||
{
|
||||
dataCacheConsole = dataCacheConsoles[sessionContext.Index] = new SaxDataCacheConsole_Online(sessionContext);
|
||||
}
|
||||
return dataCacheConsole;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Email console.
|
||||
public override SaxEmailConsole CreateEmailConsole(SaxSessionContext sessionContext)
|
||||
{
|
||||
SaxEmailConsole emailConsole = new SaxEmailConsole_Web(app.EmailConfig);
|
||||
return emailConsole;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
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.Text;
|
||||
|
||||
namespace Saxonite.Net.Core.Web.Online.Consoles
|
||||
{
|
||||
//=======================================================================
|
||||
public class SaxDataCacheConsole_Online : SaxDataCacheConsole_Web
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
private System.Timers.Timer refreshTimer;
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
public SaxDataCacheConsole_Online(SaxSessionContext sessionContext) :
|
||||
base(sessionContext)
|
||||
{
|
||||
RegionsSupported = true;
|
||||
SportsSupported = true;
|
||||
|
||||
// Initialise refresh timer.
|
||||
refreshTimer = new System.Timers.Timer(Math.Max(2, Sax.Config.Online.DataCache.RefreshIntervalSecs.Initial) * 1000);
|
||||
refreshTimer.Elapsed += RefreshTimer_Elapsed;
|
||||
refreshTimer.Start();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Refresh data cache.
|
||||
private void RefreshTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
|
||||
{
|
||||
refreshTimer.Stop();
|
||||
|
||||
if (RegionsSupported) RefreshRegions();
|
||||
if (SportsSupported) RefreshSports();
|
||||
|
||||
refreshTimer.Interval = Math.Max(10, Sax.Config.Online.DataCache.RefreshIntervalSecs.Periodic) * 1000;
|
||||
refreshTimer.Start();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Saxonite.Net.Core.Web.Online.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;
|
||||
using Saxonite.Net.Standard.Base.Bus;
|
||||
using Sarand.Net.Standard.Base.App;
|
||||
using Sarand.Net.Standard.Base.Bus;
|
||||
using Sarand.Net.Standard.Base.Utils;
|
||||
|
||||
namespace Saxonite.Net.Core.Web.Online.Controllers
|
||||
{
|
||||
//=======================================================================
|
||||
// Online Account Controller - Ajax actions.
|
||||
//=======================================================================
|
||||
partial class AccountController
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Account - Get Contact Details.
|
||||
//-------------------------------------------------------------------
|
||||
public class ContactResponseData
|
||||
{
|
||||
public ContactResponseData(SaxUserAccount userAccount)
|
||||
{
|
||||
EmailAddress = userAccount.EmailAddress;
|
||||
DisplayName = userAccount.GetDisplayName();
|
||||
Title = userAccount.Title;
|
||||
FirstName = userAccount.FirstName;
|
||||
LastName = userAccount.LastName;
|
||||
}
|
||||
public String EmailAddress { get; }
|
||||
public String DisplayName { get; }
|
||||
public String Title { get; }
|
||||
public String FirstName { get; }
|
||||
public String LastName { get; }
|
||||
}
|
||||
public class AjaxResponse_AccountContactData : AjaxResponse_Data
|
||||
{
|
||||
public ContactResponseData Contact { get; set; }
|
||||
}
|
||||
// Ajax request.
|
||||
[HttpPost]
|
||||
[Route("Account/Ajax/GetProfileContact")]
|
||||
public IActionResult GetProfileContact(Model_Account model)
|
||||
{
|
||||
String fn = GetType().Name + ".GetProfileContact";
|
||||
AjaxResponse_AccountContactData responseData = new AjaxResponse_AccountContactData();
|
||||
String messageTitle = Localiser["account.caption.contact"];
|
||||
|
||||
// Validate session and referrer.
|
||||
if (!ValidReferer)
|
||||
return Forbid();
|
||||
if (!ValidUserSession)
|
||||
return GetAjaxResult_InvalidSession(responseData);
|
||||
|
||||
// Validate data.
|
||||
model.ValidateContactDetails(responseData);
|
||||
if (responseData.HasControlErrors())
|
||||
return Json(responseData);
|
||||
|
||||
// Retrieve user details.
|
||||
SaxUserAccount userAccount = AccountMgr.GetUserAccountById(SessionData.User.UserAccountId);
|
||||
if (userAccount == null)
|
||||
{
|
||||
SaLogger.Warn(fn + " Failed to retrieve user account for Id: " + SysId.ToString(SessionData.User.UserAccountId));
|
||||
return Json(responseData.SetGoHome(messageTitle, @Localiser.GetJavascriptText("msg.something.gone.wrong")));
|
||||
}
|
||||
|
||||
// Populate data to response.
|
||||
responseData.Contact = new ContactResponseData(userAccount);
|
||||
responseData.Success = true;
|
||||
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Account - Store Contact Details.
|
||||
//-------------------------------------------------------------------
|
||||
public class AjaxResponse_StoreAccountContactDetails : AjaxResponse_Data
|
||||
{
|
||||
public ContactResponseData Contact { get; set; }
|
||||
}
|
||||
// Ajax request.
|
||||
[HttpPost]
|
||||
[Route("Account/Ajax/StoreProfileContact")]
|
||||
public IActionResult StoreProfileContact(Model_Account model)
|
||||
{
|
||||
String fn = GetType().Name + ".StoreProfileContact";
|
||||
AjaxResponse_StoreAccountContactDetails responseData = new AjaxResponse_StoreAccountContactDetails();
|
||||
String messageTitle = Localiser["account.caption.contact"];
|
||||
|
||||
// Validate session and referrer.
|
||||
if (!ValidReferer)
|
||||
return Forbid();
|
||||
if (!ValidUserSession)
|
||||
return GetAjaxResult_InvalidSession(responseData);
|
||||
|
||||
// Validate data.
|
||||
model.ValidateContactDetails(responseData);
|
||||
if (responseData.HasControlErrors())
|
||||
return Json(responseData);
|
||||
|
||||
// Retrieve user details.
|
||||
SaxUserAccount userAccount = AccountMgr.GetUserAccountById(SessionData.User.UserAccountId);
|
||||
if (userAccount == null)
|
||||
{
|
||||
SaLogger.Warn(fn + " Failed to retrieve user account for Id: " + SysId.ToString(SessionData.User.UserAccountId));
|
||||
return Json(responseData.SetGoHome(messageTitle, @Localiser.GetJavascriptText("msg.something.gone.wrong")));
|
||||
}
|
||||
if (!userAccount.EmailAddress.EqualsCI(SessionData.User.EmailAddress))
|
||||
{
|
||||
SaLogger.Warn(fn + " Unable to update user account '" + userAccount.EmailAddress + ". Email doesn't match session: " + SessionData.User.EmailAddress);
|
||||
return Json(responseData.SetGoHome(messageTitle, @Localiser.GetJavascriptText("msg.something.gone.wrong")));
|
||||
}
|
||||
|
||||
// Populate user properties.
|
||||
userAccount.Changed = false;
|
||||
if (model.AccountTitle != null)
|
||||
userAccount.Title = model.AccountTitle;
|
||||
if (model.AccountFirstName != null)
|
||||
userAccount.FirstName = model.AccountFirstName;
|
||||
if (model.AccountLastName != null)
|
||||
userAccount.LastName = model.AccountLastName;
|
||||
|
||||
// Update User Account.
|
||||
if (userAccount.Changed) SaLogger.Info(fn + " Updating user account '" + userAccount.EmailAddress + "'. Id: " + SysId.ToString(userAccount.Id), true);
|
||||
if (AccountMgr.UpdateUserAccount(userAccount, DbUpdateInfo))
|
||||
{
|
||||
// ToDo Refresh Session with updated organisation details.
|
||||
}
|
||||
else
|
||||
{
|
||||
SaLogger.Warn(fn + " Failed to update user account '" + userAccount.EmailAddress + "': " + AccountMgr.ErrorStateMessage);
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"]));
|
||||
}
|
||||
|
||||
// Construct response.
|
||||
responseData.Contact = new ContactResponseData(userAccount);
|
||||
responseData.Success = true;
|
||||
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Saxonite.Net.Core.Web.Online.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.Online.Controllers
|
||||
{
|
||||
//=======================================================================
|
||||
// Online Account Controller.
|
||||
//=======================================================================
|
||||
public partial class AccountController : SaxController_Online
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Construction.
|
||||
public AccountController(SaxLocaliser localiser, ILogger<HomeController> logger) :
|
||||
base(localiser, logger)
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
[HttpGet]
|
||||
public IActionResult Index()
|
||||
{
|
||||
// Validate session.
|
||||
if (!ValidUserSession)
|
||||
return RedirectToAction("", "");
|
||||
|
||||
Model_Account model = new Model_Account(Localiser);
|
||||
return View(model);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
[HttpGet]
|
||||
public IActionResult Profile()
|
||||
{
|
||||
// Validate session.
|
||||
if (!ValidUserSession)
|
||||
return RedirectToAction("", "");
|
||||
|
||||
Model_Account model = new Model_Account(Localiser);
|
||||
return View(model);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Saxonite.Net.Core.Web.Online.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;
|
||||
using Sarand.Net.Standard.Base.Bus;
|
||||
using Saxonite.Net.Standard.Base.Bus;
|
||||
using Sarand.Net.Standard.Base.App;
|
||||
using Sarand.Net.Standard.Base.Utils;
|
||||
|
||||
namespace Saxonite.Net.Core.Web.Online.Controllers
|
||||
{
|
||||
//=======================================================================
|
||||
public class HomeController : SaxController_Online
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Data members.
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Construction.
|
||||
public HomeController(SaxLocaliser localiser, ILogger<HomeController> logger) :
|
||||
base(localiser, logger)
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
[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);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
[HttpGet]
|
||||
public IActionResult Privacy()
|
||||
{
|
||||
Model_Home model = new Model_Home(Localiser);
|
||||
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,283 @@
|
||||
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.Online.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.Online.Controllers
|
||||
{
|
||||
//=======================================================================
|
||||
// Online Journalist Controller - Ajax actions.
|
||||
//=======================================================================
|
||||
partial class JournalistController
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Journalist Article data.
|
||||
public class ArticleData_List
|
||||
{
|
||||
public String Id { get; set; }
|
||||
public String Headline { get; set; }
|
||||
public String Summary { get; set; }
|
||||
public String RegionCaptionPrimary { get; set; }
|
||||
public String SportCaptionPrimary { get; set; }
|
||||
public String PublishedOnFmt { get; set; }
|
||||
public String CreatedOnFmt { get; set; }
|
||||
public String StatusCode { get; set; }
|
||||
public String StatusDesc { get; set; }
|
||||
}
|
||||
public class ArticleData_Full : ArticleData_List
|
||||
{
|
||||
public String RegionIdPrimary { get; set; }
|
||||
public String SportIdPrimary { get; set; }
|
||||
public String PublishedOn_YYYYMMDD { get; set; }
|
||||
}
|
||||
private void PopulateArticleResponseData(Model_Journalist model, SaxJournalistArticle article, ArticleData_List data)
|
||||
{
|
||||
data.Id = SysId.ToString(article.Id);
|
||||
data.Headline = article.Headline;
|
||||
data.Summary = article.Summary;
|
||||
data.RegionCaptionPrimary = model.GetRegionCaption(article.RegionIdPrimary);
|
||||
data.SportCaptionPrimary = model.GetSportCaption(article.SportIdPrimary);
|
||||
data.CreatedOnFmt = DateUtil.Format(article.CreateStamp.On, "d-MMM-yyyy");
|
||||
data.PublishedOnFmt = DateUtil.Format(article.PublishedAt, "d-MMM-yyyy");
|
||||
data.StatusCode = article.StatusCode;
|
||||
data.StatusDesc = article.StatusDesc;
|
||||
}
|
||||
private void PopulateArticleResponseData(Model_Journalist model, SaxJournalistArticle article, ArticleData_Full data)
|
||||
{
|
||||
PopulateArticleResponseData(model, article, data as ArticleData_List);
|
||||
data.RegionIdPrimary = SysId.ToString(article.RegionIdPrimary);
|
||||
data.SportIdPrimary = SysId.ToString(article.SportIdPrimary);
|
||||
data.PublishedOn_YYYYMMDD = DateUtil.Format(article.PublishedAt, "yyyyMMdd");
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Get Article List ajax request.
|
||||
//-------------------------------------------------------------------
|
||||
public class AjaxResponse_ArticleList : AjaxResponse_Data
|
||||
{
|
||||
public List<ArticleData_List> Articles { get; } = new List<ArticleData_List>();
|
||||
}
|
||||
//-------------------------------------------------------------------
|
||||
[HttpPost]
|
||||
[Route("Journalist/Ajax/GetArticleList")]
|
||||
public IActionResult Ajax_GetArticleList(Model_Journalist model)
|
||||
{
|
||||
String fn = GetType().Name + ".Ajax_GetArticleList";
|
||||
String messageTitle = Localiser["journalist.caption.articles"];
|
||||
AjaxResponse_ArticleList responseData = new AjaxResponse_ArticleList();
|
||||
|
||||
// Validate session and referrer.
|
||||
if (!ValidReferer)
|
||||
return Forbid();
|
||||
if (!ValidUserSession)
|
||||
return GetAjaxResult_InvalidSession(responseData);
|
||||
if (!UserIsJournalist)
|
||||
return GetAjaxResult_GoHome(responseData);
|
||||
|
||||
// Item filter.
|
||||
SaxJournalistArticle.Params prmArticle = new SaxJournalistArticle.Params();
|
||||
prmArticle.UserAccountIdsJournalistIncluded.Add(SessionData.User.UserAccountId);
|
||||
prmArticle.ArticleFilter = model.Filter.Article;
|
||||
prmArticle.StatusCat = SaxJournalistArticle.StatusCat.NotDeleted;
|
||||
model.DataItemTotal = (ulong)ActivityMgr.GetJournalistArticleCount(prmArticle);
|
||||
model.PopulatePagingTo(prmArticle.ResultsPaging);
|
||||
|
||||
// Item columns.
|
||||
ColumnDfn colId = ActivityMgr.GetJournalistArticleColumnDfn(SaxJournalistArticle.Column.Id);
|
||||
ColumnDfn colHeadline = ActivityMgr.GetJournalistArticleColumnDfn(SaxJournalistArticle.Column.Headline);
|
||||
ColumnDfn colSummary = ActivityMgr.GetJournalistArticleColumnDfn(SaxJournalistArticle.Column.Summary);
|
||||
ColumnDfn colRegionIdPrimary = ActivityMgr.GetJournalistArticleColumnDfn(SaxJournalistArticle.Column.RegionIdPrimary);
|
||||
ColumnDfn colSportIdPrimary = ActivityMgr.GetJournalistArticleColumnDfn(SaxJournalistArticle.Column.SportIdPrimary);
|
||||
ColumnDfn colPublishedOn = ActivityMgr.GetJournalistArticleColumnDfn(SaxJournalistArticle.Column.PublishedAt_Date);
|
||||
ColumnDfn colStatus = ActivityMgr.GetJournalistArticleColumnDfn(SaxJournalistArticle.Column.Status);
|
||||
ColumnDfn colCreatedOn = ActivityMgr.GetJournalistArticleColumnDfn(SaxJournalistArticle.Column.CreateOn_Date);
|
||||
List<ColumnDfn> columns = new List<ColumnDfn>()
|
||||
{
|
||||
colId, colHeadline, colSummary,
|
||||
colRegionIdPrimary, colSportIdPrimary, colPublishedOn,
|
||||
colStatus, colCreatedOn
|
||||
};
|
||||
|
||||
// Sort columns.
|
||||
colCreatedOn.SortAscending = false;
|
||||
prmArticle.SortColumns.Add(colCreatedOn);
|
||||
|
||||
// Query items.
|
||||
model.DataItemCount = 0;
|
||||
using (DbResultSet resultSet = ActivityMgr.ExecuteJournalistArticleQuery(columns, prmArticle))
|
||||
{
|
||||
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
|
||||
{
|
||||
model.DataItemCount++;
|
||||
NamedValue.Map values = resultSet.GetRecordValues();
|
||||
ArticleData_List articleData = new ArticleData_List()
|
||||
{
|
||||
Id = values.GetString(colId.DataName),
|
||||
Headline = values.GetString(colHeadline.DataName),
|
||||
Summary = values.GetString(colSummary.DataName),
|
||||
RegionCaptionPrimary = model.GetRegionCaption(values.GetString(colRegionIdPrimary.DataName).ToSysId()),
|
||||
SportCaptionPrimary = model.GetSportCaption(values.GetString(colSportIdPrimary.DataName).ToSysId()),
|
||||
CreatedOnFmt = DateUtil.Format(values.GetDateTime(colCreatedOn.DataName), "d-MMM-yyyy"),
|
||||
PublishedOnFmt = DateUtil.Format(values.GetDateTime(colPublishedOn.DataName), "d-MMM-yyyy"),
|
||||
StatusCode = values.GetString(colStatus.DataName),
|
||||
StatusDesc = SacsSystem.GetStatusDescForCode(values.GetString(colStatus.DataName))
|
||||
};
|
||||
responseData.Articles.Add(articleData);
|
||||
}
|
||||
}
|
||||
|
||||
// Complete response.
|
||||
responseData.SetPaging(model);
|
||||
responseData.Success = true;
|
||||
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Get Article ajax request.
|
||||
//-------------------------------------------------------------------
|
||||
public class AjaxResponse_Article : AjaxResponse_Data
|
||||
{
|
||||
public ArticleData_Full Article { get; } = new ArticleData_Full();
|
||||
}
|
||||
//-------------------------------------------------------------------
|
||||
[HttpPost]
|
||||
[Route("Journalist/Ajax/GetArticle")]
|
||||
public IActionResult Ajax_GetArticle(Model_Journalist model)
|
||||
{
|
||||
String fn = GetType().Name + ".Ajax_GetArticle";
|
||||
String messageTitle = Localiser["journalist.caption.article"];
|
||||
AjaxResponse_Article responseData = new AjaxResponse_Article();
|
||||
|
||||
// Validate session and referrer.
|
||||
if (!ValidReferer)
|
||||
return Forbid();
|
||||
if (!ValidUserSession)
|
||||
return GetAjaxResult_InvalidSession(responseData);
|
||||
if (!UserIsJournalist)
|
||||
return GetAjaxResult_GoHome(responseData);
|
||||
|
||||
// Identify item.
|
||||
SysId articleId = model.ArticleId.ToSysId();
|
||||
if (!SysId.IsValid(articleId))
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.specified"]));
|
||||
|
||||
// Retrieve item.
|
||||
SaxJournalistArticle article = ActivityMgr.GetJournalistArticleById(articleId);
|
||||
if (article == null)
|
||||
{
|
||||
SaLogger.Warn(fn + " Failed to retrieve Journalist Article for Id: '" + model.ArticleId + "'");
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
|
||||
}
|
||||
|
||||
// Construct response.
|
||||
PopulateArticleResponseData(model, article, responseData.Article);
|
||||
|
||||
// Complete response.
|
||||
responseData.Success = true;
|
||||
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Store Article ajax request.
|
||||
//-------------------------------------------------------------------
|
||||
[HttpPost]
|
||||
[Route("JOurnalist/Ajax/StoreArticle")]
|
||||
public IActionResult Ajax_StoreArticle(Model_Journalist model)
|
||||
{
|
||||
String fn = GetType().Name + ".Ajax_StoreArticle";
|
||||
String messageTitle = Localiser["journalist.caption.article"];
|
||||
AjaxResponse_Article responseData = new AjaxResponse_Article();
|
||||
|
||||
// Validate session and referrer.
|
||||
if (!ValidReferer)
|
||||
return Forbid();
|
||||
if (!ValidUserSession)
|
||||
return GetAjaxResult_InvalidSession(responseData);
|
||||
if (!UserIsJournalist)
|
||||
return GetAjaxResult_GoHome(responseData);
|
||||
|
||||
// Validate item properties.
|
||||
model.ValidateArticleData(responseData);
|
||||
if (responseData.HasControlErrors())
|
||||
return Json(responseData);
|
||||
|
||||
// Validate Article against Id against 'Exists' flag.
|
||||
SysId articleId = model.ArticleId.ToSysId();
|
||||
bool articleExists = StringUtil.ToBool(model.ArticleExists);
|
||||
if (articleExists && !SysId.IsValid(articleId))
|
||||
{
|
||||
SaLogger.Warn(fn + " Id not specified for existing Article.");
|
||||
return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"]));
|
||||
}
|
||||
if (!articleExists && SysId.IsValid(articleId))
|
||||
{
|
||||
SaLogger.Warn(fn + " Id specified for new Article: '" + model.ArticleId + "'");
|
||||
return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"]));
|
||||
}
|
||||
|
||||
// Retrieve existing item.
|
||||
SaxJournalistArticle article = ActivityMgr.GetJournalistArticleById(articleId);
|
||||
if (article == null && SysId.IsValid(articleId))
|
||||
{
|
||||
SaLogger.Warn(fn + " Failed to retrieve article for Id: '" + model.ArticleId + "'");
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
|
||||
}
|
||||
|
||||
// Create new item if required.
|
||||
if (article == null)
|
||||
article = ActivityMgr.CreateJournalistArticle(SessionData.User.UserAccountId);
|
||||
|
||||
// Populate item properties.
|
||||
if (model.ArticleRegionId != null)
|
||||
article.RegionIdPrimary = model.ArticleRegionId.ToSysId();
|
||||
if (model.ArticleSportId != null)
|
||||
article.SportIdPrimary = model.ArticleSportId.ToSysId();
|
||||
if (model.ArticleHeadline != null)
|
||||
article.Headline = model.ArticleHeadline;
|
||||
if (model.ArticleSummary != null)
|
||||
article.Summary = model.ArticleSummary;
|
||||
if (model.ArticleStatusCode != null)
|
||||
article.Status = model.ArticleStatus;
|
||||
|
||||
// Store item.
|
||||
if (!SysId.IsValid(article.Id))
|
||||
{
|
||||
if (!ActivityMgr.InsertJournalistArticle(article, DbUpdateInfo))
|
||||
{
|
||||
SaLogger.Warn(fn + " Failed to insert article: " + ActivityMgr.ErrorStateMessage);
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.insert.failed"] + "\n\n" + ActivityMgr.ErrorStateMessage));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ActivityMgr.UpdateJournalistArticle(article, DbUpdateInfo))
|
||||
{
|
||||
SaLogger.Warn(fn + " Failed to update article [Id:" + SysId.ToString(article.Id) + "]: " + ActivityMgr.ErrorStateMessage);
|
||||
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + ActivityMgr.ErrorStateMessage));
|
||||
}
|
||||
}
|
||||
|
||||
// Construct response.
|
||||
PopulateArticleResponseData(model, article, responseData.Article);
|
||||
|
||||
// Complete response.
|
||||
responseData.Success = true;
|
||||
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Saxonite.Net.Core.Web.Base.App;
|
||||
using Saxonite.Net.Core.Web.Online.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Saxonite.Net.Core.Web.Online.Controllers
|
||||
{
|
||||
//=======================================================================
|
||||
// Online Journalist Controller.
|
||||
//=======================================================================
|
||||
public partial class JournalistController : SaxController_Online
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Construction.
|
||||
public JournalistController(SaxLocaliser localiser, ILogger<HomeController> logger) :
|
||||
base(localiser, logger)
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
[HttpGet]
|
||||
public IActionResult Index()
|
||||
{
|
||||
// Validate session.
|
||||
if (!ValidUserSession || !UserIsJournalist)
|
||||
return RedirectToAction("", "");
|
||||
|
||||
// No 'Index' view as yet - default to 'Articles' view.
|
||||
//Model_Journalist model = new Model_Journalist(Localiser);
|
||||
//return View(model);
|
||||
return RedirectToAction("Articles", "Journalist");
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
[HttpGet]
|
||||
public IActionResult Articles()
|
||||
{
|
||||
// Validate session.
|
||||
if (!ValidUserSession || !UserIsJournalist)
|
||||
return RedirectToAction("", "");
|
||||
|
||||
Model_Journalist model = new Model_Journalist(Localiser);
|
||||
return View(model);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Saxonite.Net.Core.Web.Online.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.Online.Controllers
|
||||
{
|
||||
//=======================================================================
|
||||
public class PoliciesController : SaxController_Online
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// 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,96 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Sarand.Net.Standard.Base.App;
|
||||
using Sarand.Net.Standard.Base.Bus;
|
||||
using Sarand.Net.Standard.Base.Core;
|
||||
using Sarand.Net.Standard.Base.Dbas;
|
||||
using Saxonite.Net.Core.Web.Base.App;
|
||||
using Saxonite.Net.Core.Web.Online.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.Online.Controllers
|
||||
{
|
||||
//=======================================================================
|
||||
// Online Reference Controller - Ajax actions.
|
||||
//=======================================================================
|
||||
public partial class ReferenceController : SaxController_Online
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Construction.
|
||||
public ReferenceController(SaxLocaliser localiser, ILogger<HomeController> logger) :
|
||||
base(localiser, logger)
|
||||
{}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Regions
|
||||
//-------------------------------------------------------------------
|
||||
public class AjaxResponse_RegionList : AjaxResponse_Base
|
||||
{
|
||||
public List<SaxModel_Online.RegionData> Regions { get; } = new List<SaxModel_Online.RegionData>();
|
||||
}
|
||||
//-------------------------------------------------------------------
|
||||
[HttpPost]
|
||||
[Route("Reference/Ajax/GetRegions")]
|
||||
public IActionResult Ajax_GetRegionList(Model_Reference model)
|
||||
{
|
||||
String fn = GetType().Name + ".Ajax_GetRegionList";
|
||||
String messageTitle = Localiser["reference.caption.regions"];
|
||||
AjaxResponse_RegionList responseData = new AjaxResponse_RegionList();
|
||||
|
||||
// Validate session and referrer.
|
||||
if (!ValidReferer)
|
||||
return Forbid();
|
||||
if (!ValidUserSession)
|
||||
return GetAjaxResult_InvalidSession(responseData);
|
||||
|
||||
// Populate regions.
|
||||
foreach (SaxModel_Online.RegionData regionData in model.GetRegionDataList_Active())
|
||||
responseData.Regions.Add(regionData);
|
||||
|
||||
// Complete response.
|
||||
responseData.Success = true;
|
||||
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Sports
|
||||
//-------------------------------------------------------------------
|
||||
public class AjaxResponse_SportList : AjaxResponse_Base
|
||||
{
|
||||
public List<SaxModel_Online.SportData> Sports { get; } = new List<SaxModel_Online.SportData>();
|
||||
}
|
||||
//-------------------------------------------------------------------
|
||||
[HttpPost]
|
||||
[Route("Reference/Ajax/GetSports")]
|
||||
public IActionResult Ajax_GetSportList(Model_Reference model)
|
||||
{
|
||||
String fn = GetType().Name + ".Ajax_GetSportList";
|
||||
String messageTitle = Localiser["reference.caption.sports"];
|
||||
AjaxResponse_SportList responseData = new AjaxResponse_SportList();
|
||||
|
||||
// Validate session and referrer.
|
||||
if (!ValidReferer)
|
||||
return Forbid();
|
||||
if (!ValidUserSession)
|
||||
return GetAjaxResult_InvalidSession(responseData);
|
||||
|
||||
// Populate sports.
|
||||
foreach (SaxModel_Online.SportData sportData in model.GetSportDataList_Active())
|
||||
responseData.Sports.Add(sportData);
|
||||
|
||||
// Complete response.
|
||||
responseData.Success = true;
|
||||
|
||||
return Json(responseData);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
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.Base.App;
|
||||
using Saxonite.Net.Core.Web.Base.Controllers;
|
||||
using Saxonite.Net.Core.Web.Online.App;
|
||||
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.Online.Controllers
|
||||
{
|
||||
//=======================================================================
|
||||
public class SaxController_Online : SaxController_Base
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Data members.
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Protected construction.
|
||||
protected SaxController_Online(SaxLocaliser localiser, Microsoft.Extensions.Logging.ILogger<HomeController> logger) : base(localiser)
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Session data.
|
||||
//-------------------------------------------------------------------
|
||||
/// <summary>Online session data</summary>
|
||||
protected SaxSessionData_Online SessionData_Online { get { return SessionData as SaxSessionData_Online; } }
|
||||
protected override SaxSessionData DeserializeSessionData(String sessionDataJson)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<SaxSessionData_Online>(sessionDataJson, SaxSessionDataJsonSerializerSettings);
|
||||
}
|
||||
// Security profile.
|
||||
public bool UserIsJournalist { get { return SessionData != null && SessionData.User != null && SessionData.User.IsJournalist; } }
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// 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_Online = 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_Online)
|
||||
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_Online(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 - 'Keep Logged In' is termed 'Remember Me' in the Online application.
|
||||
public static String CookieName_KeepLoggedIn = "SAXRM";
|
||||
public static String Base64Key_KeepLoggedIn = SacsEncryptionHelper.EncodePlainKey("<=Saxonite^OnlIne{R$Member-m3}~>", SacsEncryptionHelper.KeyLength);
|
||||
//public static String Base64IV_KeepLoggedIn_Generated = SacsEncryptionHelper.InitSymmetricEncryptionIV(Base64Key_KeepLoggedIn);
|
||||
public static String Base64IV_KeepLoggedIn = "zZiDncOwG4H+PpChrd4DvA==";
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// 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_Online.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,455 @@
|
||||
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.Online.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.Online.Controllers
|
||||
{
|
||||
//=======================================================================
|
||||
// Online Security Controller - Ajax actions.
|
||||
//=======================================================================
|
||||
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 referrer.
|
||||
if (!ValidReferer)
|
||||
return Forbid();
|
||||
|
||||
// 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_Online?.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();
|
||||
|
||||
// Validate session and referrer.
|
||||
if (!ValidReferer)
|
||||
return Forbid();
|
||||
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);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
public SaxUserAccount GetUserAccountForSignin(Model_Security model, AjaxResponse_Data responseData)
|
||||
{
|
||||
String fn = "SigninController.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")))
|
||||
{
|
||||
// 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);
|
||||
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"]);
|
||||
return userAccount;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// 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"];
|
||||
|
||||
// Validate session and referrer.
|
||||
if (!ValidReferer)
|
||||
return Forbid();
|
||||
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_Online.User.UserAccountId);
|
||||
if (userAccount == null)
|
||||
{
|
||||
SaLogger.Warn(fn + " System error - failed to retrieve account details.");
|
||||
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_Base
|
||||
{
|
||||
public int Score { get; set; }
|
||||
}
|
||||
// Ajax request.
|
||||
[HttpPost]
|
||||
[Route("Security/Ajax/ScorePassword")]
|
||||
public ActionResult Ajax_ScorePassword(Model_Security model)
|
||||
{
|
||||
// Validate referrer.
|
||||
if (!ValidReferer)
|
||||
return Forbid();
|
||||
|
||||
// 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"];
|
||||
|
||||
// Validate referrer.
|
||||
if (!ValidReferer)
|
||||
return Forbid();
|
||||
|
||||
// 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_Online != null)
|
||||
{
|
||||
userAccount = AccountMgr.GetUserAccountById(SessionData_Online.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 referrer.
|
||||
if (!ValidReferer)
|
||||
return Forbid();
|
||||
|
||||
// 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,125 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Sarand.Net.Standard.Base.Bus;
|
||||
using Sarand.Net.Standard.Base.Core;
|
||||
using Sarand.Net.Standard.Base.Helpers;
|
||||
using Saxonite.Net.Core.Web.Online.App;
|
||||
using Saxonite.Net.Core.Web.Online.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;
|
||||
using Sarand.Net.Standard.Base.Utils;
|
||||
using Sarand.Net.Standard.Base.App;
|
||||
|
||||
namespace Saxonite.Net.Core.Web.Online.Controllers
|
||||
{
|
||||
//=======================================================================
|
||||
// Online Security Controller.
|
||||
//=======================================================================
|
||||
public partial class SecurityController : SaxController_Online
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// 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.
|
||||
model.PageCaption = Localiser["security.caption.change.password"];
|
||||
|
||||
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.PageCaption = Localiser["security.caption.request.password"];
|
||||
model.ProhibitCache = false;
|
||||
|
||||
return View(model);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
|
||||
namespace Saxonite.Net.Core.Web.Online.Models
|
||||
{
|
||||
//=======================================================================
|
||||
public class ErrorViewModel
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
public string RequestId { get; set; }
|
||||
|
||||
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
using Sarand.Net.Standard.Base.Bus;
|
||||
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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Saxonite.Net.Core.Web.Online.Models
|
||||
{
|
||||
//=======================================================================
|
||||
public class Model_Account : SaxModel_Online
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Construction.
|
||||
public Model_Account(SaxLocaliser localiser) :
|
||||
base(localiser)
|
||||
{
|
||||
AdditionalFiles_CSS.Add("css/sax-account.css");
|
||||
AdditionalFiles_JS.Add("js/sax-account.js");
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Properties.
|
||||
public String AccountEmailAddress { get; set; }
|
||||
public String AccountTitle { get; set; }
|
||||
public String AccountFirstName { get; set; }
|
||||
public String AccountLastName { get; set; }
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
public bool ValidateContactDetails(SaxController_Base.AjaxResponse_Data responseData)
|
||||
{
|
||||
// Title.
|
||||
if (AccountTitle != null)
|
||||
{
|
||||
responseData.SetControlValue(nameof(AccountTitle), AccountTitle);
|
||||
if (StringUtil.IsEmpty(AccountTitle))
|
||||
responseData.SetControlError(nameof(AccountTitle), Localiser["msg.validation.mandatory.value"]);
|
||||
else if (StringUtil.GetLength(AccountTitle) > GetControlMaxLength(SaxDbDfn.ColumnIdx.UserAccount_Title))
|
||||
responseData.SetControlError(nameof(AccountTitle), Localiser["msg.validation.maximum.length", GetControlMaxLength(SaxDbDfn.ColumnIdx.JournalistArticle_Headline)]);
|
||||
}
|
||||
|
||||
// First Name.
|
||||
if (AccountFirstName != null)
|
||||
{
|
||||
responseData.SetControlValue(nameof(AccountFirstName), AccountFirstName);
|
||||
if (StringUtil.IsEmpty(AccountFirstName))
|
||||
responseData.SetControlError(nameof(AccountFirstName), Localiser["msg.validation.mandatory.value"]);
|
||||
else if (StringUtil.GetLength(AccountFirstName) > GetControlMaxLength(SaxDbDfn.ColumnIdx.UserAccount_FirstName))
|
||||
responseData.SetControlError(nameof(AccountFirstName), Localiser["msg.validation.maximum.length", GetControlMaxLength(SaxDbDfn.ColumnIdx.JournalistArticle_Headline)]);
|
||||
}
|
||||
|
||||
// Last Name.
|
||||
if (AccountLastName != null)
|
||||
{
|
||||
responseData.SetControlValue(nameof(AccountLastName), AccountLastName);
|
||||
if (StringUtil.IsEmpty(AccountLastName))
|
||||
responseData.SetControlError(nameof(AccountLastName), Localiser["msg.validation.mandatory.value"]);
|
||||
else if (StringUtil.GetLength(AccountLastName) > GetControlMaxLength(SaxDbDfn.ColumnIdx.UserAccount_LastName))
|
||||
responseData.SetControlError(nameof(AccountLastName), Localiser["msg.validation.maximum.length", GetControlMaxLength(SaxDbDfn.ColumnIdx.JournalistArticle_Headline)]);
|
||||
}
|
||||
|
||||
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.Online.Models
|
||||
{
|
||||
//=======================================================================
|
||||
public class Model_Home : SaxModel_Online
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Construction.
|
||||
public Model_Home(SaxLocaliser localiser) :
|
||||
base(localiser)
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
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.Standard.Base.Bus;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Saxonite.Net.Core.Web.Online.Models
|
||||
{
|
||||
//=======================================================================
|
||||
public class Model_Journalist : SaxModel_Online
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Construction.
|
||||
public Model_Journalist(SaxLocaliser localiser) :
|
||||
base(localiser)
|
||||
{
|
||||
//AdditionalFiles_CSS.Add("css/sax-journalist.css");
|
||||
AdditionalFiles_JS.Add("js/sax-journalist.js");
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// List configuration.
|
||||
public class FilterData
|
||||
{
|
||||
public String Article { get; set; }
|
||||
}
|
||||
public FilterData Filter { get; } = new FilterData();
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Article properties.
|
||||
public String ArticleId { get; set; }
|
||||
public String ArticleExists { get; set; }
|
||||
// Region.
|
||||
public String ArticleRegionId { get; set; }
|
||||
public String ArticleRegionCaption { get; }
|
||||
// Sport.
|
||||
public String ArticleSportId { get; set; }
|
||||
public String ArticleSportCaption { get; set; }
|
||||
// Article Content.
|
||||
public String ArticleHeadline { get; set; }
|
||||
public String ArticleSummary { get; set; }
|
||||
public String ArticleStatusCode { get; set; }
|
||||
public SaStatus ArticleStatus { get { return SacsSystem.GetStatusForCode(ArticleStatusCode); } }
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Validation.
|
||||
//-------------------------------------------------------------------
|
||||
// Article.
|
||||
public bool ValidateArticleData(SaxController_Base.AjaxResponse_Data responseData)
|
||||
{
|
||||
// Article headline.
|
||||
if (ArticleHeadline != null)
|
||||
{
|
||||
responseData.SetControlValue(nameof(ArticleHeadline), ArticleHeadline);
|
||||
if (StringUtil.IsEmpty(ArticleHeadline))
|
||||
responseData.SetControlError(nameof(ArticleHeadline), Localiser["msg.validation.mandatory.value"]);
|
||||
else if (StringUtil.GetLength(ArticleHeadline) > GetControlMaxLength(SaxDbDfn.ColumnIdx.JournalistArticle_Headline))
|
||||
responseData.SetControlError(nameof(ArticleHeadline), Localiser["msg.validation.maximum.length", GetControlMaxLength(SaxDbDfn.ColumnIdx.JournalistArticle_Headline)]);
|
||||
}
|
||||
|
||||
// Article summary.
|
||||
if (ArticleSummary != null)
|
||||
{
|
||||
responseData.SetControlValue(nameof(ArticleSummary), ArticleSummary);
|
||||
if (StringUtil.IsEmpty(ArticleSummary))
|
||||
responseData.SetControlError(nameof(ArticleSummary), Localiser["msg.validation.mandatory.value"]);
|
||||
else if (StringUtil.GetLength(ArticleSummary) > GetControlMaxLength(SaxDbDfn.ColumnIdx.JournalistArticle_Summary))
|
||||
responseData.SetControlError(nameof(ArticleSummary), Localiser["msg.validation.maximum.length", GetControlMaxLength(SaxDbDfn.ColumnIdx.JournalistArticle_Summary)]);
|
||||
}
|
||||
|
||||
// Article region.
|
||||
if (ArticleRegionId != null)
|
||||
{
|
||||
SysId articleRegionId = ArticleRegionId.ToSysId();
|
||||
if (!SysId.IsValid(articleRegionId))
|
||||
responseData.SetControlError(nameof(ArticleRegionId), Localiser["msg.validation.mandatory.value"]);
|
||||
}
|
||||
|
||||
// Article sport.
|
||||
if (ArticleSportId != null)
|
||||
{
|
||||
SysId articleSportId = ArticleSportId.ToSysId();
|
||||
if (!SysId.IsValid(articleSportId))
|
||||
responseData.SetControlError(nameof(ArticleSportId), Localiser["msg.validation.mandatory.value"]);
|
||||
}
|
||||
|
||||
// Article status.
|
||||
if (ArticleStatusCode != null)
|
||||
{
|
||||
if (StringUtil.IsEmpty(ArticleStatusCode))
|
||||
responseData.SetControlError(nameof(ArticleStatusCode), Localiser["msg.validation.mandatory.value"]);
|
||||
else if (!SaxJournalistArticle.ValidStatuses.Contains(ArticleStatus))
|
||||
responseData.SetControlError(nameof(ArticleStatusCode), Localiser["msg.validation.invalid.value"]);
|
||||
}
|
||||
|
||||
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.Online.Models
|
||||
{
|
||||
//=======================================================================
|
||||
public class Model_Policies : SaxModel_Online
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Construction.
|
||||
public Model_Policies(SaxLocaliser localiser) :
|
||||
base(localiser)
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Properties.
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
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.Standard.Base.Bus;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Saxonite.Net.Core.Web.Online.Models
|
||||
{
|
||||
//=======================================================================
|
||||
public class Model_Reference: SaxModel_Online
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Construction.
|
||||
public Model_Reference(SaxLocaliser localiser) :
|
||||
base(localiser)
|
||||
{}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
@@ -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.Online.Models
|
||||
{
|
||||
//=======================================================================
|
||||
public class Model_Security : SaxModel_Online
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// 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,114 @@
|
||||
using Sarand.Net.Standard.Base.Bus;
|
||||
using Saxonite.Net.Core.Web.Base.App;
|
||||
using Saxonite.Net.Core.Web.Base.Models;
|
||||
using Saxonite.Net.Core.Web.Online.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.Online.Models
|
||||
{
|
||||
//=======================================================================
|
||||
public class SaxModel_Online : SaxModel_Base
|
||||
{
|
||||
//-------------------------------------------------------------------
|
||||
// Protected construction.
|
||||
protected SaxModel_Online(SaxLocaliser localiser) :
|
||||
base(localiser)
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Session.
|
||||
public SaxSessionData_Online SessionData_Online { get { return SessionData as SaxSessionData_Online; } }
|
||||
// Security profile.
|
||||
public bool UserIsJournalist { get { return SessionData != null && SessionData.User != null && SessionData.User.IsJournalist; } }
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Reference Data.
|
||||
//-------------------------------------------------------------------
|
||||
// Regions.
|
||||
/// <summary>Region Data class - to be JSON serialised in Ajax responses.</summary>
|
||||
public class RegionData
|
||||
{
|
||||
public String Id { get; set; }
|
||||
public int RegionType { get; set; }
|
||||
public String RegionTypeCode { get; set; }
|
||||
public String RegionTypeDesc { get; set; }
|
||||
public String Name { get; set; }
|
||||
public String ParentId { get; set; }
|
||||
public String StatusCode { get; set; }
|
||||
public String StatusDesc { get; set; }
|
||||
}
|
||||
public List<RegionData> GetRegionDataList_Active()
|
||||
{
|
||||
List<RegionData> regions = new List<RegionData>();
|
||||
foreach (SaxRegion region in DataCacheConsole.GetActiveRegions())
|
||||
{
|
||||
RegionData regionData = new RegionData()
|
||||
{
|
||||
Id = SysId.ToString(region.Id),
|
||||
RegionType = (int)region.RegionType,
|
||||
RegionTypeCode = region.RegionType.ToString(),
|
||||
RegionTypeDesc = Localiser.GetRegionTypeCaption(region.RegionType),
|
||||
Name = GetRegionCaption(region),
|
||||
ParentId = SysId.ToString(region.RegionIdParent),
|
||||
StatusCode = region.StatusCode,
|
||||
StatusDesc = Localiser.GetStatusCaption(region.Status)
|
||||
};
|
||||
regions.Add(regionData);
|
||||
}
|
||||
return regions;
|
||||
}
|
||||
public String GetRegionCaption(SysId regionId)
|
||||
{
|
||||
SaxRegion region = DataCacheConsole.GetActiveRegions().Find(item => item.Id.Equals(regionId));
|
||||
return Localiser.GetRegionName(region);
|
||||
}
|
||||
public String GetRegionCaption(SaxRegion region)
|
||||
{
|
||||
return Localiser.GetRegionName(region);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Sports.
|
||||
public class SportData
|
||||
{
|
||||
public Int32 Id { get; set; }
|
||||
public String Name { get; set; }
|
||||
public String StatusCode { get; set; }
|
||||
public String StatusDesc { get; set; }
|
||||
}
|
||||
public List<SportData> GetSportDataList_Active()
|
||||
{
|
||||
List<SportData> sports = new List<SportData>();
|
||||
foreach (SaxSport sport in DataCacheConsole.GetActiveSports())
|
||||
{
|
||||
SportData sportData = new SportData()
|
||||
{
|
||||
Id = SysId.ToInt(sport.Id),
|
||||
Name = GetSportCaption(sport),
|
||||
StatusCode = sport.StatusCode,
|
||||
StatusDesc = Localiser.GetStatusCaption(sport.Status)
|
||||
};
|
||||
sports.Add(sportData);
|
||||
}
|
||||
return sports;
|
||||
}
|
||||
public String GetSportCaption(SysId sportId)
|
||||
{
|
||||
SaxSport sport = DataCacheConsole.GetActiveSports().Find(item => item.Id.Equals(sportId));
|
||||
return Localiser.GetSportName(sport);
|
||||
}
|
||||
public String GetSportCaption(SaxSport sport)
|
||||
{
|
||||
return Localiser.GetSportName(sport);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
}
|
||||
//=======================================================================
|
||||
}
|
||||
Reference in New Issue
Block a user