Move Saxonite’s projects into a dedicated `Workspace` folder and align the solution with .NET 10 and the current Common workspace. Update encryption integration, test project structure and coverage, and repository guidance and upgrade notes. - Relocate application and library projects while retaining Git rename history. - Split tests into Admin, Online, and Shared projects; add representative behavioral and serialization tests. - Update Common integration, application version, and repository documentation. Application version: 1.3.0 Reviewed-on: #1 Co-authored-by: andy <andy@sarand.com>
728 lines
34 KiB
C#
728 lines
34 KiB
C#
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.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.Helpers;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Saxonite.Net.Core.Web.Online.Controllers
|
|
{
|
|
//=======================================================================
|
|
// Online Organisation Controller - Members - Ajax actions.
|
|
//=======================================================================
|
|
partial class OrganisationController
|
|
{
|
|
//-------------------------------------------------------------------
|
|
// Organisation Member data.
|
|
private class MemberListData : AjaxResponse_Data.ItemData_Base
|
|
{
|
|
public String UserAccountId { get; set; }
|
|
public bool? CurrentUser { get; set; } = null;
|
|
public String EmailAddress { get; set; }
|
|
public String DisplayName { get; set; }
|
|
public String MemberRoles { get; set; }
|
|
public List<String> RoleCodes { get; set; } = new List<string>();
|
|
public List<CodeDesc> Roles { get; set; } = new List<CodeDesc>();
|
|
public String AdminComment { get; set; }
|
|
}
|
|
private class MemberItemData : MemberListData
|
|
{
|
|
public String Title { get; set; }
|
|
public String FirstName { get; set; }
|
|
public String LastName { get; set; }
|
|
}
|
|
private void PopulateMemberResponseListData(Model_Organisation_Members model, SaxUserOrganisation userOrganisation, SaxUserAccount userAccount, SaxOrganisation organisation, MemberListData data)
|
|
{
|
|
data.Id = model.EncryptId(userOrganisation.Id);
|
|
data.UserAccountId = model.EncryptId(userOrganisation.UserAccountId);
|
|
if (userOrganisation.UserAccountId.Equals(SessionUserAccountId)) data.CurrentUser = true;
|
|
data.EmailAddress = userAccount.EmailAddress;
|
|
data.DisplayName = userAccount.GetDisplayName();
|
|
data.RoleCodes.Clear();
|
|
data.Roles.Clear();
|
|
foreach (String memberRoleCode in userOrganisation.GetOrganisationRoleCodes())
|
|
{
|
|
if (StringUtil.IsEmpty(memberRoleCode.Trim())) continue;
|
|
Sax.OrganisationRole organisationRole = (Sax.OrganisationRole)OrganisationRoleCodes.GetTypeForCode(memberRoleCode);
|
|
if (organisationRole <= 0 || organisationRole == Sax.OrganisationRole.Member) continue;
|
|
data.RoleCodes.Add(memberRoleCode);
|
|
data.Roles.Add(memberRoleCode, Localiser.GetOrganisationRoleCaption(organisationRole));
|
|
}
|
|
data.MemberRoles = data.Roles.Select(item => item.Desc).ToList().Concat(", ");
|
|
data.AdminComment = userOrganisation.AdminComment;
|
|
// NB UserAccount status (not UserOrganisation).
|
|
data.StatusCode = userAccount.StatusCode;
|
|
data.StatusDesc = userAccount.StatusDesc;
|
|
data.CreatedOnFmt = Localiser.FormatDate(userOrganisation.CreateStamp.On);
|
|
}
|
|
private void PopulateMemberResponseItemData(Model_Organisation_Members model, SaxUserOrganisation userOrganisation, SaxUserAccount userAccount, SaxOrganisation organisation, MemberItemData data)
|
|
{
|
|
PopulateMemberResponseListData(model, userOrganisation, userAccount, organisation, data as MemberListData);
|
|
data.Title = userAccount.Title;
|
|
data.FirstName = userAccount.FirstName;
|
|
data.LastName = userAccount.LastName;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Get Member List ajax request.
|
|
//-------------------------------------------------------------------
|
|
private class AjaxResponse_MemberList : AjaxResponse_Data
|
|
{
|
|
// Construction.
|
|
public AjaxResponse_MemberList(SaxModel_Online model) : base(model) { }
|
|
// Member list.
|
|
public List<MemberListData> Members { get; } = new List<MemberListData>();
|
|
// User profile.
|
|
public class UserData
|
|
{
|
|
public bool CanManage { get; set; }
|
|
}
|
|
public UserData User { get; } = new UserData();
|
|
// Reference data.
|
|
public List<CodeDesc> Titles { get; } = new List<CodeDesc>();
|
|
public List<CodeDesc> MemberRoles { get; } = new List<CodeDesc>();
|
|
}
|
|
//-------------------------------------------------------------------
|
|
[HttpPost]
|
|
[Route("Organisation/Ajax/GetMemberList")]
|
|
[Route("Organisation/Ajax/GetMembers")]
|
|
public IActionResult Ajax_GetMemberList(Model_Organisation_Members model)
|
|
{
|
|
String fn = GetType().Name + ".Ajax_GetMemberList";
|
|
String messageTitle = Localiser["organisation.members"];
|
|
AjaxResponse_MemberList responseData = new AjaxResponse_MemberList(model);
|
|
|
|
// Validate session and referrer.
|
|
if (!ValidReferer) return Forbid();
|
|
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
|
|
RefreshModel(model);
|
|
|
|
// Record disposable items as they are created so all can be disposed when needed.
|
|
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
|
|
{
|
|
|
|
// Organisation.
|
|
SysId organisationId = model.ExtractSysId(model.Filter.OrganisationId);
|
|
SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationId);
|
|
disposableItems.Add(organisation);
|
|
if (organisation == null || !organisation.IsStatusActive)
|
|
{
|
|
SaLogger.Warn(fn + " " + (organisation == null ? "Invalid" : "Inactive") + " organisation for Member list. OrganisationId: '" + model.Filter.OrganisationId + "'");
|
|
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.system"]));
|
|
}
|
|
|
|
// Store filter data.
|
|
SessionData_Online.Filter.SetMemberOrganisation(organisation);
|
|
|
|
// Reference data - titles.
|
|
foreach (TypeCodeDesc title in SaxSystem.GetCodesOfType(Sax.CodeType.Title))
|
|
// ToDo Title translations.
|
|
responseData.Titles.Add(title.Code, title.Desc);
|
|
|
|
// Reference data - member roles.
|
|
foreach (TypeCodeDesc memberRole in OrganisationRoleCodes.FindAll(item => item.Type != (int)Sax.OrganisationRole.Member))
|
|
responseData.MemberRoles.Add(memberRole.Code, Localiser.GetOrganisationRoleCaption((Sax.OrganisationRole)memberRole.Type));
|
|
|
|
// Session User roles.
|
|
responseData.User.CanManage = AccountMgr.UserHasAnyOrganisationRole(SessionUserAccountId, organisation.Id, new List<Sax.OrganisationRole> { Sax.OrganisationRole.Admin, Sax.OrganisationRole.MemberAdmin });
|
|
|
|
// Item filter.
|
|
SaxUserOrganisation.Params prmMember = new SaxUserOrganisation.Params();
|
|
prmMember.OrganisationIdsIncluded.Add(organisationId);
|
|
prmMember.UserAccountStatusCat = responseData.User.CanManage ? SaxUserAccount.StatusCat.AwaitingOrActive : SaxUserAccount.StatusCat.ActiveOnly;
|
|
prmMember.OrganisationMembersOnly = true;
|
|
prmMember.UserFilterPartial = model.Filter.Member;
|
|
prmMember.StatusCat = SaxUserOrganisation.StatusCat.ActiveOnly;
|
|
model.Paging.ItemTotal = (ulong)AccountMgr.GetUserOrganisationCount(prmMember);
|
|
model.PopulatePagingTo(prmMember.ResultsPaging);
|
|
|
|
// Item columns.
|
|
ColumnDfn colId = AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.Id);
|
|
ColumnDfn colUserAccountId = AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.UserAccountId);
|
|
ColumnDfn colEmailAddress = responseData.User.CanManage ? AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.User_EmailAddress) : new ColumnDfn(-1, "");
|
|
ColumnDfn colDisplayName = AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.User_DisplayName);
|
|
ColumnDfn colOrganisationRoleCodes = responseData.User.CanManage ? AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.OrganisationRoleCodes) : new ColumnDfn(-1, "");
|
|
ColumnDfn colUserStatus = AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.User_Status);
|
|
ColumnDfn colAdminComment = responseData.User.CanManage ? AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.AdminComment) : new ColumnDfn(-1, "");
|
|
ColumnDfn colCreatedOn = AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.CreateOn_Date);
|
|
List<ColumnDfn> columns = new List<ColumnDfn>()
|
|
{
|
|
colId, colUserAccountId , colEmailAddress, colDisplayName,
|
|
colOrganisationRoleCodes, colUserStatus,
|
|
colAdminComment, colCreatedOn
|
|
};
|
|
|
|
// Sort columns.
|
|
colCreatedOn.SortAscending = false;
|
|
prmMember.SortColumns.Add(colDisplayName);
|
|
|
|
// Query items.
|
|
using (DbResultSet resultSet = AccountMgr.ExecuteUserOrganisationQuery(columns, prmMember))
|
|
{
|
|
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
|
|
{
|
|
NamedValue.Map values = resultSet.GetRecordValues();
|
|
MemberListData memberData = new MemberListData()
|
|
{
|
|
Id = model.EncryptId(new SysId(values.GetLong(colId.DataName))),
|
|
UserAccountId = model.EncryptId(new SysId(values.GetLong(colUserAccountId.DataName))),
|
|
EmailAddress = values.GetString(colEmailAddress.DataName),
|
|
DisplayName = AccountMgr.GetUserOrganisationDataValue(colDisplayName, values).ToString(),
|
|
CreatedOnFmt = Localiser.FormatDate(values.GetDateTime(colCreatedOn.DataName)),
|
|
AdminComment = values.GetString(colAdminComment.DataName),
|
|
// NB UserAccount status (not UserOrganisation).
|
|
StatusCode = values.GetString(colUserStatus.DataName),
|
|
StatusDesc = SacsSystem.GetStatusDescForCode(values.GetString(colUserStatus.DataName))
|
|
};
|
|
if (model.ExtractSysId(memberData.UserAccountId).Equals(SessionUserAccountId)) memberData.CurrentUser = true;
|
|
String memberRoleCodes = values.GetString(colOrganisationRoleCodes.DataName);
|
|
foreach (String memberRoleCode in StringListUtil.Split(memberRoleCodes, SaxUserOrganisation.UserOrgnisationRoleDelim))
|
|
{
|
|
if (StringUtil.IsEmpty(memberRoleCode.Trim())) continue;
|
|
Sax.OrganisationRole organisationRole = (Sax.OrganisationRole)OrganisationRoleCodes.GetTypeForCode(memberRoleCode);
|
|
if (organisationRole <= 0 || organisationRole == Sax.OrganisationRole.Member) continue;
|
|
memberData.RoleCodes.Add(memberRoleCode);
|
|
memberData.Roles.Add(memberRoleCode, Localiser.GetOrganisationRoleCaption(organisationRole));
|
|
}
|
|
memberData.MemberRoles = memberData.Roles.Select(item => item.Desc).ToList().Concat(", ");
|
|
responseData.Members.Add(memberData);
|
|
}
|
|
}
|
|
}
|
|
if (model.Paging.IsPaged)
|
|
model.Paging.ItemCount = (ushort)responseData.Members.Count;
|
|
|
|
// Complete response.
|
|
responseData.SetPaging(model);
|
|
responseData.Success = true;
|
|
return Json(responseData);
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Get Member ajax request.
|
|
//-------------------------------------------------------------------
|
|
private class AjaxResponse_Member : AjaxResponse_Data
|
|
{
|
|
// Construction.
|
|
public AjaxResponse_Member(SaxModel_Online model) : base(model) { }
|
|
// Member details.
|
|
public MemberItemData Member { get; } = new MemberItemData();
|
|
}
|
|
//-------------------------------------------------------------------
|
|
[HttpPost]
|
|
[Route("Organisation/Ajax/GetMember")]
|
|
public IActionResult Ajax_GetMember(Model_Organisation_Members model)
|
|
{
|
|
String fn = GetType().Name + ".Ajax_GetMember";
|
|
String messageTitle = Localiser["label.member"];
|
|
AjaxResponse_Member responseData = new AjaxResponse_Member(model);
|
|
|
|
// Validate session and referrer.
|
|
if (!ValidReferer) return Forbid();
|
|
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
|
|
RefreshModel(model);
|
|
|
|
// Identify item.
|
|
SysId memberId = model.ExtractSysId(model.MemberId);
|
|
if (!SysId.IsValid(memberId))
|
|
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.specified"]));
|
|
|
|
// Record disposable items as they are created so all can be disposed when needed.
|
|
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
|
|
{
|
|
|
|
// Retrieve item.
|
|
SaxUserOrganisation userOrganisation = AccountMgr.GetUserOrganisationById(memberId);
|
|
disposableItems.Add(userOrganisation);
|
|
if (userOrganisation == null)
|
|
{
|
|
SaLogger.Warn(fn + " Failed to retrieve Organisation Member for Id: '" + model.MemberId + "'");
|
|
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
|
|
}
|
|
|
|
// Validate organisation.
|
|
SaxOrganisation organisation = AccountMgr.GetOrganisationById(userOrganisation.OrganisationId);
|
|
disposableItems.Add(organisation);
|
|
if (organisation == null || !organisation.IsStatusActive)
|
|
{
|
|
SaLogger.Warn(fn + " " + (organisation == null ? "Invalid" : "Inactive") + " Organisation for Member. Id: '" + userOrganisation.OrganisationId + "'");
|
|
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.system"]));
|
|
}
|
|
|
|
// Session User roles.
|
|
bool userCanManageMembers = AccountMgr.UserHasAnyOrganisationRole(SessionUserAccountId, organisation.Id, new List<Sax.OrganisationRole> { Sax.OrganisationRole.Admin, Sax.OrganisationRole.MemberAdmin });
|
|
|
|
// Validate user account.
|
|
SaxUserAccount userAccount = AccountMgr.GetUserAccountById(userOrganisation.UserAccountId);
|
|
disposableItems.Add(userAccount);
|
|
bool userActive = userAccount.IsStatusActive || (userAccount.IsStatusAwaiting && userCanManageMembers);
|
|
if (userAccount == null || !userActive)
|
|
{
|
|
SaLogger.Warn(fn + " " + (organisation == null ? "Invalid" : "Inactive") + " User Account for Member. Id: '" + userOrganisation.UserAccountId + "'");
|
|
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.system"]));
|
|
}
|
|
|
|
// Make a note of the selected Organisation.
|
|
SessionData_Online.Filter.SetMemberOrganisation(organisation);
|
|
|
|
PopulateMemberResponseItemData(model, userOrganisation, userAccount, organisation, responseData.Member);
|
|
}
|
|
|
|
// Construct response.
|
|
responseData.Success = true;
|
|
return Json(responseData);
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Store Member ajax request.
|
|
//-------------------------------------------------------------------
|
|
private class AjaxResponse_StoreMember : AjaxResponse_Data
|
|
{
|
|
// Construction.
|
|
public AjaxResponse_StoreMember(SaxModel_Online model) : base(model) { }
|
|
// Member details.
|
|
public MemberItemData Member { get; } = new MemberItemData();
|
|
// Feedback.
|
|
public bool? MemberNameRequired { get; set; } = null;
|
|
}
|
|
[HttpPost]
|
|
[Route("Organisation/Ajax/StoreMember")]
|
|
public IActionResult Ajax_StoreMember(Model_Organisation_Members model)
|
|
{
|
|
String fn = GetType().Name + ".Ajax_StoreMember";
|
|
String messageTitle = Localiser["label.member"];
|
|
AjaxResponse_StoreMember responseData = new AjaxResponse_StoreMember(model);
|
|
|
|
// Validate session and referrer.
|
|
if (!ValidReferer) return Forbid();
|
|
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
|
|
RefreshModel(model);
|
|
|
|
// Validate item properties.
|
|
model.ValidateMemberData(responseData);
|
|
if (responseData.HasControlErrors())
|
|
return Json(responseData);
|
|
|
|
//---------------------------------------------------------------
|
|
// User Organisation identifiers.
|
|
// New Member:
|
|
// Organisation Id [MemberOrganisationId].
|
|
// Email address [MemberEmailAddress].
|
|
// Existing member:
|
|
// User Organisation Id [MemberId].
|
|
//---------------------------------------------------------------
|
|
|
|
// Validate Member against Id against 'Exists' flag.
|
|
SysId memberId = model.ExtractSysId(model.MemberId);
|
|
bool memberExists = StringUtil.ToBool(model.MemberExists);
|
|
if (memberExists && !SysId.IsValid(memberId))
|
|
{
|
|
SaLogger.Warn(fn + " Id not specified for existing User Organisation.");
|
|
return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"]));
|
|
}
|
|
if (!memberExists && SysId.IsValid(memberId))
|
|
{
|
|
SaLogger.Warn(fn + " Id specified for new User Organisation: '" + model.MemberId + "'");
|
|
return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"]));
|
|
}
|
|
|
|
// Record disposable items as they are created so all can be disposed when needed.
|
|
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
|
|
{
|
|
|
|
// Retrieve existing item.
|
|
SaxUserOrganisation userOrganisation = AccountMgr.GetUserOrganisationById(memberId);
|
|
disposableItems.Add(userOrganisation);
|
|
if (userOrganisation == null && SysId.IsValid(memberId))
|
|
{
|
|
SaLogger.Warn(fn + " Failed to retrieve User Organisation for Id: '" + model.MemberId + "'");
|
|
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.id.not.found"]));
|
|
}
|
|
|
|
// Validate the Organisation.
|
|
SysId organisationId = userOrganisation != null ? userOrganisation.OrganisationId : model.ExtractSysId(model.MemberOrganisationId);
|
|
if (!SysId.IsValid(organisationId))
|
|
{
|
|
SaLogger.Warn(fn + " Organisation Id not specified for Member.");
|
|
return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"]));
|
|
}
|
|
SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationId);
|
|
disposableItems.Add(organisation);
|
|
if (organisation == null || !organisation.IsStatusActive)
|
|
{
|
|
SaLogger.Warn(fn + " " + (organisation == null ? "Invalid" : "Inactive") + " Organisation for User Organisation. OrganisationId: '" + model.MemberOrganisationId + "'");
|
|
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.system"]));
|
|
}
|
|
if (userOrganisation != null && !userOrganisation.OrganisationId.Equals(organisationId))
|
|
{
|
|
SaLogger.Warn(fn + " Inconsistent Organisation Id specified for User Organisation. Expected: " + userOrganisation.OrganisationId + ". Found: " + organisationId);
|
|
return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"]));
|
|
}
|
|
|
|
// Ensure that the user has the correct role to update Members for this organisation.
|
|
bool userCanManageMembers = AccountMgr.UserHasAnyOrganisationRole(SessionUserAccountId, organisationId, new List<Sax.OrganisationRole> { Sax.OrganisationRole.Admin, Sax.OrganisationRole.MemberAdmin });
|
|
if (!userCanManageMembers)
|
|
{
|
|
SaLogger.Warn(fn + " User doesn't have 'Member Admin' role for Organisation Id: " + organisationId);
|
|
return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"]));
|
|
}
|
|
|
|
// Validate the User Account for an existing member.
|
|
SaxUserAccount userAccount = null;
|
|
if (userOrganisation != null)
|
|
{
|
|
userAccount = AccountMgr.GetUserAccountById(userOrganisation.UserAccountId);
|
|
disposableItems.Add(userAccount);
|
|
bool userActive = userAccount.IsStatusActive || (userAccount.IsStatusAwaiting && userCanManageMembers);
|
|
if (userAccount == null || !userActive)
|
|
{
|
|
SaLogger.Warn(fn + " " + (userAccount == null ? "Invalid" : "Inactive") + " User Account for User Organisation. UserAccountId: '" + userOrganisation.UserAccountId + "'");
|
|
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.system"]));
|
|
}
|
|
if (userOrganisation != null && !userOrganisation.UserAccountId.Equals(userAccount.Id))
|
|
{
|
|
SaLogger.Warn(fn + " Inconsistent User Account Id specified for User Organisation. Expected: " + userOrganisation.UserAccountId + ". Found: " + userAccount.Id);
|
|
return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"]));
|
|
}
|
|
|
|
// Validate roles for current user.
|
|
if (userOrganisation.UserAccountId.Equals(SessionUserAccountId))
|
|
{
|
|
// User is modifying their own organisation member.
|
|
if (userOrganisation.CanManageOrganisation && !model.OrganisationRoleCodes.Any(item => item.Type == (int)Sax.OrganisationRole.Admin))
|
|
{
|
|
// User has removed their own 'Admin' role.
|
|
SaLogger.Warn(fn + " Unable to store member - user is attempting to remove their own 'Admin' role.");
|
|
return Json(responseData.SetPopupMessage(messageTitle, Localiser["organisation.members.msg.remove.own.admin.role"]));
|
|
}
|
|
if (!userOrganisation.CanManageMembers)
|
|
{
|
|
SaLogger.Warn(fn + " Unable to store member - user is attempting to remove their own 'Admin' and/or 'Member Admin' role.");
|
|
return Json(responseData.SetPopupMessage(messageTitle, Localiser["organisation.members.msg.remove.own.admin.role"]));
|
|
}
|
|
}
|
|
}
|
|
|
|
// New member - Lookup user by email and create new account if needed.
|
|
if (userAccount == null)
|
|
{
|
|
// Check if account exists for email.
|
|
userAccount = AccountMgr.GetUserAccountByEmailAddress(model.MemberEmailAddress);
|
|
if (userAccount != null)
|
|
{
|
|
// Existing User Account - get User Organisation if available.
|
|
userOrganisation = AccountMgr.GetUserOrganisationByUserOrganisation(userAccount.Id, organisation.Id);
|
|
disposableItems.Add(userOrganisation);
|
|
}
|
|
else
|
|
{
|
|
// If name hasn't been specified, return to page to complete details.
|
|
if (!StringUtil.ToBool(model.MemberNameSupplied))
|
|
{
|
|
responseData.SetControlError(nameof(model.MemberFirstName), Localiser["msg.validation.mandatory.value"]);
|
|
responseData.SetControlError(nameof(model.MemberLastName), Localiser["msg.validation.mandatory.value"]);
|
|
responseData.MemberNameRequired = true;
|
|
responseData.Success = true;
|
|
return Json(responseData);
|
|
}
|
|
|
|
// Create new User Account.
|
|
userAccount = AccountMgr.CreateUserAccount();
|
|
userAccount.EmailAddress = model.MemberEmailAddress;
|
|
userAccount.Status = SaStatus.Awaiting;
|
|
}
|
|
disposableItems.Add(userAccount);
|
|
}
|
|
|
|
// Store user account.
|
|
if (StringUtil.ToBool(model.MemberNameSupplied))
|
|
{
|
|
if (model.MemberTitle != null)
|
|
userAccount.Title = model.MemberTitle;
|
|
if (model.MemberFirstName != null)
|
|
userAccount.FirstName = model.MemberFirstName;
|
|
if (model.MemberLastName != null)
|
|
userAccount.LastName = model.MemberLastName;
|
|
}
|
|
if (!SysId.IsValid(userAccount.Id))
|
|
{
|
|
if (!AccountMgr.InsertUserAccount(userAccount, DbUpdateInfo))
|
|
{
|
|
SaLogger.Warn(fn + " Failed to insert User Account: " + AccountMgr.ErrorStateMessage);
|
|
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.insert.failed"]));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!AccountMgr.UpdateUserAccount(userAccount, DbUpdateInfo))
|
|
{
|
|
SaLogger.Warn(fn + " Failed to insert User Account: " + AccountMgr.ErrorStateMessage);
|
|
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.update.failed"]));
|
|
}
|
|
}
|
|
|
|
// Create new item if required.
|
|
if (userOrganisation == null)
|
|
{
|
|
userOrganisation = AccountMgr.CreateUserOrganisation(userAccount.Id, organisation.Id);
|
|
disposableItems.Add(userOrganisation);
|
|
}
|
|
|
|
// User Organisation Roles.
|
|
if (model.MemberRoleCodes != null)
|
|
userOrganisation.OrganisationRoles = SaxUserOrganisation.GetOrganisationRolesFromCodes(model.GetMemberRoleCodes());
|
|
// Ensure user has member role.
|
|
userOrganisation.AddOrganisationRole(Sax.OrganisationRole.Member);
|
|
|
|
// User organisation properties.
|
|
if (model.MemberAdminComment != null)
|
|
userOrganisation.AdminComment = model.MemberAdminComment;
|
|
userOrganisation.Status = SaStatus.Active;
|
|
|
|
// Store item.
|
|
if (!SysId.IsValid(userOrganisation.Id))
|
|
{
|
|
if (!AccountMgr.InsertUserOrganisation(userOrganisation, DbUpdateInfo))
|
|
{
|
|
SaLogger.Warn(fn + " Failed to insert User Organisation: " + AccountMgr.ErrorStateMessage);
|
|
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.insert.failed"]));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!AccountMgr.UpdateUserOrganisation(userOrganisation, DbUpdateInfo))
|
|
{
|
|
SaLogger.Warn(fn + " Failed to update User Organisation [Id:" + SysId.ToString(userOrganisation.Id) + "]: " + AccountMgr.ErrorStateMessage);
|
|
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.update.failed"]));
|
|
}
|
|
}
|
|
|
|
// Construct response.
|
|
PopulateMemberResponseItemData(model, userOrganisation, userAccount, organisation, responseData.Member);
|
|
}
|
|
|
|
// Complete response.
|
|
responseData.Success = true;
|
|
return Json(responseData);
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Activate Member ajax request.
|
|
//-------------------------------------------------------------------
|
|
private class AjaxResponse_ActivateMember : AjaxResponse_Data
|
|
{
|
|
// Construction.
|
|
public AjaxResponse_ActivateMember(SaxModel_Online model) : base(model) { }
|
|
// Member details.
|
|
public MemberItemData Member { get; } = new MemberItemData();
|
|
}
|
|
[HttpPost]
|
|
[Route("Organisation/Ajax/ActivateMember")]
|
|
public IActionResult Ajax_ActivateMember(Model_Organisation_Members model)
|
|
{
|
|
String fn = GetType().Name + ".Ajax_ActivateMember";
|
|
String messageTitle = Localiser["label.member"];
|
|
AjaxResponse_ActivateMember responseData = new AjaxResponse_ActivateMember(model);
|
|
|
|
// Validate session and referrer.
|
|
if (!ValidReferer) return Forbid();
|
|
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
|
|
RefreshModel(model);
|
|
|
|
// Record disposable items as they are created so all can be disposed when needed.
|
|
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
|
|
{
|
|
|
|
// Retrieve User Organisation [MemberId).
|
|
SysId userOrganisationId = model.ExtractSysId(model.MemberId);
|
|
SaxUserOrganisation userOrganisation = AccountMgr.GetUserOrganisationById(userOrganisationId);
|
|
disposableItems.Add(userOrganisation);
|
|
if (userOrganisation == null || !userOrganisation.IsStatusActive)
|
|
{
|
|
SaLogger.Warn(fn + " User Organisation not found or not active. Id: " + SysId.ToString(userOrganisationId));
|
|
return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"]));
|
|
}
|
|
|
|
// Retrieve the Organisation.
|
|
SaxOrganisation organisation = AccountMgr.GetOrganisationById(userOrganisation.OrganisationId);
|
|
disposableItems.Add(organisation);
|
|
if (organisation == null || !organisation.IsStatusActive)
|
|
{
|
|
SaLogger.Warn(fn + " Organisation not found or not active. Id: " + SysId.ToString(userOrganisation.OrganisationId));
|
|
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.system"]));
|
|
}
|
|
|
|
// Ensure that the user has the correct role to update Members for this organisation.
|
|
bool userCanManageMembers = AccountMgr.UserHasAnyOrganisationRole(SessionUserAccountId, organisation.Id, new List<Sax.OrganisationRole> { Sax.OrganisationRole.Admin, Sax.OrganisationRole.MemberAdmin });
|
|
if (!userCanManageMembers)
|
|
{
|
|
SaLogger.Warn(fn + " User doesn't have 'Member Admin' role for Organisation Id: " + organisation.Id);
|
|
return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"]));
|
|
}
|
|
|
|
// Retrieve User Account.
|
|
SaxUserAccount userAccount = null;
|
|
userAccount = AccountMgr.GetUserAccountById(userOrganisation.UserAccountId);
|
|
disposableItems.Add(userAccount);
|
|
if (userAccount == null)
|
|
{
|
|
SaLogger.Warn(fn + " Failed to retrieve User Account. Id: " + userOrganisation.UserAccountId);
|
|
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.system"]));
|
|
}
|
|
if (!userAccount.IsStatusAwaiting)
|
|
{
|
|
SaLogger.Warn(fn + " User Account is not awaiting. Id: " + userOrganisation.UserAccountId + ". Status: " + userAccount.StatusDesc);
|
|
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.system"]));
|
|
}
|
|
|
|
// Update user's status.
|
|
userAccount.Status = SaStatus.Active;
|
|
if (AccountMgr.UpdateUserAccount(userAccount, DbUpdateInfo))
|
|
{
|
|
// Send activation email.
|
|
SaEmailData emailData = new SaEmailData();
|
|
emailData.Config = EmailConfig;
|
|
emailData.AddressesTo.Add(userAccount.EmailAddress);
|
|
emailData.Subject = Localiser["account.activate.email.subject"];
|
|
PopulateAccountActivatedEmailBody(emailData, userAccount);
|
|
if (EmailConsole.SendEmail(emailData))
|
|
SaLogger.Info(fn + " User Activation email sent to " + userAccount.EmailAddress, true);
|
|
else SaLogger.Warn(fn + " Failed to send User Activation email to " + userAccount.EmailAddress);
|
|
// Treat the activation as successful regardless of whether the email was sent.
|
|
responseData.SetNotifyMessage(messageTitle, Localiser["account.activate.notify"]);
|
|
}
|
|
else
|
|
{
|
|
SaLogger.Warn(fn + " Failed to insert User Account: " + AccountMgr.ErrorStateMessage);
|
|
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.update.failed"]));
|
|
}
|
|
|
|
// Construct response.
|
|
PopulateMemberResponseItemData(model, userOrganisation, userAccount, organisation, responseData.Member);
|
|
}
|
|
|
|
// Complete response.
|
|
responseData.Success = true;
|
|
return Json(responseData);
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
private void PopulateAccountActivatedEmailBody(SaEmailData emailData, SaxUserAccount userAccount)
|
|
{
|
|
// Will need to handle culture (language/country) as and when introduced.
|
|
emailData.MessageBody = SaxResourceHelper.GetResourceText(SaxResourceHelper.TextResource.Email_Security_AccountActivated_Html);
|
|
emailData.HtmlBody = true;
|
|
|
|
// Replace dynamic tags.
|
|
EmailConsole.ReplaceDynamicEmailBodyTags(emailData, null, userAccount);
|
|
|
|
// Replace dynamic tag for link back to change password page.
|
|
SaEmailConsole.ReplaceDynamicEmailTag(emailData, "[#Link.RequestPassword]", SecurityController.GenerateRequestPasswordLink(this, userAccount.Id, userAccount.EmailAddress, ""));
|
|
SaEmailConsole.ReplaceDynamicEmailTag(emailData, "[#Link.SignIn]", GetRedirectLocation(RedirectLocation.WebAppBase) + "Security/SignIn");
|
|
SaEmailConsole.ReplaceDynamicEmailTag(emailData, "[#Link.ExpiresMinutes]", SecurityController.GetRequestPasswordEmailLinkExpiresMinutes(this).ToString());
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Remove Member ajax request.
|
|
//-------------------------------------------------------------------
|
|
private class AjaxResponse_RemoveMember : AjaxResponse_Data
|
|
{
|
|
// Construction.
|
|
public AjaxResponse_RemoveMember(SaxModel_Online model) : base(model) { }
|
|
// Member details.
|
|
public MemberItemData Member { get; } = new MemberItemData();
|
|
}
|
|
[HttpPost]
|
|
[Route("Organisation/Ajax/RemoveMember")]
|
|
public IActionResult Ajax_RemoveMember(Model_Organisation_Members model)
|
|
{
|
|
String fn = GetType().Name + ".Ajax_RemoveMember";
|
|
String messageTitle = Localiser["label.member"];
|
|
AjaxResponse_RemoveMember responseData = new AjaxResponse_RemoveMember(model);
|
|
|
|
// Validate session and referrer.
|
|
if (!ValidReferer) return Forbid();
|
|
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
|
|
RefreshModel(model);
|
|
|
|
// Record disposable items as they are created so all can be disposed when needed.
|
|
using (DisposableCollection<IDisposable> disposableItems = new DisposableCollection<IDisposable>())
|
|
{
|
|
|
|
// Retrieve User Organisation [MemberId).
|
|
SysId userOrganisationId = model.ExtractSysId(model.MemberId);
|
|
SaxUserOrganisation userOrganisation = AccountMgr.GetUserOrganisationById(userOrganisationId);
|
|
disposableItems.Add(userOrganisation);
|
|
if (userOrganisation == null || !userOrganisation.IsStatusActive)
|
|
{
|
|
SaLogger.Warn(fn + " User Organisation not found or not active. Id: " + SysId.ToString(userOrganisationId));
|
|
return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"]));
|
|
}
|
|
|
|
// Retrieve the Organisation.
|
|
SaxOrganisation organisation = AccountMgr.GetOrganisationById(userOrganisation.OrganisationId);
|
|
disposableItems.Add(organisation);
|
|
if (organisation == null || !organisation.IsStatusActive)
|
|
{
|
|
SaLogger.Warn(fn + " Organisation not found or not active. Id: " + SysId.ToString(userOrganisation.OrganisationId));
|
|
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.system"]));
|
|
}
|
|
|
|
// Ensure that the user has the correct role to update Members for this organisation.
|
|
bool userCanManageMembers = AccountMgr.UserHasAnyOrganisationRole(SessionUserAccountId, organisation.Id, new List<Sax.OrganisationRole> { Sax.OrganisationRole.Admin, Sax.OrganisationRole.MemberAdmin });
|
|
if (!userCanManageMembers)
|
|
{
|
|
SaLogger.Warn(fn + " User doesn't have 'Member Admin' role for Organisation Id: " + organisation.Id);
|
|
return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"]));
|
|
}
|
|
|
|
// Retrieve User Account.
|
|
SaxUserAccount userAccount = null;
|
|
userAccount = AccountMgr.GetUserAccountById(userOrganisation.UserAccountId);
|
|
disposableItems.Add(userAccount);
|
|
if (userAccount == null)
|
|
{
|
|
SaLogger.Warn(fn + " Failed to retrieve User Account. Id: " + userOrganisation.UserAccountId);
|
|
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.system"]));
|
|
}
|
|
if (!userAccount.IsStatusAwaiting && !userAccount.IsStatusActive)
|
|
{
|
|
SaLogger.Warn(fn + " User Account is not awaiting or active. Id: " + userOrganisation.UserAccountId);
|
|
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.system"]));
|
|
}
|
|
|
|
// Remove User Organisation roles.
|
|
userOrganisation.ClearOrganisationRoles();
|
|
if (AccountMgr.UpdateUserOrganisation(userOrganisation, DbUpdateInfo))
|
|
{
|
|
responseData.SetNotifyMessage(messageTitle, Localiser["msg.item.removed"]);
|
|
}
|
|
else
|
|
{
|
|
SaLogger.Warn(fn + " Failed to update User Organisation: " + AccountMgr.ErrorStateMessage);
|
|
return Json(responseData.SetPopupMessage(messageTitle, Localiser["msg.error.item.update.failed"]));
|
|
}
|
|
|
|
// Construct response.
|
|
PopulateMemberResponseItemData(model, userOrganisation, userAccount, organisation, responseData.Member);
|
|
}
|
|
|
|
// Complete response.
|
|
responseData.Success = true;
|
|
return Json(responseData);
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
}
|
|
//=======================================================================
|
|
}
|