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.Email; using Sarand.Net.Standard.Base.Utils; using Saxonite.Net.Core.Web.Admin.Models; using Saxonite.Net.Standard.Base.App; using Saxonite.Net.Standard.Base.Bus; using Saxonite.Net.Standard.Base.Helpers; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Saxonite.Net.Core.Web.Admin.Controllers { //======================================================================= // Controller: Organisation - Activation - Ajax //======================================================================= partial class OrganisationController { //------------------------------------------------------------------- // Get Organisation Activation Info ajax request. //------------------------------------------------------------------- private class AjaxResponse_OrganisationActivationInfo : AjaxResponse_Data { // Construction. public AjaxResponse_OrganisationActivationInfo(SaxModel_Admin model) : base(model) { } // Organisation details. public OrganisationItemData organisation { get; } = new OrganisationItemData(); // Member awaiting activation. public List Members { get; } = new List(); } //------------------------------------------------------------------- [HttpPost] [Route("Organisation/Ajax/GetActivationInfo")] public IActionResult Ajax_GetActivationInfo(Model_Organisation model) { String fn = GetType().Name + ".Ajax_GetActivationInfo"; String messageTitle = Localiser["organisation.caption.organisation"]; AjaxResponse_OrganisationActivationInfo responseData = new AjaxResponse_OrganisationActivationInfo(model); // Validate session. if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData); // Record disposable items as they are created so all can be disposed when needed. using (DisposableCollection disposableItems = new DisposableCollection()) { if (StringUtil.IsEmpty(AppConfig.URL.Online.Root)) return Json(responseData.SetMessage(messageTitle, "Root online URL not specified.")); // Identify item. SysId organisationId = model.OrganisationId.ToSysId(); if (!SysId.IsValid(organisationId)) { SaLogger.Warn(fn + " Request to get activation info without valid organisation Id."); return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.specified"])); } // Retrieve item. SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationId); disposableItems.Add(organisation); if (organisation == null) { SaLogger.Warn(fn + " Failed to retrieve organisation for Id: '" + model.OrganisationId + "'"); return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"])); } // Organisation address. SaxItemAddress address = ItemDataMgr.GetFirstItemAddressByItemAndType(organisation.GetItemId()); disposableItems.Add(address); // Construct response. PopulateOrganisationResponseItemData(organisation, address, responseData.organisation); GetOrganisationMembers(model, organisationId, responseData.Members); } // Complete response. responseData.Success = true; return Json(responseData); } //------------------------------------------------------------------- private class OrganisationMemberData : SacsDisposable { protected override void Dispose(bool disposing) { if (disposing) { if (UserOrganisation != null) UserOrganisation.Dispose(); UserOrganisation = null; if (UserAccount != null) UserAccount.Dispose(); UserAccount = null; } } public SaxUserOrganisation UserOrganisation { get; set; } public SaxUserAccount UserAccount { get; set; } } //------------------------------------------------------------------- // Activate Organisation ajax request. //------------------------------------------------------------------- [HttpPost] [Route("Organisation/Ajax/Activate")] [Route("Organisation/Ajax/ActivateItem")] public IActionResult Ajax_Activate(Model_OrganisationActivation model) { String fn = GetType().Name + ".Ajax_Activate"; String messageTitle = Localiser["organisation.caption.organisation"]; AjaxResponse_Organisation responseData = new AjaxResponse_Organisation(model); // Validate session. if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData); // Record disposable items as they are created so all can be disposed when needed. using (DisposableCollection disposableItems = new DisposableCollection()) { // Retrieve existing item. SysId organisationId = model.OrganisationId.ToSysId(); SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationId); disposableItems.Add(organisation); if (organisation == null) { SaLogger.Warn(fn + " Failed to retrieve organisation for. : '" + model.OrganisationId + "'"); return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"])); } // Organisation address. SaxItemAddress address = ItemDataMgr.GetFirstItemAddressByItemAndType(organisation.GetItemId()); disposableItems.Add(address); // Retrieve users. List organisationMembersToActivate = new List(); foreach (String userOrganisationId in model.OrganisationMemberIds) { if (userOrganisationId == null) continue; OrganisationMemberData organisationMember = new OrganisationMemberData(); disposableItems.Add(organisationMember); organisationMember.UserOrganisation = AccountMgr.GetUserOrganisationById(userOrganisationId.ToSysId()); organisationMember.UserAccount = AccountMgr.GetUserAccountById(organisationMember.UserOrganisation?.UserAccountId ?? null); if (organisationMember.UserOrganisation == null) { SaLogger.Warn(fn + " Failed to retrieve user organisation. Id: '" + userOrganisationId + "'"); return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"])); } if (organisationMember.UserAccount == null) { SaLogger.Warn(fn + " Failed to retrieve user account . Id: '" + organisationMember.UserOrganisation.UserAccountId + "'"); return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"])); } if (organisationMember.UserAccount.Status != SaStatus.Awaiting) { SaLogger.Warn(fn + " User organisation is not awaiting. Id: '" + userOrganisationId + "'. Status: " + organisationMember.UserAccount.StatusDesc); return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"])); } // Check if this user is to be activated. bool activateUser = false; String controlName_Activate = "MemberActivate_" + userOrganisationId; foreach (String key in Request.Form.Keys) { if (key.EqualsCI(controlName_Activate)) { activateUser = true; break; } } if (activateUser) organisationMembersToActivate.Add(organisationMember); } if (organisationMembersToActivate.IsEmpty()) { // No users to be activated. // Check that organisation already has active members. if (AccountMgr.GetOrganisationMemberCount(organisation.Id) < 1) { SaLogger.Warn(fn + " Unable to activate Organisation - no active members. Id: '" + SysId.ToString(organisationId)); return Json(responseData.SetMessage(messageTitle, Localiser["organisation.activate.msg.no.active.members"])); } } // Activate organisation. organisation.Status = Sarand.Net.Standard.Base.Core.SaStatus.Active; if (!AccountMgr.UpdateOrganisation(organisation, DbUpdateInfo)) { SaLogger.Warn(fn + " Failed to update organisation [Id:" + SysId.ToString(organisation.Id) + "]: " + AccountMgr.ErrorStateMessage); return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + AccountMgr.ErrorStateMessage)); } // Activate awaiting members and send welcome email to each. foreach (OrganisationMemberData organisationMember in organisationMembersToActivate) { if (organisationMember.UserOrganisation == null) continue; if (AccountMgr.UpdateUserOrganisation(organisationMember.UserOrganisation, DbUpdateInfo)) { if (organisationMember.UserAccount == null) continue; organisationMember.UserAccount.Status = SaStatus.Active; if (AccountMgr.UpdateUserAccount(organisationMember.UserAccount, DbUpdateInfo)) { // Send activation email. if (SendAccountActivatedEmail(this, organisationMember.UserAccount)) SaLogger.Info(fn + " User Activation email sent to " + organisationMember.UserAccount.EmailAddress, true); else SaLogger.Warn(fn + " Failed to send User Activation email to " + organisationMember.UserAccount.EmailAddress); } else SaLogger.Warn(fn + " Failed to update user account [Id:" + SysId.ToString(organisationMember.UserAccount.Id) + "]: " + AccountMgr.ErrorStateMessage); } else SaLogger.Warn(fn + " Failed to update user organisation [Id:" + SysId.ToString(organisationMember.UserOrganisation.Id) + "]: " + AccountMgr.ErrorStateMessage); } // Construct response. PopulateOrganisationResponseItemData(organisation, address, responseData.organisation); } // Complete response. responseData.Success = true; return Json(responseData); } //------------------------------------------------------------------- /// /// Generate the User Activation email. /// NB URL must be to the Online application. /// NB Encryption must be done with the Online logic. /// public static bool SendAccountActivatedEmail(SaxController_Admin controller, SaxUserAccount userAccount) { SaEmailData emailData = new SaEmailData(); emailData.Config = controller.EmailConfig; emailData.AddressesTo.Add(userAccount.EmailAddress); emailData.Subject = controller.Localiser["user.activate.email.subject"]; PopulateAccountActivatedEmailBody(controller, emailData, userAccount); return controller.EmailConsole.SendEmail(emailData); } private static void PopulateAccountActivatedEmailBody(SaxController_Admin controller, SaEmailData emailData, SaxUserAccount userAccount) { // Will need to handle culture (language/country) as and when introduced. emailData.MessageBody = SaxResourceHelper.GetTextResource(SaxResourceHelper.TextResource.Email_Security_AccountActivated_Html); emailData.HtmlBody = true; // Replace dynamic tags. controller.EmailConsole.ReplaceDynamicEmailBodyTags(emailData, null, userAccount); // Replace dynamic tag for link back to change password page. SaEmailConsole.ReplaceDynamicEmailTag(emailData, "[#Link.RequestPassword]", SecurityController.GenerateRequestPasswordLink(controller, userAccount.Id, userAccount.EmailAddress, "", true)); SaEmailConsole.ReplaceDynamicEmailTag(emailData, "[#Link.SignIn]", controller.AppConfig.URL.Online.Root + "Security/SignIn"); SaEmailConsole.ReplaceDynamicEmailTag(emailData, "[#Link.ExpiresMinutes]", SecurityController.GetRequestPasswordEmailLinkExpiresMinutes(controller).ToString()); } //------------------------------------------------------------------- } }