diff --git a/SaxoniteCoreWeb/Base/Controllers/SaxController_Base.cs b/SaxoniteCoreWeb/Base/Controllers/SaxController_Base.cs index 56f573d..4e966e0 100644 --- a/SaxoniteCoreWeb/Base/Controllers/SaxController_Base.cs +++ b/SaxoniteCoreWeb/Base/Controllers/SaxController_Base.cs @@ -486,6 +486,14 @@ namespace Saxonite.Net.Core.Web.Base.Controllers public String UpdatedOnFmt { get; set; } } /// + /// Item data with label and value. + /// + public class ItemLabelValue + { + public String Label { get; set; } + public String Value { get; set; } + } + /// /// Item data with just Id and name. /// - Id is String to allow for obfuscated values. /// diff --git a/SaxoniteCoreWeb/Base/Models/SaxModel_Base.cs b/SaxoniteCoreWeb/Base/Models/SaxModel_Base.cs index fc76d2a..4ed6a17 100644 --- a/SaxoniteCoreWeb/Base/Models/SaxModel_Base.cs +++ b/SaxoniteCoreWeb/Base/Models/SaxModel_Base.cs @@ -392,6 +392,21 @@ namespace Saxonite.Net.Core.Web.Base.Models } } + /// + /// List of Organisation Categories for Admin dropdown. + /// Includes complete set of categories. + /// + public IEnumerable AdminOrganisationCategoriesForSelect + { + get + { + List values = new List(); + foreach (TypeCodeDesc organisationCategory in OrganisationCategoryCodes) + values.Add(new SelectListItem(Localiser.GetOrganisationCategoryCaption((Sax.OrganisationCategory)organisationCategory.Type), organisationCategory.Code)); + return values; + } + } + //------------------------------------------------------------------- // Messages. //------------------------------------------------------------------- diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/HomeController-Ajax-ApplicationStats.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/HomeController-Ajax-ApplicationStats.cs new file mode 100644 index 0000000..f49c3c6 --- /dev/null +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/HomeController-Ajax-ApplicationStats.cs @@ -0,0 +1,184 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Routing; +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 Sarand.Net.Standard.Dbas.Data; +using Saxonite.Net.Core.Web.Admin.Models; +using Saxonite.Net.Standard.Base.App; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Saxonite.Net.Core.Web.Admin.Controllers +{ + //======================================================================= + // Home Controller - Ajax - Application Stats + //======================================================================= + partial class HomeController + { + //------------------------------------------------------------------- + // Application Stats data. + private class ApplicationStatsData + { + public class UserStatsData + { + public String Caption { get; set; } + public AjaxResponse_Data.ItemLabelValue Awaiting { get; set; } + public String AwaitingLink { get; set; } + public List OnlineStats { get; set; } + public String OnlineLink { get; set; } + } + public UserStatsData User { get; } = new UserStatsData(); + public class OrganisationStatsData + { + public String Caption { get; set; } + public AjaxResponse_Data.ItemLabelValue Awaiting { get; set; } + public String AwaitingLink { get; set; } + } + public OrganisationStatsData Organisation { get; } = new OrganisationStatsData(); + } + + //------------------------------------------------------------------- + // Get Application Stats ajax request. + //------------------------------------------------------------------- + private class AjaxResponse_ApplicationStats : AjaxResponse_Data + { + // Construction. + public AjaxResponse_ApplicationStats(SaxModel_Admin model) : base(model) { } + // Table list. + public ApplicationStatsData Stats { get; } = new ApplicationStatsData(); + } + //------------------------------------------------------------------- + [HttpPost] + [Route("Home/Ajax/GetApplicationStats")] + public IActionResult Ajax_GetApplicationStats(Model_Sport model) + { + String fn = GetType().Name + ".Ajax_GetApplicationStats"; + String messageTitle = Localiser["home.caption.application"]; + AjaxResponse_ApplicationStats responseData = new AjaxResponse_ApplicationStats(model); + + // Validate session. + if (!ValidUserSession) + return GetAjaxResult_InvalidSession(responseData); + + // Captions. + responseData.Stats.User.Caption = Localiser["user.caption.users"]; + responseData.Stats.Organisation.Caption = Localiser["organisation.caption.organisations"]; + + // Core Database connection. + ISacsDatabase db = SaxGlobals.App.GetCoreDatabase(SessionContext); + + // Query to count items awaiting activation. + Dictionary tableRowCounts = new Dictionary(StringComparer.OrdinalIgnoreCase); + List query = new List() + { + // User accounts. + "SELECT", + "ItemType = '" + DbQuery.FormatForCondition(nameof(Sax.DataItemType.UserAccount)) + "',", + "ItemCount = Count(*)", + "FROM UserAccount ua", + "WHERE ua.Status = 'AWA'", + // Organisations. + "UNION SELECT", + "ItemType = '" + DbQuery.FormatForCondition(nameof(Sax.DataItemType.Organisation)) + "',", + "ItemCount = Count(*)", + "FROM Organisation org", + "WHERE org.Status = 'AWA'", + }; + using (DbResultSet dbResultSet = DbResultSet_DataReader.ExecuteQuery(db, query.Concat("\n"))) + { + while (dbResultSet != null && dbResultSet.NextRecord()) + { + NamedValue.Map values = dbResultSet.GetRecordValues(); + Sax.DataItemType itemType = EnumUtil.ParseEnum(values.GetValue("ItemType").ToString(), Sax.DataItemType.Unknown); + int itemCount = (int)values.GetValue("ItemCount").AsLong(); + switch (itemType) + { + case Sax.DataItemType.UserAccount: + responseData.Stats.User.Awaiting = new AjaxResponse_Data.ItemLabelValue() { Label = Localiser["label.awaiting.activation"], Value = itemCount.ToString() }; + responseData.Stats.User.AwaitingLink = Url.Action("", "User", new RouteValueDictionary() { { "sta", "Await" } }).TrimStart('/'); + break; + case Sax.DataItemType.Organisation: + responseData.Stats.Organisation.Awaiting = new AjaxResponse_Data.ItemLabelValue() { Label = Localiser["label.awaiting.activation"], Value = itemCount.ToString() }; + responseData.Stats.Organisation.AwaitingLink = Url.Action("", "Organisation", new RouteValueDictionary() { { "sta", "Await" } }).TrimStart('/'); + break; + } + } + } + + // Query to count users online. + DateTime today = DateTime.Today; + DateTime now = DateTime.Now; + const String online5Mins = "Online5Mins"; + const String online15Mins = "Online15Mins"; + const String online30Mins = "Online30Mins"; + const String online60Mins = "Online60Mins"; + const String onlineToday = "OnlineToday"; + const String onlineWeek = "OnlineWeek"; + const String onlineMonth = "OnlineMonth"; + query = new List() + { + // Users Online last 5 mins. + "Select '" + online5Mins + "' ItemType, (Select Count(*) From UserOnline Where OnlineAt>=" + DbQuery.FormatDateTimeForCondition(now.AddMinutes(-5)) + ") ItemCount ", + "Union Select '" + online15Mins + "' ItemType, (Select Count(*) From UserOnline Where OnlineAt>=" + DbQuery.FormatDateTimeForCondition(now.AddMinutes(-15)) + ") ItemCount ", + "Union Select '" + online30Mins + "' ItemType, (Select Count(*) From UserOnline Where OnlineAt>=" + DbQuery.FormatDateTimeForCondition(now.AddMinutes(-30)) + ") ItemCount ", + "Union Select '" + online60Mins + "' ItemType, (Select Count(*) From UserOnline Where OnlineAt>=" + DbQuery.FormatDateTimeForCondition(now.AddMinutes(-60)) + ") ItemCount ", + "Union Select '" + onlineToday + "' ItemType, (Select Count(*) From UserOnline Where OnlineAt>=" + DbQuery.FormatDateForCondition(today) + ") ItemCount ", + "Union Select '" + onlineWeek + "' ItemType, (Select Count(*) From UserOnline Where OnlineAt>=" + DbQuery.FormatDateForCondition(DateUtil.StartOfWorkingWeek(today)) + ") ItemCount ", + "Union Select '" + onlineMonth + "' ItemType, (Select Count(*) From UserOnline Where OnlineAt>=" + DbQuery.FormatDateForCondition(DateUtil.StartOfMonth(today)) + ") ItemCount ", + }; + using (DbResultSet dbResultSet = DbResultSet_DataReader.ExecuteQuery(db, query.Concat("\n"))) + { + while (dbResultSet != null && dbResultSet.NextRecord()) + { + if (responseData.Stats.User.OnlineStats == null) + responseData.Stats.User.OnlineStats = new List(); + NamedValue.Map values = dbResultSet.GetRecordValues(); + String itemType = values.GetValue("ItemType").ToString(); + int itemCount = (int)values.GetValue("ItemCount").AsLong(); + switch (itemType) + { + case online5Mins: + if (itemCount > 0) + responseData.Stats.User.OnlineStats.Add(new AjaxResponse_Data.ItemLabelValue() { Label = Localiser["label.online.last5Mins"], Value = itemCount.ToString() }); + break; + case online15Mins: + if (itemCount > 0) + responseData.Stats.User.OnlineStats.Add(new AjaxResponse_Data.ItemLabelValue() { Label = Localiser["label.online.last15Mins"], Value = itemCount.ToString() }); + break; + case online30Mins: + if (itemCount > 0) + responseData.Stats.User.OnlineStats.Add(new AjaxResponse_Data.ItemLabelValue() { Label = Localiser["label.online.last30Mins"], Value = itemCount.ToString() }); + break; + case online60Mins: + if (itemCount > 0) + responseData.Stats.User.OnlineStats.Add(new AjaxResponse_Data.ItemLabelValue() { Label = Localiser["label.online.last60Mins"], Value = itemCount.ToString() }); + break; + case onlineToday: + if (itemCount > 0) + responseData.Stats.User.OnlineStats.Add(new AjaxResponse_Data.ItemLabelValue() { Label = Localiser["label.online.today"], Value = itemCount.ToString() }); + break; + case onlineWeek: + if (itemCount > 0) + responseData.Stats.User.OnlineStats.Add(new AjaxResponse_Data.ItemLabelValue() { Label = Localiser["label.online.thisWeek"], Value = itemCount.ToString() }); + break; + case onlineMonth: + if (itemCount > 0) + responseData.Stats.User.OnlineStats.Add(new AjaxResponse_Data.ItemLabelValue() { Label = Localiser["label.online.thisMonth"], Value = itemCount.ToString() }); + break; + } + responseData.Stats.User.OnlineLink = Url.Action("", "UsersOnline", new RouteValueDictionary() { }).TrimStart('/'); + } + } + + // Complete response. + responseData.Success = true; + return Json(responseData); + } + + //------------------------------------------------------------------- + } + //======================================================================= +} diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/HomeController-Ajax-AwaitingActivation.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/HomeController-Ajax-AwaitingActivation.cs deleted file mode 100644 index 1e0ee03..0000000 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/HomeController-Ajax-AwaitingActivation.cs +++ /dev/null @@ -1,116 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Routing; -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 Sarand.Net.Standard.Dbas.Data; -using Saxonite.Net.Core.Web.Admin.Models; -using Saxonite.Net.Standard.Base.App; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Saxonite.Net.Core.Web.Admin.Controllers -{ - //======================================================================= - // Home - Ajax - Awaiting Activation controller - //======================================================================= - partial class HomeController - { - //------------------------------------------------------------------- - // Items Awaiting Activation data. - private class ActivationItemData - { - public String ItemType { get; set; } - public String ItemTypeDesc { get; set; } - public String ItemTypeDescPlural { get; set; } - public long ItemCount { get; set; } - public String ViewLink { get; set; } - } - - //------------------------------------------------------------------- - // Get Items Awaiting Activation ajax request. - //------------------------------------------------------------------- - private class AjaxResponse_ItemsAwaitingActivation : AjaxResponse_Data - { - // Construction. - public AjaxResponse_ItemsAwaitingActivation(SaxModel_Admin model) : base(model) { } - // Table list. - public List Items { get; } = new List(); - } - //------------------------------------------------------------------- - [HttpPost] - [Route("Home/Ajax/GetItemsAwaitingActivation")] - public IActionResult Ajax_GetItemsAwaitingActivation(Model_Sport model) - { - String fn = GetType().Name + ".Ajax_GetItemsAwaitingActivation"; - String messageTitle = Localiser["home.caption.application"]; - AjaxResponse_ItemsAwaitingActivation responseData = new AjaxResponse_ItemsAwaitingActivation(model); - - // Validate session. - if (!ValidUserSession) - return GetAjaxResult_InvalidSession(responseData); - - // Core Database connection. - ISacsDatabase db = SaxGlobals.App.GetCoreDatabase(SessionContext); - - // Query to count items awaiting activation. - Dictionary tableRowCounts = new Dictionary(StringComparer.OrdinalIgnoreCase); - List query = new List() - { - // User accounts. - "SELECT", - "ItemType = '" + DbQuery.FormatForCondition(nameof(Sax.DataItemType.UserAccount)) + "',", - "ItemCount = Count(*)", - "FROM UserAccount ua", - "WHERE ua.Status = 'AWA'", - // Organisations. - "UNION SELECT", - "ItemType = '" + DbQuery.FormatForCondition(nameof(Sax.DataItemType.Organisation)) + "',", - "ItemCount = Count(*)", - "FROM Organisation org", - "WHERE org.Status = 'AWA'", - }; - using (DbResultSet dbResultSet = DbResultSet_DataReader.ExecuteQuery(db, query.Concat("\n"))) - { - while (dbResultSet != null && dbResultSet.NextRecord()) - { - Sax.DataItemType itemType = EnumUtil.ParseEnum(dbResultSet.GetRecordValues().GetValue("ItemType").ToString(), Sax.DataItemType.Unknown); - String itemTypeDesc = nameof(itemType); - String itemTypeDescPlural = itemTypeDesc; - String viewLink = ""; - switch (itemType) - { - case Sax.DataItemType.UserAccount: - itemTypeDesc = "User Account"; - itemTypeDescPlural = "User Accounts"; - viewLink = Url.Action("", "User", new RouteValueDictionary() { { "sta", "Await" } }).TrimStart('/'); - break; - case Sax.DataItemType.Organisation: - itemTypeDesc = "Organisation"; - itemTypeDescPlural = "Organisations"; - viewLink = Url.Action("", "Organisation", new RouteValueDictionary() { { "sta", "Await" } }).TrimStart('/'); - break; - } - ActivationItemData activationItem = new ActivationItemData() - { - ItemType = nameof(itemType), - ItemTypeDesc = itemTypeDesc, - ItemTypeDescPlural = itemTypeDescPlural, - ItemCount = dbResultSet.GetRecordValues().GetValue("ItemCount").AsLong(), - ViewLink = viewLink, - }; - responseData.Items.Add(activationItem); - } - } - - // Complete response. - responseData.Success = true; - return Json(responseData); - } - - //------------------------------------------------------------------- - } - //======================================================================= -} diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Activation-Ajax.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Activation-Ajax.cs index 9364a14..56a4ac2 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Activation-Ajax.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Activation-Ajax.cs @@ -45,28 +45,34 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData); - 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)) + // Record disposable items as they are created so all can be disposed when needed. + using (DisposableCollection disposableItems = new DisposableCollection()) { - 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); - 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"])); - } + if (StringUtil.IsEmpty(AppConfig.URL.Online.Root)) + return Json(responseData.SetMessage(messageTitle, "Root online URL not specified.")); - // Construct response. - PopulateOrganisationResponseData(organisation, responseData.organisation); - GetOrganisationMembers(model, organisationId, responseData.Members); + // 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"])); + } + + // Construct response. + PopulateOrganisationResponseData(organisation, responseData.organisation); + GetOrganisationMembers(model, organisationId, responseData.Members); + } // Complete response. responseData.Success = true; @@ -105,10 +111,14 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData); - // Retrieve existing item. - SysId organisationId = model.OrganisationId.ToSysId(); - using (SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationId)) + // 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 && SysId.IsValid(organisationId)) { SaLogger.Warn(fn + " Failed to retrieve organisation for. : '" + model.OrganisationId + "'"); @@ -116,93 +126,87 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers } // Retrieve users. - using (DisposableCollection organisationMembersToActivate = new DisposableCollection()) + List organisationMembersToActivate = new List(); + foreach (String userOrganisationId in model.OrganisationMemberIds) { - 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) { - if (userOrganisationId == null) continue; - OrganisationMemberData organisationMember = new OrganisationMemberData(); - 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 + "'"); - organisationMember.Dispose(); - 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 + "'"); - organisationMember.Dispose(); - 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); - organisationMember.Dispose(); - 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); - else organisationMember.Dispose(); - organisationMember = null; + SaLogger.Warn(fn + " Failed to retrieve user organisation. Id: '" + userOrganisationId + "'"); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"])); } - - if (organisationMembersToActivate.IsEmpty()) + if (organisationMember.UserAccount == null) { - // No users to be activated. - // Check that organisation already has active members. - if (AccountMgr.GetOrganisationMemberCount(organisation.Id) < 1) + 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)) { - 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"])); + activateUser = true; + break; } } + if (activateUser) + organisationMembersToActivate.Add(organisationMember); + } - // Activate organisation. - organisation.Status = Sarand.Net.Standard.Base.Core.SaStatus.Active; - if (!AccountMgr.UpdateOrganisation(organisation, DbUpdateInfo)) + if (organisationMembersToActivate.IsEmpty()) + { + // No users to be activated. + // Check that organisation already has active members. + if (AccountMgr.GetOrganisationMemberCount(organisation.Id) < 1) { - 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)); + 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 awaiting members and send welcome email to each. - foreach (OrganisationMemberData organisationMember in organisationMembersToActivate) + // 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.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)) { - 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); + // 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 organisation [Id:" + SysId.ToString(organisationMember.UserOrganisation.Id) + "]: " + AccountMgr.ErrorStateMessage); + SaLogger.Warn(fn + " Failed to update user account [Id:" + SysId.ToString(organisationMember.UserAccount.Id) + "]: " + AccountMgr.ErrorStateMessage); } - - // Construct response. - PopulateOrganisationResponseData(organisation, responseData.organisation); + else + SaLogger.Warn(fn + " Failed to update user organisation [Id:" + SysId.ToString(organisationMember.UserOrganisation.Id) + "]: " + AccountMgr.ErrorStateMessage); } + + // Construct response. + PopulateOrganisationResponseData(organisation, responseData.organisation); } // Complete response. @@ -216,7 +220,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers /// 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) + public static bool SendAccountActivatedEmail(SaxController_Admin controller, SaxUserAccount userAccount) { SaEmailData emailData = new SaEmailData(); emailData.Config = controller.EmailConfig; diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Members-Ajax.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Members-Ajax.cs index 9df804c..fe41dd2 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Members-Ajax.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Members-Ajax.cs @@ -119,6 +119,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers SaxUserOrganisation.Params prmUserOrganisation = new SaxUserOrganisation.Params(); prmUserOrganisation.OrganisationIdsIncluded.Add(organisationId); prmUserOrganisation.OrganisationMembersOnly = true; + prmUserOrganisation.UserAccountStatusCat = SaxUserAccount.StatusCat.AwaitingOnly; prmUserOrganisation.StatusCat = SaxUserOrganisation.StatusCat.NotDeleted; model.Paging.ItemTotal = (ulong)AccountMgr.GetUserOrganisationCount(prmUserOrganisation); model.PopulatePagingTo(prmUserOrganisation.ResultsPaging); @@ -193,37 +194,46 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData); - // Identify item. - SysId userOrganisationId = model.UserOrganisationId.ToSysId(); - if (!SysId.IsValid(userOrganisationId)) - return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.specified"])); - - // Retrieve item. - SaxUserOrganisation userOrganisation = AccountMgr.GetUserOrganisationById(userOrganisationId); - if (userOrganisation == null) + // Record disposable items as they are created so all can be disposed when needed. + using (DisposableCollection disposableItems = new DisposableCollection()) { - SaLogger.Warn(fn + " Failed to retrieve User Organisation for Id: '" + model.UserOrganisationId + "'"); - return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"])); + + // Identify item. + SysId userOrganisationId = model.UserOrganisationId.ToSysId(); + if (!SysId.IsValid(userOrganisationId)) + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.specified"])); + + // Retrieve item. + SaxUserOrganisation userOrganisation = AccountMgr.GetUserOrganisationById(userOrganisationId); + disposableItems.Add(userOrganisation); + if (userOrganisation == null) + { + SaLogger.Warn(fn + " Failed to retrieve User Organisation for Id: '" + model.UserOrganisationId + "'"); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"])); + } + + // Retrieve User Account. + SaxUserAccount userAccount = AccountMgr.GetUserAccountById(userOrganisation.UserAccountId); + disposableItems.Add(userAccount); + if (userAccount == null) + { + SaLogger.Warn(fn + " Failed to retrieve user account for Id: '" + userOrganisation.UserAccountId + "'"); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"])); + } + + // Retrieve Organisation. + SaxOrganisation organisation = AccountMgr.GetOrganisationById(userOrganisation.OrganisationId); + disposableItems.Add(organisation); + if (organisation == null) + { + SaLogger.Warn(fn + " Failed to retrieve organisation for Id: '" + userOrganisation.OrganisationId + "'"); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"])); + } + + // Construct response. + PopulateMemberResponseData(responseData.Member, userOrganisation, userAccount, organisation); } - // Retrieve User Account. - SaxUserAccount userAccount = AccountMgr.GetUserAccountById(userOrganisation.UserAccountId); - if (userAccount == null) - { - SaLogger.Warn(fn + " Failed to retrieve user account for Id: '" + userOrganisation.UserAccountId + "'"); - return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"])); - } - - // Retrieve Organisation. - SaxOrganisation organisation = AccountMgr.GetOrganisationById(userOrganisation.OrganisationId); - if (organisation == null) - { - SaLogger.Warn(fn + " Failed to retrieve organisation for Id: '" + userOrganisation.OrganisationId + "'"); - return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"])); - } - - // Construct response. - PopulateMemberResponseData(responseData.Member, userOrganisation, userAccount, organisation); responseData.Success = true; return Json(responseData); } @@ -248,94 +258,109 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers if (responseData.HasControlErrors()) return Json(responseData); - // User account. - SysId userAccountId = model.MemberUserAccountId.ToSysId(); - SaxUserAccount userAccount = null; - if (!SysId.IsValid(userAccountId) && !StringUtil.IsEmpty(model.MemberUserEmailAddress)) + // Record disposable items as they are created so all can be disposed when needed. + using (DisposableCollection disposableItems = new DisposableCollection()) { - userAccount = AccountMgr.GetUserAccountByEmailAddress(model.MemberUserEmailAddress); - userAccountId = userAccount?.Id ?? new SysId(); - } - if (!SysId.IsValid(userAccountId)) - { - SaLogger.Warn(fn + " User Account must be identified"); - return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.data.incomplete"])); - } - // Organisation. - SysId organisationId = model.MemberOrganisationId.ToSysId(); - SaxOrganisation organisation = null; - if (!SysId.IsValid(organisationId)) - { - SaLogger.Warn(fn + " Organisation Id must be specified"); - return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.data.incomplete"])); - } + // User account. + SysId userAccountId = model.MemberUserAccountId.ToSysId(); + SaxUserAccount userAccount = null; + if (!SysId.IsValid(userAccountId) && !StringUtil.IsEmpty(model.MemberUserEmailAddress)) + { + userAccount = AccountMgr.GetUserAccountByEmailAddress(model.MemberUserEmailAddress); + disposableItems.Add(userAccount); + userAccountId = userAccount?.Id ?? new SysId(); + } + if (!SysId.IsValid(userAccountId)) + { + SaLogger.Warn(fn + " User Account must be identified"); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.data.incomplete"])); + } - // Retrieve existing item. - SysId userOrganisationId = model.UserOrganisationId.ToSysId(); - SaxUserOrganisation userOrganisation = AccountMgr.GetUserOrganisationById(userOrganisationId); - if (userOrganisation == null && SysId.IsValid(userOrganisationId)) - { - SaLogger.Warn(fn + " Failed to retrieve user organisation for Id: '" + userOrganisationId + "'"); - return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"])); - } - if (userOrganisation != null && (!userOrganisation.UserAccountId.Equals(userAccountId) || !userOrganisation.OrganisationId.Equals(organisationId))) - { - // Either User Account and/or Organisation Ids don't match. - SaLogger.Warn(fn + " Inconsistent user organisation properties. UserAccountId: " + userOrganisation.UserAccountId + " vs " + userAccountId + ". OrganisationId: " + userOrganisation.OrganisationId + " vs " + organisationId + "."); - return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"])); - } + // Organisation. + SysId organisationId = model.MemberOrganisationId.ToSysId(); + SaxOrganisation organisation = null; + if (!SysId.IsValid(organisationId)) + { + SaLogger.Warn(fn + " Organisation Id must be specified"); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.data.incomplete"])); + } - // User Organisation not identified. - if (userOrganisation == null) - { - // See whether user has an 'interest' in the organisation. - SaxUserOrganisation.Params prmUserOrganisation = new SaxUserOrganisation.Params(); - prmUserOrganisation.UserAccountIdsIncluded.Add(userAccountId); - prmUserOrganisation.OrganisationIdsIncluded.Add(organisationId); - prmUserOrganisation.OrganisationMembersOnly = false; - prmUserOrganisation.StatusCat = SaxUserOrganisation.StatusCat.Any; - userOrganisation = AccountMgr.GetUserOrganisation(prmUserOrganisation); - if (userOrganisation != null) - userOrganisation.Status = SaStatus.Active; + // Retrieve existing item. + SysId userOrganisationId = model.UserOrganisationId.ToSysId(); + SaxUserOrganisation userOrganisation = AccountMgr.GetUserOrganisationById(userOrganisationId); + disposableItems.Add(userOrganisation); + if (userOrganisation == null && SysId.IsValid(userOrganisationId)) + { + SaLogger.Warn(fn + " Failed to retrieve user organisation for Id: '" + userOrganisationId + "'"); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"])); + } + if (userOrganisation != null && (!userOrganisation.UserAccountId.Equals(userAccountId) || !userOrganisation.OrganisationId.Equals(organisationId))) + { + // Either User Account and/or Organisation Ids don't match. + SaLogger.Warn(fn + " Inconsistent user organisation properties. UserAccountId: " + userOrganisation.UserAccountId + " vs " + userAccountId + ". OrganisationId: " + userOrganisation.OrganisationId + " vs " + organisationId + "."); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"])); + } + + // User Organisation not identified. + if (userOrganisation == null) + { + // See whether user has an 'interest' in the organisation. + SaxUserOrganisation.Params prmUserOrganisation = new SaxUserOrganisation.Params(); + prmUserOrganisation.UserAccountIdsIncluded.Add(userAccountId); + prmUserOrganisation.OrganisationIdsIncluded.Add(organisationId); + prmUserOrganisation.OrganisationMembersOnly = false; + prmUserOrganisation.StatusCat = SaxUserOrganisation.StatusCat.Any; + userOrganisation = AccountMgr.GetUserOrganisation(prmUserOrganisation); + if (userOrganisation != null) + userOrganisation.Status = SaStatus.Active; + else + // Organisation not found for user. + userOrganisation = AccountMgr.CreateUserOrganisation(userAccountId, organisationId); + disposableItems.Add(userOrganisation); + } + + // Retrieve associated items. + if (userAccount == null) + { + userAccount = AccountMgr.GetUserAccountById(userAccountId); + disposableItems.Add(userAccount); + } + if (organisation == null) + { + organisation = AccountMgr.GetOrganisationById(organisationId); + disposableItems.Add(organisation); + } + + // Populate item properties. + if (model.MemberRoleCodes != null) + userOrganisation.OrganisationRoles = SaxUserOrganisation.GetOrganisationRolesFromCodes(model.GetMemberRoleCodes()); + userOrganisation.AddOrganisationRole(Sax.OrganisationRole.Member); + if (model.MemberStatusCode != null) + userOrganisation.Status = model.MemberStatus; + + // Store item. + if (!SysId.IsValid(userOrganisation.Id)) + { + if (!AccountMgr.InsertUserOrganisation(userOrganisation, DbUpdateInfo)) + { + SaLogger.Warn(fn + " Failed to insert user organisation: " + AccountMgr.ErrorStateMessage); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.insert.failed"] + "\n\n" + AccountMgr.ErrorStateMessage)); + } + } else - // Organisation not found for user. - userOrganisation = AccountMgr.CreateUserOrganisation(userAccountId, organisationId); - } - - // Retrieve associated items. - if (userAccount == null) - userAccount = AccountMgr.GetUserAccountById(userAccountId); - if (organisation == null) - organisation = AccountMgr.GetOrganisationById(organisationId); - - // Populate item properties. - if (model.MemberRoleCodes != null) - userOrganisation.OrganisationRoles = SaxUserOrganisation.GetOrganisationRolesFromCodes(model.GetMemberRoleCodes()); - userOrganisation.AddOrganisationRole(Sax.OrganisationRole.Member); - if (model.MemberStatusCode != null) - userOrganisation.Status = model.MemberStatus; - - // Store item. - if (!SysId.IsValid(userOrganisation.Id)) - { - if (!AccountMgr.InsertUserOrganisation(userOrganisation, DbUpdateInfo)) { - SaLogger.Warn(fn + " Failed to insert user organisation: " + AccountMgr.ErrorStateMessage); - return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.insert.failed"] + "\n\n" + AccountMgr.ErrorStateMessage)); - } - } - else - { - if (!AccountMgr.UpdateUserOrganisation(userOrganisation, DbUpdateInfo)) - { - SaLogger.Warn(fn + " Failed to update user organisation [Id:" + SysId.ToString(userOrganisation.Id) + "]: " + AccountMgr.ErrorStateMessage); - return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + AccountMgr.ErrorStateMessage)); + if (!AccountMgr.UpdateUserOrganisation(userOrganisation, DbUpdateInfo)) + { + SaLogger.Warn(fn + " Failed to update user organisation [Id:" + SysId.ToString(userOrganisation.Id) + "]: " + AccountMgr.ErrorStateMessage); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"] + "\n\n" + AccountMgr.ErrorStateMessage)); + } } + + // Construct response. + PopulateMemberResponseData(responseData.Member, userOrganisation, userAccount, organisation); } - // Construct response. - PopulateMemberResponseData(responseData.Member, userOrganisation, userAccount, organisation); responseData.Success = true; return Json(responseData); } diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/SystemController-Ajax-Database.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/SystemController-Ajax-Database.cs index 3e2d1b5..bd6a871 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/SystemController-Ajax-Database.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/SystemController-Ajax-Database.cs @@ -50,7 +50,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers //------------------------------------------------------------------- [HttpPost] [Route("System/Ajax/GetDatabaseTables")] - public IActionResult Ajax_GetDatabaseTables(Model_Sport model) + public IActionResult Ajax_GetDatabaseTables(Model_System model) { String fn = GetType().Name + ".Ajax_GetDatabaseTables"; String messageTitle = Localiser["system.caption.database"]; @@ -83,6 +83,10 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers List tableDfns = db.GetTableDfns(); foreach (DbDfnTable tableDfn in tableDfns.OrderBy(item => item.Name)) { + // Filter by table name. + if (!StringUtil.IsEmpty(model.DatabaseFilter.Table) && !tableDfn.Name.ContainsCI(model.DatabaseFilter.Table)) + continue; + DatabaseTableData tableData = new DatabaseTableData(); tableData.SchemaName = tableDfn.Schema; tableData.TableName = tableDfn.Name; diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/UserController-Ajax.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/UserController-Ajax.cs index 8dfaf10..583eabc 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/UserController-Ajax.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/UserController-Ajax.cs @@ -211,7 +211,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers { // Identify item. - SysId userAccountId = model.UserAccountId.ToSysId(); + SysId userAccountId = model.UserId.ToSysId(); if (!SysId.IsValid(userAccountId)) return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.specified"])); @@ -302,12 +302,12 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers { // Retrieve existing item. - SysId userAccountId = model.UserAccountId.ToSysId(); + SysId userAccountId = model.UserId.ToSysId(); SaxUserAccount user = AccountMgr.GetUserAccountById(userAccountId); disposableItems.Add(user); if (user == null && SysId.IsValid(userAccountId)) { - SaLogger.Warn(fn + " Failed to retrieve user for Id: '" + model.UserAccountId + "'"); + SaLogger.Warn(fn + " Failed to retrieve user for Id: '" + model.UserId + "'"); return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"])); } @@ -428,12 +428,12 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers { // Retrieve user. - SysId userAccountId = model.UserAccountId.ToSysId(); + SysId userAccountId = model.UserId.ToSysId(); SaxUserAccount userAccount = AccountMgr.GetUserAccountById(userAccountId); disposableItems.Add(userAccount); if (userAccount == null) { - SaLogger.Warn(fn + " Failed to retrieve user for Id: '" + model.UserAccountId + "'"); + SaLogger.Warn(fn + " Failed to retrieve user for Id: '" + model.UserId + "'"); return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"])); } diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/UsersOnlineController-Ajax.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/UsersOnlineController-Ajax.cs index 6ee45e1..98848e2 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/UsersOnlineController-Ajax.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/UsersOnlineController-Ajax.cs @@ -160,7 +160,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers { // Identify item. - SysId userAccountId = model.UserAccountId.ToSysId(); + SysId userAccountId = model.UserId.ToSysId(); if (!SysId.IsValid(userAccountId)) return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.specified"])); diff --git a/SaxoniteCoreWebAdmin/Admin/Models/Model_User.cs b/SaxoniteCoreWebAdmin/Admin/Models/Model_User.cs index 48946be..064a16e 100644 --- a/SaxoniteCoreWebAdmin/Admin/Models/Model_User.cs +++ b/SaxoniteCoreWebAdmin/Admin/Models/Model_User.cs @@ -61,7 +61,7 @@ namespace Saxonite.Net.Core.Web.Admin.Models //------------------------------------------------------------------- // User properties. - public String UserAccountId { get; set; } + public String UserId { get; set; } [EmailAddress] public String UserEmailAddress { get; set; } public String UserTitle { get; set; } @@ -115,7 +115,7 @@ namespace Saxonite.Net.Core.Web.Admin.Models { // Check for duplicate email address. SaxUserAccount.Params prmUserAccount = new SaxUserAccount.Params(); - SysId userAccountId = UserAccountId.ToSysId(); + SysId userAccountId = UserId.ToSysId(); if (SysId.IsValid(userAccountId)) prmUserAccount.IdsExcluded.Add(userAccountId); prmUserAccount.EmailAddress = UserEmailAddress; diff --git a/SaxoniteCoreWebAdmin/Localisation/SaxAdminProperties.resx b/SaxoniteCoreWebAdmin/Localisation/SaxAdminProperties.resx index 75045a0..ce7cbac 100644 --- a/SaxoniteCoreWebAdmin/Localisation/SaxAdminProperties.resx +++ b/SaxoniteCoreWebAdmin/Localisation/SaxAdminProperties.resx @@ -138,8 +138,8 @@ Admin - - Awaiting Activation + + Application Status Home @@ -165,6 +165,9 @@ Available + + Awaiting activation + Cancel @@ -267,6 +270,9 @@ Members + + Members awaiting activation + Name @@ -282,6 +288,27 @@ OK + + Online last 15 mins + + + Online last 30 mins + + + Online last 5 mins + + + Online last 60 mins + + + Online this month + + + Online this week + + + Online today + Page {0} of {1} diff --git a/SaxoniteCoreWebAdmin/Views/Home/Index.cshtml b/SaxoniteCoreWebAdmin/Views/Home/Index.cshtml index 88ff289..c7ed855 100644 --- a/SaxoniteCoreWebAdmin/Views/Home/Index.cshtml +++ b/SaxoniteCoreWebAdmin/Views/Home/Index.cshtml @@ -7,27 +7,46 @@ sax.msg.noItemsAwaitAct = '@Localiser.GetJavascriptText("home.msg.no.items.awaiting.activation")'; sax.msg.dbSchemaValid = '@Localiser.GetJavascriptText("system.msg.database.schema.valid")'; + @* Items Awaiting Activation *@ -
+
-
@Localiser["home.caption.awaiting.activation"]
+
@Localiser["home.caption.application.status"]
@* Items awaiting activation templates *@
-
+
-
+
-
@(Localiser["label.items.awaiting.activation"]):
+
+ + + + + + + +
diff --git a/SaxoniteCoreWebAdmin/Views/Organisation/Index.cshtml b/SaxoniteCoreWebAdmin/Views/Organisation/Index.cshtml index 97d6811..0be0053 100644 --- a/SaxoniteCoreWebAdmin/Views/Organisation/Index.cshtml +++ b/SaxoniteCoreWebAdmin/Views/Organisation/Index.cshtml @@ -49,7 +49,7 @@
@Localiser["label.category"]
- @Html.DropDownListFor(model => model.Filter.OrganisationCategoryCode, Model.OrganisationCategoriesForSelect, Localiser["label.any"], new { @id = "", @class = "FilterOrganisationCategory sax-select" }) + @Html.DropDownListFor(model => model.Filter.OrganisationCategoryCode, Model.AdminOrganisationCategoriesForSelect, Localiser["label.any"], new { @id = "", @class = "FilterOrganisationCategory sax-select" })
@* Organisation sport *@ diff --git a/SaxoniteCoreWebAdmin/Views/Organisation/Panel-ActivationDialog.cshtml b/SaxoniteCoreWebAdmin/Views/Organisation/Panel-ActivationDialog.cshtml index 5f0f5e3..41231a0 100644 --- a/SaxoniteCoreWebAdmin/Views/Organisation/Panel-ActivationDialog.cshtml +++ b/SaxoniteCoreWebAdmin/Views/Organisation/Panel-ActivationDialog.cshtml @@ -68,19 +68,21 @@ -
Members awaiting activation.
-
-
- - - - - - -
- - -
+
+
@Localiser["label.members.awaiting.activation"]:
+
+
+ + + + + + +
+ + +
+
} diff --git a/SaxoniteCoreWebAdmin/Views/Organisation/Panel-OrganisationDialog.cshtml b/SaxoniteCoreWebAdmin/Views/Organisation/Panel-OrganisationDialog.cshtml index 963cb7a..834e847 100644 --- a/SaxoniteCoreWebAdmin/Views/Organisation/Panel-OrganisationDialog.cshtml +++ b/SaxoniteCoreWebAdmin/Views/Organisation/Panel-OrganisationDialog.cshtml @@ -49,7 +49,7 @@
- @Html.DropDownListFor(model => model.OrganisationCategoryValue, Model.OrganisationCategoriesForSelect, Localiser["label.select"], new { @id = "cbOrganisationCategory", @class = "OrganisationCategory sax-select" }) + @Html.DropDownListFor(model => model.OrganisationCategoryValue, Model.AdminOrganisationCategoriesForSelect, Localiser["label.select"], new { @id = "cbOrganisationCategory", @class = "OrganisationCategory sax-select" })
@@ -78,14 +78,15 @@
- +
@Html.HiddenFor(model => model.OrganisationSportId, new { @id = "ifOrganisationSportId", @class = "OrganisationSportId" }) -
-
+
+
@(Localiser[
+
@@ -103,8 +104,8 @@
@Html.HiddenFor(model => model.OrganisationRegionIdCountry, new { @id = "ifOrganisationRegionIdCountry", @class = "OrganisationRegionIdCountry" }) -
-
+
+
diff --git a/SaxoniteCoreWebAdmin/Views/System/Database.cshtml b/SaxoniteCoreWebAdmin/Views/System/Database.cshtml index 0ef64eb..a453b9d 100644 --- a/SaxoniteCoreWebAdmin/Views/System/Database.cshtml +++ b/SaxoniteCoreWebAdmin/Views/System/Database.cshtml @@ -17,7 +17,7 @@ @using (@Html.BeginForm(FormMethod.Post, new { @id = "frmDatabaseFilter", @class = "sax-form", @onSubmit = "return false;" })) {
-
@Localiser["label.database.table"]
+
@Localiser["label.search"]
@Html.TextBoxFor(model => model.DatabaseFilter.Table, new { @id = "ifFilterDatabaseTable", @class = "FilterDatabaseTable", @maxlength = "32" })
} diff --git a/SaxoniteCoreWebAdmin/Views/User/Panel-UserDialog.cshtml b/SaxoniteCoreWebAdmin/Views/User/Panel-UserDialog.cshtml index 6080820..3f4dc0b 100644 --- a/SaxoniteCoreWebAdmin/Views/User/Panel-UserDialog.cshtml +++ b/SaxoniteCoreWebAdmin/Views/User/Panel-UserDialog.cshtml @@ -15,14 +15,14 @@ @using (@Html.BeginForm(FormMethod.Post, new { @id = "frmUserId", @class = "sax-form" })) { - @Html.HiddenFor(model => model.UserAccountId, new { @class = "UserAccountId" }) + @Html.HiddenFor(model => model.UserId, new { @class = "UserId" }) }
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmUser", @class = "sax-form", @onSubmit = "return false;" })) { - @Html.HiddenFor(model => model.UserAccountId, new { @class = "UserAccountId" }) + @Html.HiddenFor(model => model.UserId, new { @class = "UserId" }) @* User email *@
@@ -181,7 +181,7 @@
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmUserEmails", @class = "sax-form", @onSubmit = "return false;" })) { - @Html.HiddenFor(model => model.UserAccountId, new { @class = "UserAccountId" }) + @Html.HiddenFor(model => model.UserId, new { @class = "UserId" })
@Localiser["user.emails.intro"]
diff --git a/SaxoniteCoreWebAdmin/wwwroot/css/sax-main.css b/SaxoniteCoreWebAdmin/wwwroot/css/sax-main.css index 0684bb4..f9ce95d 100644 --- a/SaxoniteCoreWebAdmin/wwwroot/css/sax-main.css +++ b/SaxoniteCoreWebAdmin/wwwroot/css/sax-main.css @@ -346,6 +346,8 @@ a:focus .ui-dialog.ui-widget.ui-widget-content.ui-corner-all { border-radius: 6px; + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; } .ui-dialog.ui-widget .ui-widget-header @@ -375,11 +377,14 @@ a:focus .ui-dialog .ui-dialog-buttonpane { + padding: 4px 8px; border-color: #d4af37; } .ui-dialog .ui-dialog-buttonpane button { + min-width:80px; + margin:4px 8px 4px 0; color: #fff; background-color: #d4af37; border-color: #d4af37; diff --git a/SaxoniteCoreWebAdmin/wwwroot/js/sax-home.js b/SaxoniteCoreWebAdmin/wwwroot/js/sax-home.js index 3e456c7..6717ee4 100644 --- a/SaxoniteCoreWebAdmin/wwwroot/js/sax-home.js +++ b/SaxoniteCoreWebAdmin/wwwroot/js/sax-home.js @@ -5,68 +5,84 @@ (function (win, doc, undef) { "use strict"; - let $pnl = sax.$pnl, $dlg = sax.$dlg, $frm = sax.$frm; + let ah = sax.adminHome = {}; + let $pnl = ah.$pnl = {}, $dlg = ah.$dlg = {}, $frm = ah.$frm = {}; sax.itemIdPrefix = 'Itm_'; //-------------------------------------------------------------- - sax.refreshAwaitActivate = function (delay) + ah.refreshApplicationStats = function (delay) { + clearTimeout(ah.toStats); if (sax.awaitingLogin) return; - if (!sax.awaitingRefreshResponse) + if (!ah.awaitingStatsResponse) { - var $items = $('.sax-panel-item-list', $pnl.awaitAct).fadeOut(200).empty(); - var $msgs = $('.sax-panel-messages', $pnl.awaitAct).hide().empty().append($('
' + sax.msg.dataQuerying + '
')).fadeIn(200); + var $items = $('.sax-panel-item-list', $pnl.appStats).fadeOut(200).empty(); + var $msgs = $('.sax-panel-messages', $pnl.appStats).hide().empty().append($('
' + sax.msg.dataQuerying + '
')).fadeIn(200); } - sax.ajax.awaitAct = sax.ajax.awaitAct || { url: sax.url.base + 'Home/Ajax/GetItemsAwaitingActivation', formId: 'frmNavigate', callbackFn: sax.handleAwaitActivateResponse, noWaitIcon: true }; + sax.ajax.awaitAct = sax.ajax.awaitAct || { url: sax.url.base + 'Home/Ajax/GetApplicationStats', formId: 'frmNavigate', callbackFn: ah.handleStatsResponse, noWaitIcon: true }; sax.doAjaxRequest(sax.ajax.awaitAct, delay || 50); - sax.awaitingRefreshResponse = true; + ah.awaitingStatsResponse = true; } - sax.handleAwaitActivateResponse = function (response) + ah.handleStatsResponse = function (response) { //sax.debug('handleAwaitActivateResponse', response); - delete sax.awaitingRefreshResponse; - var $msgs = $('.sax-panel-messages', $pnl.awaitAct).hide().empty(); - var $items = $('.sax-panel-item-list', $pnl.awaitAct).hide().empty(); - var $tmpl = $('.sax-panel-item-template .sax-panel-item.AwaitActivateItem', $pnl.awaitAct); + delete ah.awaitingStatsResponse; + var $msgs = $('.sax-panel-messages', $pnl.appStats).hide().empty(); + var $items = $('.sax-panel-item-list', $pnl.appStats).hide().empty(); + ah.toStats = setTimeout(ah.refreshApplicationStats, 60000); if (!response.success) return; - // Items. - var items = response.items; - if (!items.length) - return $msgs.append($('
' + sax.msg.noItemsAwaitAct + '
')).fadeIn(500); - for (let itemIdx = 0; itemIdx < items.length; itemIdx++) - $items.append(sax.populateAwaitActivateItem($tmpl.clone(), items[itemIdx])); + //return $msgs.append($('
' + sax.msg.noItemsAwaitAct + '
')).fadeIn(500); + // Users. + let user = response.stats.user; + let $user = sax.getAppStatItemTemplate(); + $('.sax-header .sax-caption', $user).text(user.caption||'Users'); + ah.populateAppStat($user, user.awaiting.label, user.awaiting.value, user.awaitingLink) + for (let item of user.onlineStats) + ah.populateAppStat($user, item.label, item.value, user.onlineLink) + $items.append($user); + // Organisations. + let org = response.stats.organisation; + let $org = sax.getAppStatItemTemplate(); + $('.sax-header .sax-caption', $org).text(org.caption||'Organisations'); + ah.populateAppStat($org, org.awaiting.label, org.awaiting.value, org.awaitingLink) + $items.append($org); $items.slideDown(750); } - sax.populateAwaitActivateItem = function ($item, item) + ah.populateAppStat = function ($item, label, value, link) { - if (!item) return; - if (item.viewLink)$('.sax-action.sax-view', $item).on('click', function () { sax.goToPage(item.viewLink); }); - $('.AwaitActivateItemType', $item).text(item.itemTypeDescPlural); - $('.AwaitActivateItemCount', $item).text(item.itemCount); + let $stat = sax.getAppStatStatTemplate(); + $stat.on('click', function () { win.location = link; }); + $('.sax-label', $stat).text(label + ':'); + $('.sax-value', $stat).text(value); + $('table.sax-item-stats', $item).append($stat); return $item; } - sax.getAwaitActivateItemTemplate = function () + sax.getAppStatItemTemplate = function () { - return $('.sax-panel-item-template .sax-panel-item.AwaitActivateItem', $pnl.results).clone(); + return $('.sax-panel-item-template .sax-panel-item.sax-item-stats', $pnl.appStats).clone(); + } + sax.getAppStatStatTemplate = function () + { + return $('.sax-panel-item-template .sax-item-stat', $pnl.appStats).clone(); } //-------------------------------------------------------------- sax.refreshDbSchema = function (delay) { if (sax.awaitingLogin) return; - if (!sax.awaitingRefreshResponse) + if (!ah.awaitingSchemaResponse) { var $items = $('.sax-panel-item-list', $pnl.dbSchema).fadeOut(200).empty(); var $msgs = $('.sax-panel-messages', $pnl.dbSchema).hide().empty().append($('
' + sax.msg.dataQuerying + '
')).fadeIn(200); } sax.ajax.dbSchema = sax.ajax.dbSchema || { url: sax.url.base + 'System/Ajax/ValidateDatabaseSchema', formId: 'frmNavigate', callbackFn: sax.handleDbSchemaResponse, noWaitIcon: true }; sax.doAjaxRequest(sax.ajax.dbSchema, delay || 50); - sax.awaitingRefreshResponse = true; + ah.awaitingSchemaResponse = true; } sax.handleDbSchemaResponse = function (response) { //sax.debug('handleDbSchemaResponse', response); - delete sax.awaitingRefreshResponse; + delete ah.awaitingSchemaResponse; var $msgs = $('.sax-panel-messages', $pnl.dbSchema).hide().empty(); var $items = $('.sax-panel-item-list', $pnl.dbSchema).hide().empty(); var $tmpl = $('.sax-panel-item-template .sax-panel-item.DbSchemaItem', $pnl.dbSchema); @@ -95,11 +111,11 @@ //-------------------------------------------------------------- $(function () { - $pnl.awaitAct = $('.sax-panel-results.AwaitActivate'); + $pnl.appStats = $('.sax-panel-results.ApplicationStats'); $pnl.dbSchema = $('.sax-panel-results.DbSchema'); - setTimeout(sax.refreshAwaitActivate, 250); - setTimeout(sax.refreshDbSchema, 250); + ah.toStats = setTimeout(ah.refreshApplicationStats, 400); + ah.toSchema = setTimeout(sax.refreshDbSchema, 600); }); }(window, window.document)); diff --git a/SaxoniteCoreWebAdmin/wwwroot/js/sax-main.js b/SaxoniteCoreWebAdmin/wwwroot/js/sax-main.js index e47f127..05c9257 100644 --- a/SaxoniteCoreWebAdmin/wwwroot/js/sax-main.js +++ b/SaxoniteCoreWebAdmin/wwwroot/js/sax-main.js @@ -359,18 +359,18 @@ // Session. sax.configureKeepLoggedIn = function (single) { - sax.debug('KeepLoggedIn Configuring'); + //sax.debug('KeepLoggedIn Configuring'); let kli = sax.kli; if (!kli.timeout) kli.timeout = setTimeout(function () { sax.doKeepLoggedIn(single); }, Math.max(30000, kli.interval || 300000)); } sax.doKeepLoggedIn = function (single) { - sax.debug('KeepLoggedIn Calling'); + //sax.debug('KeepLoggedIn Calling'); let kli = sax.kli; delete kli.timeout; var onKeepLoggedIn = function (response) { - sax.debug('KeepLoggedIn Responded', response); + //sax.debug('KeepLoggedIn Responded', response); kli.lastAt = sax.getTime(); if (!single) sax.configureKeepLoggedIn(); } @@ -378,13 +378,13 @@ } sax.configureSessionRefresh = function () { - sax.debug('SessionRefresh Configuring'); + //sax.debug('SessionRefresh Configuring'); $(document).on('mousedown mousemove touchstart scroll keydown', sax.triggeRefreshSession); } sax.triggeRefreshSession = function () { if (sax.kli.timeout) return; - sax.debug('SessionRefresh Triggering'); + //sax.debug('SessionRefresh Triggering'); sax.configureKeepLoggedIn(1); } diff --git a/SaxoniteCoreWebAdmin/wwwroot/js/sax-organisation.js b/SaxoniteCoreWebAdmin/wwwroot/js/sax-organisation.js index db669fd..ef33f60 100644 --- a/SaxoniteCoreWebAdmin/wwwroot/js/sax-organisation.js +++ b/SaxoniteCoreWebAdmin/wwwroot/js/sax-organisation.js @@ -23,7 +23,7 @@ } op.getItemTemplate = function () { - return $('.sax-panel-item-template .sax-panel-item.Organisation', $pnl.results); + return $('.sax-panel-item-template .sax-panel-item.Organisation', $pnl.results).clone(); } op.getDropdownItemTemplate = function () { @@ -36,7 +36,6 @@ sax.refreshControlValues(response.controlValues); var $msgs = $('.sax-panel-messages', $pnl.results).hide().empty(); var $items = $('.sax-panel-item-list', $pnl.results).hide().empty(); - var $tmpl = op.getItemTemplate(); sax.refreshPaging(response, '#frmOrganisationFilter', sax.refreshResults); if (!response.success) return; // Reference data - Statuses. @@ -48,12 +47,7 @@ if (!items.length) return $msgs.append($('
' + sax.msg.dataNoneFound + '
')).fadeIn(500); for (let itemIdx = 0; itemIdx < items.length; itemIdx++) - { - let item = items[itemIdx]; - let $item = $tmpl.clone(); - op.populateItem($item, item); - $items.append($item); - } + $items.append(op.populateItem(op.getItemTemplate(), items[itemIdx])); $items.fadeIn(50); } op.populateItem = function ($item, item) @@ -65,17 +59,17 @@ $('.OrganisationCategory', $item).text(item.categoryDesc); $('.OrganisationSport', $item).text(item.sportName); $('.OrganisationRegion', $item).text(item.regionName); - $('.OrganisationMemberCount', $item).on('click', function () { sax.onClickMemberCount(item.id); }).find('.MemberCount').text(item.memberCount); + $('.OrganisationMemberCount', $item).on('click', function () { op.onClickMemberCount(item.id); }).find('.MemberCount').text(item.memberCount); $('.StatusDesc', $item).text(item.statusDesc).addClass('sax-status-' + item.statusCode.toLowerCase()); $('.sax-caption .sax-action').toggleClass('sax-action', !item.deleted); if (!item.deleted) { let $mnu = $('ul.sax-dropdown-menu', $item); - $mnu.append(op.getDropdownItemTemplate().clone().find('div.sax-label').text(sax.lbl.edit).end().on('click', function () { sax.updateItem(item.id) })); + $mnu.append(op.getDropdownItemTemplate().clone().find('div.sax-label').text(sax.lbl.edit).end().on('click', function () { op.updateItem(item, $item) })); if (item.awaiting) - $mnu.append(op.getDropdownItemTemplate().clone().find('div.sax-label').text(sax.lbl.activate).end().on('click', function () { sax.activateItem(item.id) })); - $('.sax-header .sax-action.sax-item-update', $item).off('click').on('click', function () { sax.updateItem(item.id) }); - $item.off('dblclick').on('dblclick', function () { sax.updateItem(item.id) }); + $mnu.append(op.getDropdownItemTemplate().clone().find('div.sax-label').text(sax.lbl.activate).end().on('click', function () { op.activateItem(item) })); + $('.sax-header .sax-action.sax-item-update', $item).off('click').on('click', function () { op.updateItem(item, $item) }); + $item.off('dblclick').on('dblclick', function () { op.updateItem(item, $item) }); } $('.sax-burger', $item).toggle(!!$('.sax-burger .sax-dropdown-menu .sax-dropdown-item', $item).length); $('.sax-panel-messages', $pnl.results).hide().empty(); @@ -118,22 +112,26 @@ } //-------------------------------------------------------------- - sax.selectSport = function () + op.selectOrgSport = function () { if (!op.data.sports) - op.populateSports(sax.showSelectSport); - else sax.showSelectSport(); + op.populateSports(op.showSelectOrgSport); + else op.showSelectOrgSport(); } - sax.showSelectSport = function () + op.showSelectOrgSport = function () { - sax.data.selectItem.fn.selectItem({ caption: sax.dlg.title.selectSport, items: op.data.sports, fnCallback: sax.onSportSelected }); + sax.data.selectItem.fn.selectItem({ caption: sax.dlg.title.selectSport, items: op.data.sports, fnCallback: op.onOrgSportSelected }); } - sax.onSportSelected = function (spt) + op.onOrgSportSelected = function (spt) { let frm = $frm.org; $('.OrganisationSportId', frm).val(spt.id); $('.OrganisationSportCaption', frm).val(spt.name); - sax.clearControlError($('.OrganisationSportCaption', frm)); + sax.clearControlError($('.OrganisationSportId', frm)); + } + op.clearOrgSport = function () + { + $('.OrganisationSportId,.OrganisationSportCaption', $frm.org).val(''); } //-------------------------------------------------------------- @@ -173,17 +171,17 @@ } //-------------------------------------------------------------- - sax.selectRegion = function () + op.selectOrgRegion = function () { if (!op.data.regions) - op.populateRegions(sax.showSelectRegion); - else sax.showSelectRegion(); + op.populateRegions(op.showSelectOrgRegion); + else op.showSelectOrgRegion(); } - sax.showSelectRegion = function () + op.showSelectOrgRegion = function () { - sax.data.selectItem.fn.selectItem({ caption: sax.dlg.title.selectRegion, items: op.data.regions, fnCallback: sax.onRegionSelected }); + sax.data.selectItem.fn.selectItem({ caption: sax.dlg.title.selectRegion, items: op.data.regions, fnCallback: op.onOrgRegionSelected }); } - sax.onRegionSelected = function (rgn) + op.onOrgRegionSelected = function (rgn) { //sax.debug('onRegionSelected', rgn); let frm = $frm.org; @@ -193,30 +191,34 @@ } //-------------------------------------------------------------- - sax.createItem = function () + op.createItem = function () + { + op.clearItem(); + $dlg.organisation.dialog('option', 'title', sax.dlg.title.createOrganisation); + sax.openDialog($dlg.organisation, 600, 480); + } + op.clearItem = function () { let frm = $frm.org; $('.OrganisationId,.OrganisationSportId,.OrganisationRegionIdCountry,input[type="text"],textarea,select', frm).val(''); $('.OrganisationStatus', frm).val(sax.data.defaultStatusCode); sax.clearControlErrors(); - $dlg.organisation.dialog('option', 'title', sax.dlg.title.createOrganisation); - sax.openDialog($dlg.organisation, 600, 480); } //-------------------------------------------------------------- - sax.updateItem = function (id) + op.updateItem = function (item, $item) { - $('.OrganisationId', $frm.orgId).val(id); - op.ajax.updateItem = op.ajax.updateItem || { url: sax.url.base + 'Organisation/Ajax/GetItem', formId: 'frmOrganisationId', callbackFn: sax.onUpdateItem }; + $('.OrganisationId', $frm.orgId).val(item.id); + op.ajax.updateItem = op.ajax.updateItem || { url: sax.url.base + 'Organisation/Ajax/GetItem', formId: 'frmOrganisationId', callbackFn: op.onUpdateItem }; sax.doAjaxRequest(op.ajax.updateItem, 50); } - sax.onUpdateItem = function (response) + op.onUpdateItem = function (response) { //sax.debug('onUpdateItem', response); - let frm = $frm.org; - $('.OrganisationId,input[type="text"],textarea,select', frm).val(''); + op.clearItem(); if (!response.success) return; let item = response.organisation; + let frm = $frm.org; $('.OrganisationId', frm).val(item.id); $('.OrganisationName', frm).val(item.name); $('.OrganisationType', frm).val(item.typeCode); @@ -232,20 +234,20 @@ } //-------------------------------------------------------------- - sax.storeItem = function () + op.storeItem = function () { - op.ajax.storeItem = op.ajax.storeItem || { url: sax.url.base + 'Organisation/Ajax/StoreItem', formId: 'frmOrganisation', callbackFn: sax.onStoreItem }; + op.ajax.storeItem = op.ajax.storeItem || { url: sax.url.base + 'Organisation/Ajax/StoreItem', formId: 'frmOrganisation', callbackFn: op.onStoreItem }; sax.doAjaxRequest(op.ajax.storeItem, 50); } - sax.onStoreItem = function (response) + op.onStoreItem = function (response) { //sax.debug('onStoreItem', response); sax.refreshControlValues(response.controlValues); if (!response.success) return; $dlg.organisation.dialog('close'); - sax.refreshItem(response.organisation); + op.refreshItem(response.organisation); } - sax.refreshItem = function (item) + op.refreshItem = function (item) { var $item = $('#' + sax.itemIdPrefix + item.id); if (!$item.length) @@ -257,16 +259,16 @@ } //-------------------------------------------------------------- - sax.activateItem = function (id) + op.activateItem = function (item) { - //sax.debug('activateItem(' + id + ')') - $('.OrganisationId', $frm.orgId).val(id); - op.ajax.getActivationInfo = op.ajax.getActivationInfo || { url: sax.url.base + 'Organisation/Ajax/GetActivationInfo', formId: 'frmOrganisationId', callbackFn: sax.showActivationInfo }; + //sax.debug('activateItem(' + item.id + ')') + $('.OrganisationId', $frm.orgId).val(item.id); + op.ajax.getActivationInfo = op.ajax.getActivationInfo || { url: sax.url.base + 'Organisation/Ajax/GetActivationInfo', formId: 'frmOrganisationId', callbackFn: op.showActivationInfo }; sax.doAjaxRequest(op.ajax.getActivationInfo, 50); } - sax.showActivationInfo = function (response) + op.showActivationInfo = function (response) { - sax.debug('showActivationInfo', response); + //sax.debug('showActivationInfo', response); if (!response.success || !response.organisation) return; let item = response.organisation; let frm = $('#frmOrganisationActivation'); @@ -282,36 +284,38 @@ popItemValue('.OrganisationAddress', item.addressSummary); let $orgMbrs = $('.OrganisationMembers', $dlg.activate).empty(); let $mbrTmpl = $('.sax-panel-item-template tr.sax-item.Member'); - sax.debug(response.members, $orgMbrs, $mbrTmpl); - for (let idx = 0; idx < response.members.length; idx++) + let members = response.members; + //sax.debug(members, $orgMbrs, $mbrTmpl); + for (let idx = 0; idx < members.length; idx++) { - let item = response.members[idx]; + let item = members[idx]; let $item = $mbrTmpl.clone(); $('.MemberId', $item).val(item.id); $('.MemberActivate', $item).prop('name', 'MemberActivate_' + item.id) $('.MemberEmail', $item).text(item.emailAddress); $('.MemberName', $item).text(item.displayName); $orgMbrs.append($item); - sax.debug($item); + //sax.debug($item); } + $('.MembersAwaitingActivation', $dlg.activate).toggle(!!members.length); sax.openDialog($dlg.activate, 480, 420); } - sax.activateOrganisation = function () + op.activateOrganisation = function () { let frm = $('#frmOrganisationActivation'); - op.ajax.activateOrganisation = op.ajax.activateOrganisation || { url: sax.url.base + 'Organisation/Ajax/Activate', formId: frm.prop('id'), callbackFn: sax.handleAtivateResponse }; + op.ajax.activateOrganisation = op.ajax.activateOrganisation || { url: sax.url.base + 'Organisation/Ajax/Activate', formId: frm.prop('id'), callbackFn: op.handleAtivateResponse }; sax.doAjaxRequest(op.ajax.activateOrganisation, 50); } - sax.handleAtivateResponse = function (response) + op.handleAtivateResponse = function (response) { - sax.debug('handleAtivateResponse', response); + //sax.debug('handleAtivateResponse', response); if (!response.success) return; $dlg.activate.dialog('close'); - sax.refreshItem(response.organisation); + op.refreshItem(response.organisation); } //-------------------------------------------------------------- - sax.onClickMemberCount = function (id) + op.onClickMemberCount = function (id) { if (!(id * 1)) return; sax.doPleaseWait(); @@ -327,7 +331,7 @@ $pnl.filter = $('.sax-panel-filter', $pnl.orgs); $pnl.results = $('.sax-panel-results', $pnl.orgs); - $('.sax-action.sax-item-create', $pnl.filter).on('click', function () { sax.createItem(); }); + $('.sax-action.sax-item-create', $pnl.filter).on('click', function () { op.createItem(); }); $('.sax-action.sax-results-refresh', $pnl.filter).on('click', function () { sax.refreshResults(); }); $frm.filter = $('#frmOrganisationFilter'); @@ -337,16 +341,17 @@ $frm.filter.on('dblclick', '.FilterOrganisationRegionCaption', op.selectFilterRegion).on('click', '.FilterRegionSelect', op.selectFilterRegion).on('click', '.FilterRegionClear', op.clearFilterRegion); $frm.orgId = $('#frmOrganisationId'); $frm.org = $('#frmOrganisation'); - $('.sax-input, .sax-textarea', $frm.org).on('input', function () { sax.clearControlError(this); }); - $('.sax-select', $frm.org).on('change', function () { sax.clearControlError(this); }); + $frm.org.on('input', '.sax-input, .sax-textarea', function () { sax.clearControlError(this); }).on('change', '.sax-select', function () { sax.clearControlError(this); }); + $frm.org.on('dblclick', '.OrganisationSportCaption', op.selectOrgSport).on('click', '#pbSelectSport', op.selectOrgSport).on('click', '.OrganisationSportClear', op.clearOrgSport); + $frm.org.on('dblclick', '.OrganisationRegionCaptionCountry', op.selectOrgRegion).on('click', '#pbSelectRegion', op.selectOrgRegion); var opt = sax.dlgOpts(); - opt.buttons[sax.dlg.btn.ok] = function () { sax.storeItem(); }; + opt.buttons[sax.dlg.btn.ok] = function () { op.storeItem(); }; opt.buttons[sax.dlg.btn.cancel] = function () { $(this).dialog('close'); }; $dlg.organisation = $('#dlgOrganisation').dialog(opt); var opt = sax.dlgOpts(); - opt.buttons[sax.dlg.btn.ok] = function () { sax.activateOrganisation(); }; + opt.buttons[sax.dlg.btn.ok] = function () { op.activateOrganisation(); }; opt.buttons[sax.dlg.btn.cancel] = function () { $(this).dialog('close'); }; $dlg.activate = $('#dlgOrganisationActivation').dialog(opt); diff --git a/SaxoniteCoreWebAdmin/wwwroot/js/sax-user.js b/SaxoniteCoreWebAdmin/wwwroot/js/sax-user.js index 8e5d42d..bea6543 100644 --- a/SaxoniteCoreWebAdmin/wwwroot/js/sax-user.js +++ b/SaxoniteCoreWebAdmin/wwwroot/js/sax-user.js @@ -264,14 +264,14 @@ //-------------------------------------------------------------- sax.createItem = function () { - sax.clearUserData(); + sax.clearItemData(); $dlg.user.dialog('option', 'title', sax.dlg.title.createUser); sax.openDialog($dlg.user, 600, 480); } - sax.clearUserData = function () + sax.clearItemData = function () { let frm = $frm.user; - $('.UserAccountId,.UserRegionIdCountry,.UserRegionIdProvince,.UserRoleCodes,input[type="text"],textarea,select', frm).val(''); + $('.UserId,.UserRegionIdCountry,.UserRegionIdProvince,.UserRoleCodes,input[type="text"],textarea,select', frm).val(''); $('.UserStatus', frm).val(sax.data.defaultStatusCode); sax.clearControlErrors(); sax.userAccount = { userRoleCodes: [] }; @@ -280,7 +280,7 @@ //-------------------------------------------------------------- sax.updateItem = function (item) { - $('.UserAccountId', $frm.userId).val(item.id); + $('.UserId', $frm.userId).val(item.id); sax.ajax.updateItem = sax.ajax.updateItem || { url: sax.url.base + 'User/Ajax/GetItem', formId: 'frmUserId', callbackFn: sax.onUpdateItem }; sax.doAjaxRequest(sax.ajax.updateItem, 50); } @@ -288,10 +288,10 @@ { //sax.debug('onUpdateItem', response); if (!response.success) return; - sax.clearUserData(); + sax.clearItemData(); let item = sax.userAccount = response.user; let frm = $frm.user; - $('.UserAccountId', frm).val(item.id); + $('.UserId', frm).val(item.id); $('.UserEmailAddress', frm).val(item.emailAddress); $('.UserTitle', frm).val(item.title); $('.UserFirstName', frm).val(item.firstName); @@ -333,9 +333,9 @@ //-------------------------------------------------------------- sax.userEmails = function (item) { - sax.debug('userEmails', item); + //sax.debug('userEmails', item); let frm = $frm.emailsl - $('.UserAccountId', frm).val(item.id); + $('.UserId', frm).val(item.id); $('.UserEmailAddress', frm).val(item.emailAddress); $('.UserDisplayName', frm).val(item.displayName); $('.RequestPasswordAdminOption', frm).toggle(item.systemAdmin); diff --git a/SaxoniteCoreWebAdmin/wwwroot/js/sax-usr-onl.js b/SaxoniteCoreWebAdmin/wwwroot/js/sax-usr-onl.js index 06292f7..5e0799e 100644 --- a/SaxoniteCoreWebAdmin/wwwroot/js/sax-usr-onl.js +++ b/SaxoniteCoreWebAdmin/wwwroot/js/sax-usr-onl.js @@ -198,20 +198,24 @@ //-------------------------------------------------------------- sax.createItem = function () + { + sax.clearItemData(); + $dlg.user.dialog('option', 'title', sax.dlg.title.createUser); + sax.openDialog($dlg.user, 600, 480); + } + sax.clearItemData = function () { let frm = $frm.user; - $('.UserAccountId,.UserRoleCodes,input[type="text"],textarea,select', frm).val(''); + $('.UserId,.UserRoleCodes,input[type="text"],textarea,select', frm).val(''); $('.UserStatus', frm).val(sax.data.defaultStatusCode); sax.clearControlErrors(); sax.userAccount = { userRoleCodes: [] }; - $dlg.user.dialog('option', 'title', sax.dlg.title.createUser); - sax.openDialog($dlg.user, 600, 480); } //-------------------------------------------------------------- sax.updateItem = function (item) { - $('.UserAccountId', $frm.userId).val(item.id); + $('.UserId', $frm.userId).val(item.id); sax.ajax.updateItem = sax.ajax.updateItem || { url: sax.url.base + 'User/Ajax/GetItem', formId: 'frmUserId', callbackFn: sax.onUpdateItem }; sax.doAjaxRequest(sax.ajax.updateItem, 50); } @@ -219,11 +223,10 @@ { //sax.debug('onUpdateItem', response); let frm = $frm.user; - $('.UserAccountId,input[type="text"],textarea,select', frm).val(''); - sax.clearControlErrors(); + sax.clearItemData(); if (!response.success) return; let item = sax.userAccount = response.user; - $('.UserAccountId', frm).val(item.id); + $('.UserId', frm).val(item.id); $('.UserEmailAddress', frm).val(item.emailAddress); $('.UserTitle', frm).val(item.title); $('.UserFirstName', frm).val(item.firstName); @@ -263,7 +266,7 @@ { sax.debug('userEmails', item); let frm = $frm.emailsl - $('.UserAccountId', frm).val(item.id); + $('.UserId', frm).val(item.id); $('.UserEmailAddress', frm).val(item.emailAddress); $('.UserDisplayName', frm).val(item.displayName); $('.RequestPasswordAdminOption', frm).toggle(item.systemAdmin); diff --git a/SaxoniteCoreWebOnline/Online/Controllers/ActivityController-Ajax.cs b/SaxoniteCoreWebOnline/Online/Controllers/ActivityController-Ajax.cs index 0ae7747..d65f303 100644 --- a/SaxoniteCoreWebOnline/Online/Controllers/ActivityController-Ajax.cs +++ b/SaxoniteCoreWebOnline/Online/Controllers/ActivityController-Ajax.cs @@ -173,6 +173,9 @@ namespace Saxonite.Net.Core.Web.Online.Controllers SysId userAccountId = UserAccountId; SaxActivityConsole.IndexContext indexContext = new SaxActivityConsole.IndexContext(userAccountId); + // Period. + indexContext.EffectiveTo = DateTime.Today; + // Regions. if (SysId.IsValid(sessionData.User.RegionIdProvince)) indexContext.RegionIds.Add(sessionData.User.RegionIdProvince); diff --git a/SaxoniteCoreWebOnline/Online/Controllers/ArticleController-Ajax.cs b/SaxoniteCoreWebOnline/Online/Controllers/ArticleController-Ajax.cs index 6066df2..f38cf2b 100644 --- a/SaxoniteCoreWebOnline/Online/Controllers/ArticleController-Ajax.cs +++ b/SaxoniteCoreWebOnline/Online/Controllers/ArticleController-Ajax.cs @@ -67,7 +67,8 @@ namespace Saxonite.Net.Core.Web.Online.Controllers { public String RegionIdPrimary { get; set; } public String SportIdPrimary { get; set; } - public bool Published { get; set; } + public bool? Published { get; set; } = null; + public bool? PublishedInFuture { get; set; } = null; public String PublishedOn_YYYYMMDD { get; set; } public String PublishedOn_Calendar { get; set; } public ArticleContentData Content { get; } = new ArticleContentData(); @@ -90,7 +91,8 @@ namespace Saxonite.Net.Core.Web.Online.Controllers PopulateArticleResponseData(model, article, data as ArticleData_List); data.RegionIdPrimary = model.GetEncryptedRegionId(article.RegionIdPrimary); data.SportIdPrimary = model.GetEncryptedSportId(article.SportIdPrimary); - data.Published = DateUtil.IsValid(article.PublishedAt); + data.Published = DateUtil.IsValid(article.PublishedAt) ? true : (bool?)null; + data.PublishedInFuture = (data.Published == true && article.PublishedAt.CompareTo(DateTime.Today) >= 0) ? true : (bool?)null; data.PublishedOn_YYYYMMDD = DateUtil.Format(article.PublishedAt, "yyyyMMdd"); data.PublishedOn_Calendar = model.FormatDateForForm(article.PublishedAt); @@ -126,6 +128,8 @@ namespace Saxonite.Net.Core.Web.Online.Controllers public List Articles { get; } = new List(); /// Organisations that the user has rights to author articles for. public List Organisations { get; set; } + public ItemIdName defaultSport { get; set; } + public ItemIdName defaultRegion { get; set; } } //------------------------------------------------------------------- [HttpPost] @@ -144,88 +148,120 @@ namespace Saxonite.Net.Core.Web.Online.Controllers SysId userAccountId = UserAccountId; RefreshModel(model); - // Organisations that the user has rights to author articles for. - foreach (SaxAccountMgr.OrganisationIdName organisation in AccountMgr.GetOrganisationsForAnyUserRole(userAccountId, new [] { Sax.OrganisationRole.Admin, Sax.OrganisationRole.ArticleAdmin })) + // Record disposable items as they are created so all can be disposed when needed. + using (DisposableCollection disposableItems = new DisposableCollection()) { - if (responseData.Organisations == null) responseData.Organisations = new List(); - OrganisationData organisationData = new OrganisationData() + // User account. + SaxUserAccount userAccount = AccountMgr.GetUserAccountById(UserAccountId); + disposableItems.Add(userAccount); + + // Organisations that the user has rights to author articles for. + foreach (SaxAccountMgr.OrganisationIdName articleOrganisation in AccountMgr.GetOrganisationsForAnyUserRole(userAccountId, new[] { Sax.OrganisationRole.Admin, Sax.OrganisationRole.ArticleAdmin })) { - Id = model.EncryptId(organisation.Id), - Name = organisation.Name - }; - responseData.Organisations.Add(organisationData); - } - - // User must either be a journalist or allowed to author organisation articles. - if (!UserIsJournalist && responseData.Organisations == null) return GetAjaxResult_GoHome(responseData); - if (UserIsJournalist && responseData.Organisations != null) - { - OrganisationData organisationData = new OrganisationData() - { - Id = "", - Name = "No Organisation - Journalist Articles" - }; - responseData.Organisations.Insert(0, organisationData); - } - - // Filter organisation. - SysId organisationIdFilter = model.ExtractSysId(model.Filter.OrganisationId); - - // Item filter. - SaxArticle.Params prmArticle = new SaxArticle.Params(); - prmArticle.UserAccountIdsAuthorIncluded.Add(sessionData.User.UserAccountId); - if (SysId.IsValid(organisationIdFilter)) - prmArticle.OrganisationIdsIncluded.Add(organisationIdFilter); - prmArticle.ArticleFilter = model.Filter.Article; - prmArticle.StatusCat = SaxArticle.StatusCat.NotDeleted; - model.Paging.ItemTotal = (ulong)ActivityMgr.GetArticleCount(prmArticle); - model.PopulatePagingTo(prmArticle.ResultsPaging); - - // Item columns. - ColumnDfn colId = ActivityMgr.GetArticleColumnDfn(SaxArticle.Column.Id); - ColumnDfn colOrganisationId = ActivityMgr.GetArticleColumnDfn(SaxArticle.Column.OrganisationId); - ColumnDfn colHeadline = ActivityMgr.GetArticleColumnDfn(SaxArticle.Column.Headline); - ColumnDfn colSummary = ActivityMgr.GetArticleColumnDfn(SaxArticle.Column.Summary); - ColumnDfn colRegionIdPrimary = ActivityMgr.GetArticleColumnDfn(SaxArticle.Column.RegionIdPrimary); - ColumnDfn colSportIdPrimary = ActivityMgr.GetArticleColumnDfn(SaxArticle.Column.SportIdPrimary); - ColumnDfn colPublishedOn = ActivityMgr.GetArticleColumnDfn(SaxArticle.Column.PublishedAt_Date); - ColumnDfn colStatus = ActivityMgr.GetArticleColumnDfn(SaxArticle.Column.Status); - ColumnDfn colCreatedOn = ActivityMgr.GetArticleColumnDfn(SaxArticle.Column.CreateOn_Date); - List columns = new List() - { - colId, colOrganisationId, colHeadline, colSummary, - colRegionIdPrimary, colSportIdPrimary, colPublishedOn, - colStatus, colCreatedOn - }; - - // Sort columns. - colCreatedOn.SortAscending = false; - prmArticle.SortColumns.Add(colCreatedOn); - - // Query items. - using (DbResultSet resultSet = ActivityMgr.ExecuteArticleQuery(columns, prmArticle)) - { - while (resultSet != null && resultSet.HasRows && resultSet.NextRecord()) - { - NamedValue.Map values = resultSet.GetRecordValues(); - ArticleData_List articleData = new ArticleData_List() + if (responseData.Organisations == null) responseData.Organisations = new List(); + OrganisationData organisationData = new OrganisationData() { - Id = values.GetString(colId.DataName), - OrganisationId = values.GetString(colOrganisationId.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)) + Id = model.EncryptId(articleOrganisation.Id), + Name = articleOrganisation.Name }; - responseData.Articles.Add(articleData); + responseData.Organisations.Add(organisationData); } + + // User must either be a journalist or allowed to author organisation articles. + if (!UserIsJournalist && responseData.Organisations == null) return GetAjaxResult_GoHome(responseData); + if (UserIsJournalist && responseData.Organisations != null) + { + OrganisationData organisationData = new OrganisationData() + { + Id = "", + Name = "No Organisation - Journalist Articles" + }; + responseData.Organisations.Insert(0, organisationData); + } + + // Filter organisation. + SysId organisationIdFilter = model.ExtractSysId(model.Filter.OrganisationId); + SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationIdFilter); + disposableItems.Add(organisation); + + // Default Sport - defined by organisation if selected. + SysId defaultSportId = organisation?.SportIdPrimary ?? null; + if (SysId.IsValid(defaultSportId)) + { + responseData.defaultSport = new AjaxResponse_Data.ItemIdName() + { + Id = model.GetEncryptedSportId(defaultSportId), + Name = model.GetSportCaption(defaultSportId) + }; + } + + // Default region - defined by organisation (if selected) otherwise user themselves. + SysId defaultRegionId = organisation?.RegionIdPrimary ?? userAccount?.RegionIdCountry ?? null; + if (SysId.IsValid(defaultRegionId)) + { + responseData.defaultRegion = new AjaxResponse_Data.ItemIdName() + { + Id = model.GetEncryptedRegionId(defaultRegionId), + Name = model.GetRegionCaption(defaultRegionId) + }; + } + + // Item filter. + SaxArticle.Params prmArticle = new SaxArticle.Params(); + prmArticle.UserAccountIdsAuthorIncluded.Add(sessionData.User.UserAccountId); + if (SysId.IsValid(organisationIdFilter)) + prmArticle.OrganisationIdsIncluded.Add(organisationIdFilter); + prmArticle.ArticleFilter = model.Filter.Article; + prmArticle.StatusCat = SaxArticle.StatusCat.NotDeleted; + model.Paging.ItemTotal = (ulong)ActivityMgr.GetArticleCount(prmArticle); + model.PopulatePagingTo(prmArticle.ResultsPaging); + + // Item columns. + ColumnDfn colId = ActivityMgr.GetArticleColumnDfn(SaxArticle.Column.Id); + ColumnDfn colOrganisationId = ActivityMgr.GetArticleColumnDfn(SaxArticle.Column.OrganisationId); + ColumnDfn colHeadline = ActivityMgr.GetArticleColumnDfn(SaxArticle.Column.Headline); + ColumnDfn colSummary = ActivityMgr.GetArticleColumnDfn(SaxArticle.Column.Summary); + ColumnDfn colRegionIdPrimary = ActivityMgr.GetArticleColumnDfn(SaxArticle.Column.RegionIdPrimary); + ColumnDfn colSportIdPrimary = ActivityMgr.GetArticleColumnDfn(SaxArticle.Column.SportIdPrimary); + ColumnDfn colPublishedOn = ActivityMgr.GetArticleColumnDfn(SaxArticle.Column.PublishedAt_Date); + ColumnDfn colStatus = ActivityMgr.GetArticleColumnDfn(SaxArticle.Column.Status); + ColumnDfn colCreatedOn = ActivityMgr.GetArticleColumnDfn(SaxArticle.Column.CreateOn_Date); + List columns = new List() + { + colId, colOrganisationId, colHeadline, colSummary, + colRegionIdPrimary, colSportIdPrimary, colPublishedOn, + colStatus, colCreatedOn + }; + + // Sort columns. + colCreatedOn.SortAscending = false; + prmArticle.SortColumns.Add(colCreatedOn); + + // Query items. + using (DbResultSet resultSet = ActivityMgr.ExecuteArticleQuery(columns, prmArticle)) + { + while (resultSet != null && resultSet.HasRows && resultSet.NextRecord()) + { + NamedValue.Map values = resultSet.GetRecordValues(); + ArticleData_List articleData = new ArticleData_List() + { + Id = values.GetString(colId.DataName), + OrganisationId = values.GetString(colOrganisationId.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); + } + } + if (model.Paging.IsPaged) + model.Paging.ItemCount = (ushort)responseData.Articles.Count; } - if (model.Paging.IsPaged) - model.Paging.ItemCount = (ushort)responseData.Articles.Count; // Complete response. responseData.SetPaging(model); @@ -260,39 +296,46 @@ namespace Saxonite.Net.Core.Web.Online.Controllers SysId userAccountId = UserAccountId; RefreshModel(model); - // Identify item. - SysId articleId = model.ExtractSysId(model.ArticleId); - if (!SysId.IsValid(articleId)) - return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.specified"])); - - // Retrieve item. - SaxArticle article = ActivityMgr.GetArticleById(articleId); - if (article == null) + // Record disposable items as they are created so all can be disposed when needed. + using (DisposableCollection disposableItems = new DisposableCollection()) { - SaLogger.Warn(fn + " Failed to retrieve Article for Id: '" + model.ArticleId + "'"); - return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"])); - } - if (!article.UserAccountIdAuthor.Equals(userAccountId)) - { - SaLogger.Warn(fn + " Inconsistent Article Author. Expected: " + userAccountId + ". Found: " + article.UserAccountIdAuthor); - return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"])); - } - // Delete upload images. - SaxItemImage.Params prmItemImage = new SaxItemImage.Params(); - prmItemImage.ItemIdsIncluded.Add(new ItemId((short)Sax.DataItemType.Article, articleId)); - prmItemImage.ImageTypesIncluded.Add(Sax.ImageType.ArticleUpload); - prmItemImage.StatusCat = SaxItemImage.StatusCat.NotDeleted; - foreach (SaxItemImage itemImage in ItemDataMgr.GetItemImageList(prmItemImage)) - { - itemImage.SessionUid = 0; - itemImage.ImageUid = 0; - itemImage.Status = SaStatus.Deleted; - ItemDataMgr.UpdateItemImage(itemImage, DbUpdateInfo); - } + // Identify item. + SysId articleId = model.ExtractSysId(model.ArticleId); + if (!SysId.IsValid(articleId)) + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.specified"])); - // Construct response. - PopulateArticleResponseData(model, article, responseData.Article); + // Retrieve item. + SaxArticle article = ActivityMgr.GetArticleById(articleId); + disposableItems.Add(article); + if (article == null) + { + SaLogger.Warn(fn + " Failed to retrieve Article for Id: '" + model.ArticleId + "'"); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"])); + } + if (!article.UserAccountIdAuthor.Equals(userAccountId)) + { + SaLogger.Warn(fn + " Inconsistent Article Author. Expected: " + userAccountId + ". Found: " + article.UserAccountIdAuthor); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"])); + } + + // Delete upload images. + SaxItemImage.Params prmItemImage = new SaxItemImage.Params(); + prmItemImage.ItemIdsIncluded.Add(new ItemId((short)Sax.DataItemType.Article, articleId)); + prmItemImage.ImageTypesIncluded.Add(Sax.ImageType.ArticleUpload); + prmItemImage.StatusCat = SaxItemImage.StatusCat.NotDeleted; + foreach (SaxItemImage itemImage in ItemDataMgr.GetItemImageList(prmItemImage)) + { + disposableItems.Add(itemImage); + itemImage.SessionUid = 0; + itemImage.ImageUid = 0; + itemImage.Status = SaStatus.Deleted; + ItemDataMgr.UpdateItemImage(itemImage, DbUpdateInfo); + } + + // Construct response. + PopulateArticleResponseData(model, article, responseData.Article); + } responseData.Success = true; return Json(responseData); } @@ -311,9 +354,9 @@ namespace Saxonite.Net.Core.Web.Online.Controllers // Ajax request. [HttpPost] [Route("Article/Ajax/UploadImage")] - public IActionResult UploadArticleImage(Model_Article model) + public IActionResult Ajax_UploadArticleImage(Model_Article model) { - String fn = GetType().Name + ".UploadArticleImage"; + String fn = GetType().Name + ".Ajax_UploadArticleImage"; AjaxResponse_UploadArticleImage responseData = new AjaxResponse_UploadArticleImage(model); String messageTitle = Localiser["article.caption.article"]; @@ -323,85 +366,91 @@ namespace Saxonite.Net.Core.Web.Online.Controllers SaxSessionData_Online sessionData = SessionData_Online; RefreshModel(model); - // Validate number of images. - if (model.ArticleImage.IsEmpty()) + // Record disposable items as they are created so all can be disposed when needed. + using (DisposableCollection disposableItems = new DisposableCollection()) { - SaLogger.Warn(fn + " No image file supplied for upload"); - return Json(responseData); - } - if (model.ArticleImage.Count != 1) - { - SaLogger.Warn(fn + " Invalid number of image files uploaded: " + model.ArticleImage.Count); - return Json(responseData.SetControlError("ArticleImage", Localiser["msg.upload.image.single"])); - } - IFormFile file = model.ArticleImage[0]; - // Image properties. - byte[] imageData = null; - using (MemoryStream fileStream = new MemoryStream()) - { - file.CopyTo(fileStream); - imageData = fileStream.ToArray(); - } - - // Validate the image. - ImageUtils.ImageConfig imageConfig = ImageUtils.VerifyImageData(imageData); - if (!imageConfig.ValidImage) - return Json(responseData.SetControlError("ArticleImage", Localiser["msg.upload.image.invalid"])); - - // Image belongs to the article. - // If the article doesn't yet exist, it will need to be uploaded to the user first and later transferred. - SysId articleId = model.ExtractSysId(model.ArticleId); - ItemId articleItemId = new ItemId((short)Sax.DataItemType.Article, articleId); - ItemId userItemId = new ItemId((short)Sax.DataItemType.UserAccount, sessionData.User.UserAccountId); - ItemId imageItemId = SysId.IsValid(articleId) ? articleItemId : userItemId; - - // Store uploaded image as 'Upload' - awaiting confirmation that the article details are to be saved. - // NB Reuse any existing deleted article upload image to avoid creating new images unless necessary. - SaxItemImage.Params prmItemImage = new SaxItemImage.Params(); - prmItemImage.ItemIdsIncluded.Add(imageItemId); - prmItemImage.ImageTypesIncluded.Add(Sax.ImageType.Article); - prmItemImage.ImageTypesIncluded.Add(Sax.ImageType.ArticleUpload); - prmItemImage.StatusCat = SaxItemImage.StatusCat.DeletedOnly; - prmItemImage.ResultsPaging.PageLength = 1; - prmItemImage.ResultsPaging.PageNo = 1; - List itemImages = ItemDataMgr.GetItemImageList(prmItemImage); - SaxItemImage itemImage = !itemImages.IsEmpty() ? itemImages[0] : null; - if (itemImage == null) - itemImage = ItemDataMgr.CreateItemImage(imageItemId, Sax.ImageType.ArticleUpload); - itemImage.ImageType = Sax.ImageType.ArticleUpload; - itemImage.Filename = file.FileName; - itemImage.Mime = SacsContentTypeHelper.GetMimeTypeFromContentType(file.ContentType).ToString().ToLower(); - itemImage.ContentType = file.ContentType; - itemImage.Width = imageConfig.Width; - itemImage.Height = imageConfig.Height; - itemImage.ImageData = imageData; - itemImage.SessionUid = sessionData.SessionIdx; - itemImage.ImageUid = sessionData.NextItemIdx; - itemImage.Status = SaxItemImage.DefaultStatus; - - if (!SysId.IsValid(itemImage.Id)) - { - if (!ItemDataMgr.InsertItemImage(itemImage, DbUpdateInfo)) + // Validate number of images. + if (model.ArticleImage.IsEmpty()) { - SaLogger.Warn(fn + " Failed to insert uploaded image - " + ItemDataMgr.ErrorStateMessage); - return Json(responseData.SetControlError("ArticleImage", Localiser["msg.error.item.insert.failed"])); + SaLogger.Warn(fn + " No image file supplied for upload"); + return Json(responseData); } - } - else - { - if (!ItemDataMgr.UpdateItemImage(itemImage, DbUpdateInfo)) + if (model.ArticleImage.Count != 1) { - SaLogger.Warn(fn + " Failed to update uploaded image - " + ItemDataMgr.ErrorStateMessage); - return Json(responseData.SetControlError("ArticleImage", Localiser["msg.error.item.insert.failed"])); + SaLogger.Warn(fn + " Invalid number of image files uploaded: " + model.ArticleImage.Count); + return Json(responseData.SetControlError("ArticleImage", Localiser["msg.upload.image.single"])); } - } + IFormFile file = model.ArticleImage[0]; - // Segment image info. - responseData.Segment = new ArticleContentSegmentData(); - responseData.Segment.Image = new ArticleContentSegmentImageData(); - responseData.Segment.Image.ImageLink = GetArticleImageLink(itemImage.Id); - responseData.Segment.Image.ImageIdx = itemImage.ImageUid; + // Image properties. + byte[] imageData = null; + using (MemoryStream fileStream = new MemoryStream()) + { + file.CopyTo(fileStream); + imageData = fileStream.ToArray(); + } + + // Validate the image. + ImageUtils.ImageConfig imageConfig = ImageUtils.VerifyImageData(imageData); + if (!imageConfig.ValidImage) + return Json(responseData.SetControlError("ArticleImage", Localiser["msg.upload.image.invalid"])); + + // Image belongs to the article. + // If the article doesn't yet exist, it will need to be uploaded to the user first and later transferred. + SysId articleId = model.ExtractSysId(model.ArticleId); + ItemId articleItemId = new ItemId((short)Sax.DataItemType.Article, articleId); + ItemId userItemId = new ItemId((short)Sax.DataItemType.UserAccount, sessionData.User.UserAccountId); + ItemId imageItemId = SysId.IsValid(articleId) ? articleItemId : userItemId; + + // Store uploaded image as 'Upload' - awaiting confirmation that the article details are to be saved. + // NB Reuse any existing deleted article upload image to avoid creating new images unless necessary. + SaxItemImage.Params prmItemImage = new SaxItemImage.Params(); + prmItemImage.ItemIdsIncluded.Add(imageItemId); + prmItemImage.ImageTypesIncluded.Add(Sax.ImageType.Article); + prmItemImage.ImageTypesIncluded.Add(Sax.ImageType.ArticleUpload); + prmItemImage.StatusCat = SaxItemImage.StatusCat.DeletedOnly; + prmItemImage.ResultsPaging.PageLength = 1; + prmItemImage.ResultsPaging.PageNo = 1; + List itemImages = ItemDataMgr.GetItemImageList(prmItemImage); + SaxItemImage itemImage = !itemImages.IsEmpty() ? itemImages[0] : null; + if (itemImage == null) + itemImage = ItemDataMgr.CreateItemImage(imageItemId, Sax.ImageType.ArticleUpload); + disposableItems.Add(itemImage); + itemImage.ImageType = Sax.ImageType.ArticleUpload; + itemImage.Filename = file.FileName; + itemImage.Mime = SacsContentTypeHelper.GetMimeTypeFromContentType(file.ContentType).ToString().ToLower(); + itemImage.ContentType = file.ContentType; + itemImage.Width = imageConfig.Width; + itemImage.Height = imageConfig.Height; + itemImage.ImageData = imageData; + itemImage.SessionUid = sessionData.SessionIdx; + itemImage.ImageUid = sessionData.NextItemIdx; + itemImage.Status = SaxItemImage.DefaultStatus; + + if (!SysId.IsValid(itemImage.Id)) + { + if (!ItemDataMgr.InsertItemImage(itemImage, DbUpdateInfo)) + { + SaLogger.Warn(fn + " Failed to insert uploaded image - " + ItemDataMgr.ErrorStateMessage); + return Json(responseData.SetControlError("ArticleImage", Localiser["msg.error.item.insert.failed"])); + } + } + else + { + if (!ItemDataMgr.UpdateItemImage(itemImage, DbUpdateInfo)) + { + SaLogger.Warn(fn + " Failed to update uploaded image - " + ItemDataMgr.ErrorStateMessage); + return Json(responseData.SetControlError("ArticleImage", Localiser["msg.error.item.insert.failed"])); + } + } + + // Segment image info. + responseData.Segment = new ArticleContentSegmentData(); + responseData.Segment.Image = new ArticleContentSegmentImageData(); + responseData.Segment.Image.ImageLink = GetArticleImageLink(itemImage.Id); + responseData.Segment.Image.ImageIdx = itemImage.ImageUid; + } // Complete response. //responseData.SetNotification(messageTitle, "Image uploaded."); @@ -450,7 +499,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers if (SysId.IsValid(organisationId)) { // Organisation specified - User must have Article Admin role. - if (!AccountMgr.UserHasAnyOrganisationRole(userAccountId, organisationId, new [] { Sax.OrganisationRole.Admin, Sax.OrganisationRole.ArticleAdmin })) + if (!AccountMgr.UserHasAnyOrganisationRole(userAccountId, organisationId, new[] { Sax.OrganisationRole.Admin, Sax.OrganisationRole.ArticleAdmin })) { SaLogger.Warn(fn + " User does not have Article Admin role for organisation." + GetLogSuffix()); return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); @@ -515,8 +564,13 @@ namespace Saxonite.Net.Core.Web.Online.Controllers article.Headline = model.ArticleHeadline; if (model.ArticleSummary != null) article.Summary = model.ArticleSummary; - if (model.ArticlePublish) - article.PublishedAt = model.GetArticlePublishOn(); + if (!model.ArticlePublished.ToBool()) + { + if (model.ArticlePublish) + article.PublishedAt = model.GetArticlePublishOn(); + else + article.PublishedAt = DateTime.MinValue; + } if (model.ArticleStatusCode != null) article.Status = model.ArticleStatus; diff --git a/SaxoniteCoreWebOnline/Online/Models/Model_Article.cs b/SaxoniteCoreWebOnline/Online/Models/Model_Article.cs index e599b82..32f7037 100644 --- a/SaxoniteCoreWebOnline/Online/Models/Model_Article.cs +++ b/SaxoniteCoreWebOnline/Online/Models/Model_Article.cs @@ -73,6 +73,7 @@ namespace Saxonite.Net.Core.Web.Online.Models // Article images. public List ArticleImage { get; set; } // Article publication. + public String ArticlePublished { get; set; } public bool ArticlePublish { get; set; } public String ArticlePublishOn { get; set; } public DateTime GetArticlePublishOn() @@ -123,7 +124,8 @@ namespace Saxonite.Net.Core.Web.Online.Models } // Article publication. - if (ArticlePublish) + // Only relevant when the article hasn't yet been published and user has selected 'Publish'. + if (!ArticlePublished.ToBool() && ArticlePublish) { String controlName = "ArticlePublication"; DateTime publishedOn = GetArticlePublishOn(); diff --git a/SaxoniteCoreWebOnline/Views/Article/Index.cshtml b/SaxoniteCoreWebOnline/Views/Article/Index.cshtml index 166bac3..940bd20 100644 --- a/SaxoniteCoreWebOnline/Views/Article/Index.cshtml +++ b/SaxoniteCoreWebOnline/Views/Article/Index.cshtml @@ -7,7 +7,7 @@
diff --git a/SaxoniteCoreWebOnline/Views/Article/Panel-ArticleDialog.cshtml b/SaxoniteCoreWebOnline/Views/Article/Panel-ArticleDialog.cshtml index 558f57b..62f079c 100644 --- a/SaxoniteCoreWebOnline/Views/Article/Panel-ArticleDialog.cshtml +++ b/SaxoniteCoreWebOnline/Views/Article/Panel-ArticleDialog.cshtml @@ -118,6 +118,7 @@
+ @Html.HiddenFor(model => model.ArticlePublished, new { @id = "ifArticlePublished" })
@Html.CheckBoxFor(model => model.ArticlePublish, new { @id = "chkArticlePublish", @class = "ArticlePublish" }) @Localiser["label.publish"]
@Html.TextBoxFor(model => model.ArticlePublishOn, new { @id = "ifArticlePublishOn", @class = "sax-input sax-calendar ArticlePublishOn", @readonly = "readonly" })
diff --git a/SaxoniteCoreWebOnline/wwwroot/js/sax-art.js b/SaxoniteCoreWebOnline/wwwroot/js/sax-art.js index 668e685..2592b40 100644 --- a/SaxoniteCoreWebOnline/wwwroot/js/sax-art.js +++ b/SaxoniteCoreWebOnline/wwwroot/js/sax-art.js @@ -32,6 +32,8 @@ var $items = $('.sax-panel-item-list', $pnl.results).hide().empty(); if (!response.success) return; sax.data.articleOrganisations = response.organisations; + sax.data.defaultSport = response.defaultSport; + sax.data.defaultRegion = response.defaultRegion; sax.refreshPaging(response, '#frmArticleFilter', sax.refreshResults); // Menu. let $mnu = $('.sax-burger ul.sax-dropdown-menu', $pnl.filter).empty(); @@ -129,6 +131,17 @@ sax.createItem = function () { sax.clearItem(); + let $frmA = $frm.article; + if (sax.data.defaultSport) + { + $('#ifArticleSportId', $frmA).val(sax.data.defaultSport.id); + $('#ifArticleSportCaption', $frmA).val(sax.data.defaultSport.name); + } + if (sax.data.defaultRegion) + { + $('#ifArticleRegionId', $frmA).val(sax.data.defaultRegion.id); + $('#ifArticleRegionCaption', $frmA).val(sax.data.defaultRegion.name); + } sax.populateArticleContent({}); sax.openArticleDialog(sax.dlg.title.createArticle); } @@ -136,7 +149,7 @@ { let $frmF = $frm.filter; let $frmA = $frm.article; - $('#ifArticleId,#ifArticleExists,#ifArticleOrganisationId,#ifArticleRegionId,#ifArticleSportId,input[type="text"],textarea,select', $frmA).val(''); + $('#ifArticleId,#ifArticleExists,#ifArticleOrganisationId,#ifArticleRegionId,#ifArticleSportId,#ifArticlePublish,input[type="text"],textarea,select', $frmA).val(''); let orgId = $('#ifFilterOrganisationId', $frmF).val(); $('#ifArticleOrganisationId', $frmA).val(orgId); $('#ifArticleOrganisation', $frmA).val($('#ifFilterOrganisation', $frmF).val()); @@ -177,10 +190,12 @@ $('#ifArticleRegionCaption', $frmA).val(item.regionCaptionPrimary); $('#ifArticleSportId', $frmA).val(item.sportIdPrimary); $('#ifArticleSportCaption', $frmA).val(item.sportCaptionPrimary); - if (item.published) - $('span.ArticlePublishedOn', $frmA).text(item.publishedOnFmt); - $('div.ArticlePublish', $frmA).toggle(!item.published); - $('div.ArticlePublished', $frmA).toggle(!!item.published); + $('#ifArticlePublished', $frmA).val(item.published && !item.publishedInFuture ? 1 : 0); + $('#chkArticlePublish', $frmA).prop("checked", item.published); + $('.sax-input.sax-calendar.ArticlePublishOn', $frmA).datepicker('setDate', item.published ? item.publishedOn_Calendar : 'today'); + $('span.ArticlePublishedOn', $frmA).text(item.publishedOnFmt); + $('div.ArticlePublish', $frmA).toggle(!item.published || item.publishedInFuture); + $('div.ArticlePublished', $frmA).toggle(!!item.published && !item.publishedInFuture); $('#cbArticleStatusCode', $frmA).val(item.statusCode); sax.populateArticleContent(item.content); sax.openArticleDialog(sax.dlg.title.updateArticle); @@ -453,7 +468,7 @@ $(function () { $pnl.filter = $('.sax-panel-filter'); - $pnl.results= $('.sax-panel-results'); + $pnl.results = $('.sax-panel-results'); $frm.filter = $('#frmArticleFilter'); $('.sax-action.sax-item-create', $pnl.filter).on('click', function () { sax.createItem(); }); @@ -475,7 +490,7 @@ ac.$content = $('.sax-article-panel.sax-article-content'); ac.$segList = $('.sax-article-segment-list', ac.$content); - sax.initCalendars('.sax-input.sax-calendar').datepicker('option', { maxDate: '+' + (sax.maxPublishDaysAhead||'0') }); + sax.initCalendars('.sax-input.sax-calendar', { minDate: '-' + (sax.articlePublish.maxDaysBefore || '7'), maxDate: '+' + (sax.articlePublish.maxDaysAhead || '7') }); var opt = sax.dlgOpts(); opt.buttons[sax.dlg.btn.ok] = function () { sax.uploadArticleImage(); }; diff --git a/SaxoniteCoreWebOnline/wwwroot/js/sax-home.js b/SaxoniteCoreWebOnline/wwwroot/js/sax-home.js index 920f73d..7c40189 100644 --- a/SaxoniteCoreWebOnline/wwwroot/js/sax-home.js +++ b/SaxoniteCoreWebOnline/wwwroot/js/sax-home.js @@ -145,7 +145,7 @@ } sax.setActivityReaction = function (r) { - sax.debug('setActivityReaction', r, $('.sax-dialog-activity button.ActivityReaction' + r)); + //sax.debug('setActivityReaction', r, $('.sax-dialog-activity button.ActivityReaction' + r)); if ($('.sax-dialog-activity button.ActivityReaction' + r).hasClass(selectedClass)) r = 0; sax.clearActivityReactionButton(); let fn = function (response) diff --git a/SaxoniteCoreWebOnline/wwwroot/js/sax-org-evt.js b/SaxoniteCoreWebOnline/wwwroot/js/sax-org-evt.js index 6b2c422..fecf73a 100644 --- a/SaxoniteCoreWebOnline/wwwroot/js/sax-org-evt.js +++ b/SaxoniteCoreWebOnline/wwwroot/js/sax-org-evt.js @@ -253,7 +253,7 @@ $frm.evtId = $('#frmEventId'); $frm.evt = $('#frmEvent'); $('.sax-input', $frm.evt).on('input', function () { sax.clearControlError(this); }); - sax.initCalendars('#frmEvent .sax-input.sax-calendar').datepicker('option', { maxDate: '+700' }); + sax.initCalendars('#frmEvent .sax-input.sax-calendar', { maxDate: '+700' }); $frm.evt.on('dblclick', 'input.EventSportCaptionPrimary', oe.selectSportPrimary).on('click', 'button.SelectEventSportPrimary', oe.selectSportPrimary); $frm.evt.on('dblclick', 'input.EventRegionCaptionCountry', oe.selectRegionCountry).on('click', 'button.SelectEventRegionCountry', oe.selectRegionCountry); diff --git a/SaxoniteStandardBase/Base/App/Sax-Config.cs b/SaxoniteStandardBase/Base/App/Sax-Config.cs index 9992d1c..73d913f 100644 --- a/SaxoniteStandardBase/Base/App/Sax-Config.cs +++ b/SaxoniteStandardBase/Base/App/Sax-Config.cs @@ -75,7 +75,8 @@ namespace Saxonite.Net.Standard.Base.App } public static class Articles { - public static ushort PublishMaxDaysAhead { get; } = 14; + public static ushort PublishMaxDaysBefore { get; } = 7; + public static ushort PublishMaxDaysAhead { get; } = 10; } public static class Search { diff --git a/SaxoniteStandardBase/Base/App/Sax-Version.cs b/SaxoniteStandardBase/Base/App/Sax-Version.cs index 65d1c1c..4b72964 100644 --- a/SaxoniteStandardBase/Base/App/Sax-Version.cs +++ b/SaxoniteStandardBase/Base/App/Sax-Version.cs @@ -18,7 +18,7 @@ namespace Saxonite.Net.Standard.Base.App //------------------------------------------------------------------- public static class Version { - private static String version { get; } = "1.2.3.1"; + private static String version { get; } = "1.2.3.2"; // Retrieve version. public static String GetFullVersion () { return version; } public static String GetAppVersion () diff --git a/SaxoniteStandardBase/Base/Consoles/SaxActivityConsole.cs b/SaxoniteStandardBase/Base/Consoles/SaxActivityConsole.cs index c3a545c..f3d0a63 100644 --- a/SaxoniteStandardBase/Base/Consoles/SaxActivityConsole.cs +++ b/SaxoniteStandardBase/Base/Consoles/SaxActivityConsole.cs @@ -25,16 +25,16 @@ namespace Saxonite.Net.Standard.Base.Consoles public class IndexContext { // ToDo Include Localiser. - public IndexContext (SysId userAccountId) + public IndexContext(SysId userAccountId) { UserAccountId = userAccountId; } public SysId UserAccountId { get; } public List SportIds { get; } = new List(); public List RegionIds { get; } = new List(); - // Default period is ~12 months ago up to ~3 months in the future. - public DateTime EffectiveFrom { get; } = DateUtil.IncrementDate(DateTime.Today, -365); - public DateTime EffectiveTo { get; } = DateUtil.IncrementDate(DateTime.Today, 30); + // Todo: Default period is ~12 months ago up to ~3 months in the future. + public DateTime EffectiveFrom { get; set; } = DateUtil.IncrementDate(DateTime.Today, -365); + public DateTime EffectiveTo { get; set; } = DateUtil.IncrementDate(DateTime.Today, 30); public bool IsValid() { // Context must contain at least a sport, a region and a valid effective period.