From 833798c9f8b49fb6e741b1ce8db5ce07358a7b3f Mon Sep 17 00:00:00 2001 From: Andy Howard Date: Sun, 29 May 2022 13:45:22 +0100 Subject: [PATCH] v1.2.6.4 --- .../Admin/App/SaxSessionData_Admin_Tester.cs | 43 +- .../App/SaxSessionData_Online_Tester.cs | 7 + .../Base/Controllers/SaxController_Base.cs | 5 +- SaxoniteCoreWeb/Base/Models/SaxModel_Base.cs | 4 +- .../Admin/App/SaxSessionData_Admin.cs | 18 + .../Admin/Controllers/ImageController.cs | 214 +++++++++ .../OrganisationController-Ajax.cs | 107 +++-- .../OrganisationController-Images-Ajax.cs | 308 +++++++++++++ .../Controllers/OrganisationController.cs | 13 +- .../Controllers/RegionController-Ajax.cs | 8 +- .../Admin/Controllers/SaxController_Admin.cs | 35 ++ .../Admin/Controllers/UserController-Ajax.cs | 6 + .../Admin/Controllers/UserController.cs | 13 +- .../Admin/Models/ModelData_Address.cs | 35 +- .../Admin/Models/Model_Organisation.cs | 26 +- .../Admin/Models/Model_User.cs | 2 + .../Localisation/SaxAdminProperties.resx | 51 +++ .../Views/Common/ContactMethods.cshtml | 2 +- .../Views/Common/Controls-ItemAddress.cshtml | 19 + .../Common/Controls-ItemContactMethods.cshtml | 2 +- .../Views/Organisation/Index.cshtml | 43 +- .../Panel-OrganisationAddressDialog.cshtml | 7 +- .../Panel-OrganisationDialog.cshtml | 16 +- .../Panel-OrganisationImagesDialog.cshtml | 125 ++++++ SaxoniteCoreWebAdmin/Views/User/Index.cshtml | 4 +- SaxoniteCoreWebAdmin/wwwroot/css/sax-data.css | 12 +- .../img/animated/progress-basketball-32.gif | Bin 0 -> 21788 bytes .../img/animated/progress-basketball-64.gif | Bin 0 -> 41296 bytes .../wwwroot/img/animated/progress-cog-32.gif | Bin 0 -> 18838 bytes .../wwwroot/img/animated/progress-cog-64.gif | Bin 0 -> 39529 bytes SaxoniteCoreWebAdmin/wwwroot/js/sax-main.js | 5 + .../wwwroot/js/sax-org-add.js | 111 +++++ .../wwwroot/js/sax-org-img.js | 143 ++++++ SaxoniteCoreWebAdmin/wwwroot/js/sax-org.js | 86 +--- .../AccountController-Ajax-Contact.cs | 415 +++++++++--------- .../AccountController-Ajax-Organisations.cs | 40 +- .../AccountController-Ajax-Sports.cs | 24 +- ...ountController-Ajax-UserSportExperience.cs | 38 +- .../Controllers/ActivityController-Ajax.cs | 23 +- .../Controllers/ArticleController-Ajax.cs | 40 +- .../Online/Controllers/ImageController.cs | 77 +++- .../OrganisationController-Ajax-Events.cs | 20 +- .../OrganisationController-Ajax-Members.cs | 30 +- ...ganisationController-Ajax-Opportunities.cs | 20 +- .../OrganisationController-Ajax-Request.cs | 7 +- .../OrganisationController-Ajax.cs | 31 +- .../Controllers/SaxController_Online.cs | 22 +- .../SearchController-Ajax-Organisations.cs | 11 +- .../Helpers/SaxContactMethodHelper_Online.cs | 12 +- .../Online/Models/ModelData_Organisation.cs | 56 +-- .../Online/Models/Model_Article.cs | 2 +- .../Account/Profile-Organisations.cshtml | 2 +- .../Views/Article/Index.cshtml | 2 +- .../Views/Common/ContactMethods.cshtml | 2 +- .../OrganisationControls-Contact.cshtml | 2 +- .../Common/OrganisationControls-Core.cshtml | 12 +- SaxoniteCoreWebOnline/Views/Home/Index.cshtml | 9 +- .../Views/Search/Organisations.cshtml | 49 ++- .../wwwroot/css/sax-data.css | 32 +- .../wwwroot/css/sax-home.css | 22 +- SaxoniteCoreWebOnline/wwwroot/js/sax-home.js | 3 +- .../wwwroot/js/sax-src-org.js | 1 + .../wwwroot/js/sax-usr-org.js | 27 +- SaxoniteStandardBase/Base/App/Sax-Config.cs | 92 ++-- SaxoniteStandardBase/Base/App/Sax-Version.cs | 2 +- SaxoniteStandardBase/Base/App/Sax.cs | 8 +- .../Base/App/SaxDbDfn_Core.cs | 8 +- .../Base/App/SaxSystem-CodeTypes.cs | 6 +- .../Base/Bus/SaxOrganisation.cs | 30 +- .../Base/Mgr/SaxAccountMgr-Organisation.cs | 27 +- .../Base/Mgr/SaxItemDataMgr-ItemImage.cs | 10 + .../Dbas/Internal/ActivityFeedback_Db.cs | 2 +- .../Dbas/Internal/ActivityIndex_Db.cs | 2 +- .../Dbas/Internal/Article_Db.cs | 2 +- .../Dbas/Internal/ItemAddress_Db.cs | 2 +- .../Dbas/Internal/ItemImage_Db.cs | 2 +- .../Dbas/Internal/ItemSport_Db.cs | 2 +- .../Dbas/Internal/OrganisationEvent_Db.cs | 2 +- .../Internal/OrganisationOpportunity_Db.cs | 2 +- .../Dbas/Internal/Organisation_Db.cs | 14 +- .../Dbas/Internal/Region_Db.cs | 2 +- .../Dbas/Internal/Sport_Db.cs | 2 +- .../Dbas/Internal/UserAccount_Db.cs | 2 +- .../Dbas/Internal/UserActivity_Db.cs | 2 +- .../Dbas/Internal/UserOnline_Db.cs | 2 +- .../Dbas/Internal/UserOrganisation_Db.cs | 2 +- .../Dbas/Internal/UserRole_Db.cs | 2 +- .../Dbas/Internal/UserSession_Db.cs | 2 +- .../Dbas/Internal/UserSportExperience_Db.cs | 2 +- .../Dbas/Internal/UserSport_Db.cs | 2 +- .../Mgr/SaxAccountMgr_Dbas-Organisation.cs | 50 ++- .../SaxAccountMgr_Dbas-UserOrganisation.cs | 9 + .../Resources/Database/SaxDbDfn_Core.xml | 58 +-- 93 files changed, 2063 insertions(+), 788 deletions(-) create mode 100644 SaxoniteCoreWebAdmin/Admin/Controllers/ImageController.cs create mode 100644 SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Images-Ajax.cs create mode 100644 SaxoniteCoreWebAdmin/Views/Organisation/Panel-OrganisationImagesDialog.cshtml create mode 100644 SaxoniteCoreWebAdmin/wwwroot/img/animated/progress-basketball-32.gif create mode 100644 SaxoniteCoreWebAdmin/wwwroot/img/animated/progress-basketball-64.gif create mode 100644 SaxoniteCoreWebAdmin/wwwroot/img/animated/progress-cog-32.gif create mode 100644 SaxoniteCoreWebAdmin/wwwroot/img/animated/progress-cog-64.gif create mode 100644 SaxoniteCoreWebAdmin/wwwroot/js/sax-org-add.js create mode 100644 SaxoniteCoreWebAdmin/wwwroot/js/sax-org-img.js diff --git a/SaxoniteCoreUnitTests/UnitTests/Web/Admin/App/SaxSessionData_Admin_Tester.cs b/SaxoniteCoreUnitTests/UnitTests/Web/Admin/App/SaxSessionData_Admin_Tester.cs index e6572f9..ab0777a 100644 --- a/SaxoniteCoreUnitTests/UnitTests/Web/Admin/App/SaxSessionData_Admin_Tester.cs +++ b/SaxoniteCoreUnitTests/UnitTests/Web/Admin/App/SaxSessionData_Admin_Tester.cs @@ -1,6 +1,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Newtonsoft.Json; using Sarand.Net.Standard.Base.Bus; +using Sarand.Net.Standard.Base.Utils; using Saxonite.Net.Core.Web.Admin.App; using Saxonite.Net.Core.Web.Base.Controllers; using Saxonite.Net.Standard.Base.App; @@ -48,6 +49,13 @@ namespace Saxonite.Net.Core.UnitTests.Web.Admin.App Assert.IsTrue(sessionData.ValidUserSession); Assert.AreNotEqual("", sessionData.User.DisplayName); + // Filters. + sessionData.Filter.SportId = new SysId(2345); + sessionData.Filter.RegionId = new SysId(6543); + + // Encryption data. + Assert.IsFalse(StringUtil.IsEmpty(sessionData.Base64IV_AdminApp)); + int idx = sessionData.NextItemIdx; idx = sessionData.NextItemIdx; idx = sessionData.NextItemIdx; @@ -57,26 +65,35 @@ namespace Saxonite.Net.Core.UnitTests.Web.Admin.App Assert.AreNotEqual("", sessionDataJson); // Deserialise session data using same method as base controller. - SaxSessionData_Admin sessionDataDeserialized = + SaxSessionData_Admin sessionDataDeserialised = JsonConvert.DeserializeObject( sessionDataJson, SaxController_Base.SessionStorage.JsonDeserialisationSettings ); - Assert.IsNotNull(sessionDataDeserialized); + Assert.IsNotNull(sessionDataDeserialised); // Validate deserialised values. - Assert.AreEqual(sessionData.SessionIdx, sessionDataDeserialized.SessionIdx); - Assert.AreEqual(sessionData.ItemIdx, sessionDataDeserialized.ItemIdx); - Assert.AreEqual(App.SessionContext.Index, sessionDataDeserialized.SessionContext.Index); - Assert.AreEqual(Sax.Application.Admin, sessionDataDeserialized.SessionContext.Application); - Assert.AreEqual(SysId.ToInt(sessionDataDeserialized.User.UserAccountId), SysId.ToInt(sessionDataDeserialized.User.UserAccountId)); - Assert.AreEqual(SysId.ToInt(sessionData.User.UserAccountId), SysId.ToInt(sessionDataDeserialized.User.UserAccountId)); - Assert.AreEqual(sessionData.User.EmailAddress, sessionDataDeserialized.User.EmailAddress); - Assert.AreEqual(sessionData.User.FirstName, sessionDataDeserialized.User.FirstName); - Assert.AreEqual(sessionData.User.LastName, sessionDataDeserialized.User.LastName); - Assert.AreEqual(sessionData.User.DisplayName, sessionDataDeserialized.User.DisplayName); + Assert.AreEqual(sessionData.SessionIdx, sessionDataDeserialised.SessionIdx); + Assert.AreEqual(sessionData.ItemIdx, sessionDataDeserialised.ItemIdx); + Assert.AreEqual(App.SessionContext.Index, sessionDataDeserialised.SessionContext.Index); + Assert.AreEqual(Sax.Application.Admin, sessionDataDeserialised.SessionContext.Application); + Assert.AreEqual(SysId.ToInt(sessionDataDeserialised.User.UserAccountId), SysId.ToInt(sessionDataDeserialised.User.UserAccountId)); + Assert.AreEqual(SysId.ToInt(sessionData.User.UserAccountId), SysId.ToInt(sessionDataDeserialised.User.UserAccountId)); + Assert.AreEqual(sessionData.User.EmailAddress, sessionDataDeserialised.User.EmailAddress); + Assert.AreEqual(sessionData.User.FirstName, sessionDataDeserialised.User.FirstName); + Assert.AreEqual(sessionData.User.LastName, sessionDataDeserialised.User.LastName); + Assert.AreEqual(sessionData.User.DisplayName, sessionDataDeserialised.User.DisplayName); // User roles. - Assert.AreEqual(userRoles.Count, sessionDataDeserialized.User.Roles.Count); + Assert.AreEqual(userRoles.Count, sessionDataDeserialised.User.Roles.Count); + + // Filters. + + // Filters. + Assert.AreEqual(sessionData.Filter.SportId, sessionDataDeserialised.Filter.SportId); + Assert.AreEqual(sessionData.Filter.RegionId, sessionDataDeserialised.Filter.RegionId); + + // Encryption data. + Assert.AreEqual(sessionData.Base64IV_AdminApp, sessionDataDeserialised.Base64IV_AdminApp); } //------------------------------------------------------------------- diff --git a/SaxoniteCoreUnitTestsOnline/UnitTests/Online/App/SaxSessionData_Online_Tester.cs b/SaxoniteCoreUnitTestsOnline/UnitTests/Online/App/SaxSessionData_Online_Tester.cs index 17bbd70..2505d00 100644 --- a/SaxoniteCoreUnitTestsOnline/UnitTests/Online/App/SaxSessionData_Online_Tester.cs +++ b/SaxoniteCoreUnitTestsOnline/UnitTests/Online/App/SaxSessionData_Online_Tester.cs @@ -10,6 +10,7 @@ using System; using System.Collections.Generic; using System.Text; using Sarand.Net.Standard.Base.Helpers; +using Sarand.Net.Standard.Base.Utils; namespace Saxonite.Net.Core.UnitTests.Web.Online.App { @@ -77,6 +78,9 @@ namespace Saxonite.Net.Core.UnitTests.Web.Online.App sessionData.Filter.OrganisationIdMember = filterOrganisationIdMember; sessionData.Filter.OrganisationCaptionMember = filterOrganisationCaptionMember; + // Encryption data. + Assert.IsFalse(StringUtil.IsEmpty(sessionData.Base64IV_OnlineApp)); + int idx = sessionData.NextItemIdx; idx = sessionData.NextItemIdx; idx = sessionData.NextItemIdx; @@ -137,6 +141,9 @@ namespace Saxonite.Net.Core.UnitTests.Web.Online.App Assert.IsTrue(SysId.IsValid(sessionDataDeserialised.Filter.OrganisationIdMember)); Assert.AreEqual(filterOrganisationIdMember, sessionDataDeserialised.Filter.OrganisationIdMember); Assert.AreEqual(filterOrganisationCaptionMember, sessionDataDeserialised.Filter.OrganisationCaptionMember); + + // Encryption data. + Assert.AreEqual(sessionData.Base64IV_OnlineApp, sessionDataDeserialised.Base64IV_OnlineApp); } //------------------------------------------------------------------- diff --git a/SaxoniteCoreWeb/Base/Controllers/SaxController_Base.cs b/SaxoniteCoreWeb/Base/Controllers/SaxController_Base.cs index d543dd7..b853e59 100644 --- a/SaxoniteCoreWeb/Base/Controllers/SaxController_Base.cs +++ b/SaxoniteCoreWeb/Base/Controllers/SaxController_Base.cs @@ -501,9 +501,10 @@ namespace Saxonite.Net.Core.Web.Base.Controllers public String Line2 { get; set; } public String Line3 { get; set; } public String Town { get; set; } - public String Province { get; set; } public String RegionIdProvince { get; set; } - public String Country { get; set; } + public String RegionNameProvince { get; set; } + public String RegionIdCountry { get; set; } + public String RegionNameCountry { get; set; } public String PostalCode { get; set; } } /// diff --git a/SaxoniteCoreWeb/Base/Models/SaxModel_Base.cs b/SaxoniteCoreWeb/Base/Models/SaxModel_Base.cs index 68fa520..472f1b5 100644 --- a/SaxoniteCoreWeb/Base/Models/SaxModel_Base.cs +++ b/SaxoniteCoreWeb/Base/Models/SaxModel_Base.cs @@ -76,7 +76,7 @@ namespace Saxonite.Net.Core.Web.Base.Models //------------------------------------------------------------------- // Business components. - protected SaxInternals Internals { get { if (internals == null) internals = new SaxInternals(SessionContext); return internals; } } + public SaxInternals Internals { get { if (internals == null) internals = new SaxInternals(SessionContext); return internals; } } // Consoles. protected SaxDataCacheConsole DataCacheConsole { get { return Internals.DataCacheConsole; } } protected SaxSessionDataConsole SessionDataConsole { get { return Internals.SessionDataConsole; } } @@ -257,8 +257,6 @@ namespace Saxonite.Net.Core.Web.Base.Models return Math.Min(maxLength, 2000); case SaxDbDfn_Core.ColumnIdx.UserAccount_PhoneMain: case SaxDbDfn_Core.ColumnIdx.UserAccount_PhoneMobile: - case SaxDbDfn_Core.ColumnIdx.Organisation_PhoneMain: - case SaxDbDfn_Core.ColumnIdx.Organisation_PhoneAlt: return Math.Min(maxLength, 20); //case SaxDbDfn.ColumnIdx.UserAccount_Postcode: //case SaxDbDfn.ColumnIdx.ItemAddress_Postcode: diff --git a/SaxoniteCoreWebAdmin/Admin/App/SaxSessionData_Admin.cs b/SaxoniteCoreWebAdmin/Admin/App/SaxSessionData_Admin.cs index ea75943..78252d8 100644 --- a/SaxoniteCoreWebAdmin/Admin/App/SaxSessionData_Admin.cs +++ b/SaxoniteCoreWebAdmin/Admin/App/SaxSessionData_Admin.cs @@ -1,4 +1,7 @@ using Newtonsoft.Json; +using Sarand.Net.Standard.Base.Bus; +using Sarand.Net.Standard.Base.Helpers; +using Saxonite.Net.Core.Web.Admin.Controllers; using Saxonite.Net.Core.Web.Base.App; using Saxonite.Net.Standard.Base.App; using Saxonite.Net.Standard.Base.Bus; @@ -45,6 +48,21 @@ namespace Saxonite.Net.Core.Web.Admin.App /// User - JSON deserialisation route. public new UserData User { get { return base.User; } set { SetUser(new UserData_Admin(value)); } } + //------------------------------------------------------------------- + // General filters. + public class FilterData + { + // Sport. + public SysId SportId { get; set; } + // Region. + public SysId RegionId { get; set; } + } + public FilterData Filter { get; private set; } = new FilterData(); + + //------------------------------------------------------------------- + // Online application encryption IV value - for consistent encryption during session. + public String Base64IV_AdminApp { get; private set; } = SacsEncryptionHelper.InitSymmetricEncryptionIV(SaxController_Admin.Base64Key_AdminApp); + //------------------------------------------------------------------- } //======================================================================= diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/ImageController.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/ImageController.cs new file mode 100644 index 0000000..a0a7560 --- /dev/null +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/ImageController.cs @@ -0,0 +1,214 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Microsoft.Net.Http.Headers; +using Sarand.Net.Standard.Base.Bus; +using Sarand.Net.Standard.Base.Utils; +using Saxonite.Net.Core.Web.Base.App; +using Saxonite.Net.Standard.Base.App; +using Saxonite.Net.Standard.Base.Bus; +using System; +using System.Collections.Specialized; + +namespace Saxonite.Net.Core.Web.Admin.Controllers +{ + //======================================================================= + // Admin Image Controller - delivers dynamic images. + //======================================================================= + public partial class ImageController : SaxController_Admin + { + //------------------------------------------------------------------- + // Data members. + public static String ParamName_OrganisationLogoEncrypted { get; } = "ole"; + public static String ParamName_ArticleImageEncrypted { get; } = "aie"; + public static String ParamName_OrganisationId { get; } = "ori"; + public static String ParamName_ItemImageId { get; } = "iii"; + + //------------------------------------------------------------------- + // Construction. + public ImageController(SaxLocaliser localiser, ILogger logger) : + base(localiser, logger) + { + } + + //------------------------------------------------------------------- + // Account Contact image request. + //------------------------------------------------------------------- + /// + /// Get the Account Contact Image for the specified account. + /// Current User Account is used if no other specified in the encrypted parameter. + /// Encrypted parameter: + /// - uaie: User Account Id encrypted. + /// + [HttpGet] + [Route("Image/Account/Contact")] + public IActionResult GetAccountContactImage(String uaie) + { + String userAccountIdParam = "";// ToDo Decrypt from uaie value. + SysId userAccountId = userAccountIdParam.ToSysId(); + if (!SysId.IsValid(userAccountId)) + userAccountId = UserAccountId; + return GetAccountContactImage(userAccountId, Sax.ImageType.Avatar); + } + + //------------------------------------------------------------------- + // Account Contact Image Profile/Upload request. + //------------------------------------------------------------------- + /// + /// Get the current Account Profile Contact Image for the specified account. + /// Account is always current User Account - this request is only fir use from the user's own profile page. + /// 'Uploaded' avatar image will take precedence as this will be the latest if present. + /// NB Avoid caching this image as it will change whenever a new one is uploaded. + /// + [HttpGet] + [Route("Image/Account/Contact/Profile")] + [Route("Image/Account/Contact/Upload")] + [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)] + public IActionResult GetAccountContactImage() + { + // Response should not be cached. + Response.Headers[HeaderNames.CacheControl] = "no-cache, no-store, must-revalidate, max-age=0"; + Response.Headers[HeaderNames.Expires] = "-1"; + SysId userAccountId = UserAccountId; + return GetAccountContactImage(userAccountId, Sax.ImageType.Avatar_Upload); + } + + //------------------------------------------------------------------- + /// + /// Get the Avatar image for the specified user account. + /// + /// + /// + private IActionResult GetAccountContactImage(SysId userAccountId, Sax.ImageType imageTypePriority) + { + String fn = GetType().Name + ".GetAccountContactImage"; + + // Validate session and referrer. + if (!ValidReferer || !ValidUserSession) return Forbid(); + ItemId userItemId = new ItemId((short)Sax.DataItemType.UserAccount, userAccountId); + + // Retrieve account contact images. + SaxItemImage.Params prmItemImage = new SaxItemImage.Params(); + prmItemImage.ItemIdsIncluded.Add(userItemId); + prmItemImage.ImageTypesIncluded.Add(Sax.ImageType.Avatar); + prmItemImage.ImageTypesIncluded.Add(Sax.ImageType.Avatar_Upload); + prmItemImage.StatusCat = SaxItemImage.StatusCat.ActiveOnly; + SaxItemImage itemImage = null; + foreach (SaxItemImage itemImageCandidate in ItemDataMgr.GetItemImageList(prmItemImage)) + { + // One of the 'Avatar' image types will take precedence. + if (itemImage == null || itemImageCandidate.ImageType == imageTypePriority) + itemImage = itemImageCandidate; + } + + if (itemImage == null) + return NotFound(); + + // Construct response. + return File(itemImage.ImageData, itemImage.ContentType); + } + + //------------------------------------------------------------------- + // Organisation logo. + //------------------------------------------------------------------- + /// + /// Get the logo for the specified Organisation. + /// Method parameter name (ole) must match name of encrypted parameter. + /// Encrypted parameters: + /// - ori: Organisation Id. + /// + [HttpGet] + [Route("Image/Organisation/Logo")] + public IActionResult GetOrganisationLogo(String ole) + { + return GetOrganisationLogo(ole, false); + } + /// + /// Get the logo for the specified Organisation. + /// Method parameter name (ole) must match name of encrypted parameter. + /// Encrypted parameters: + /// - ori: Organisation Id. + /// + [HttpGet] + [Route("Image/Organisation/Logo/Upload")] + public IActionResult GetOrganisationLogoUpload(String ole) + { + return GetOrganisationLogo(ole, true); + } + private IActionResult GetOrganisationLogo(String encryptedParams, bool allowUploadImage) + { + String fn = GetType().Name + ".GetOrganisationLogo"; + + // Validate session and referrer. + if (!ValidReferer || !ValidUserSession) return Forbid(); + + // Extract encrypted parameters. + NameValueCollection parameters = ExtractSessionParams(encryptedParams); + if (parameters.Count < 1) return Forbid(); + + // Organisation Id. + SysId organisationId = parameters.Get(ParamName_OrganisationId).ToSysId(); + if (!SysId.IsValid(organisationId)) return Forbid(); + ItemId organisationItemId = new ItemId((short)Sax.DataItemType.Organisation, organisationId); + + // Query Organisation Logo images. + SaxItemImage.Params prmItemImage = new SaxItemImage.Params(); + prmItemImage.ItemIdsIncluded.Add(organisationItemId); + prmItemImage.ImageTypesIncluded.Add(Sax.ImageType.OrganisationLogo); + if (allowUploadImage) + prmItemImage.ImageTypesIncluded.Add(Sax.ImageType.OrganisationLogo_Upload); + prmItemImage.StatusCat = SaxItemImage.StatusCat.ActiveOnly; + SaxItemImage itemImage = null; + foreach (SaxItemImage itemImageCandidate in ItemDataMgr.GetItemImageList(prmItemImage)) + { + // The 'Upload' image will take precedence if allowed. + if (itemImage == null || itemImageCandidate.ImageType == Sax.ImageType.OrganisationLogo_Upload) + itemImage = itemImageCandidate; + } + + if (itemImage == null) + return NotFound(); + + // Construct response. + Response.Headers[HeaderNames.CacheControl] = "no-cache, no-store, must-revalidate, max-age=0"; + Response.Headers[HeaderNames.Expires] = "-1"; + return File(itemImage.ImageData, itemImage.ContentType); + } + + //------------------------------------------------------------------- + // Article image. + //------------------------------------------------------------------- + /// + /// Get the specified Article Image. + /// Method parameter name (aie) must match name of encrypted parameter. + /// Encrypted parameters: + /// - iii: Item Image Id. + /// + [HttpGet] + [Route("Image/Article")] + public IActionResult GetArticleImage(String aie) + { + String fn = GetType().Name + ".GetArticleImage"; + + // Validate session and referrer. + if (!ValidReferer || !ValidUserSession) return Forbid(); + ItemId userItemId = new ItemId((short)Sax.DataItemType.UserAccount, UserAccountId); + + // Extract encrypted parameters. + NameValueCollection parameters = ExtractSessionParams(aie); + if (parameters.Count < 1) return Forbid(); + + // Item Image. + SysId itemImageId = parameters.Get(ParamName_ItemImageId).ToSysId(); + if (!SysId.IsValid(itemImageId)) return Forbid(); + SaxItemImage itemImage = ItemDataMgr.GetItemImageById(itemImageId); + if (itemImage == null) + return NotFound(); + + // Construct response. + return File(itemImage.ImageData, itemImage.ContentType); + } + + //------------------------------------------------------------------- + } + //======================================================================= +} diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Ajax.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Ajax.cs index de9038c..88e74d2 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Ajax.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Ajax.cs @@ -9,6 +9,7 @@ using Saxonite.Net.Standard.Base.App; using Saxonite.Net.Standard.Base.Bus; using System; using System.Collections.Generic; +using System.Collections.Specialized; using System.Linq; using System.Threading.Tasks; @@ -30,13 +31,20 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers public String CategoryDesc { get; set; } public long MemberCount { get; set; } // Sport. - public long SportId { get; set; } + public String SportId { get; set; } public String SportName { get; set; } - // Region - country. - public long RegionIdCountry { get; set; } - public String RegionNameCountry { get; set; } + // Primary Region. + public class RegionData + { + public String Id { get; set; } + public String Name { get; set; } + public bool? Country { get; set; } = null; + } + public RegionData RegionPrimary { get; } = new RegionData(); // Address. public String AddressSummary { get; set; } + // Logo. + public String LogoImageUrl { get; set; } } private class OrganisationItemData : OrganisationListData { @@ -55,22 +63,30 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers data.CategoryDesc = OrganisationCategoryCodes.GetDescForType(organisation.OrganisationCategory); data.MemberCount = AccountMgr.GetOrganisationMemberCount(organisation.Id); // Sport. - data.SportId = SysId.ToLong(organisation.SportIdPrimary); + data.SportId = SysId.ToString(organisation.SportIdPrimary, null); using (SaxSport sport = ReferenceMgr.GetSportById(organisation.SportIdPrimary)) - data.SportName = sport?.Name ?? ""; - // Region. - data.RegionIdCountry = SysId.ToLong(organisation.RegionIdPrimary); - using (SaxRegion region = ReferenceMgr.GetRegionById(organisation.RegionIdPrimary)) - data.RegionNameCountry = region?.Name ?? ""; + data.SportName = sport?.Name ?? null; + // Primary region. + data.RegionPrimary.Id = SysId.ToString(organisation.RegionIdPrimary, null); + SaxRegion.Data region = Internals.GetRegion(organisation.RegionIdPrimary); + if (region != null) + { + data.RegionPrimary.Name = region.Name; + data.RegionPrimary.Country = region.RegionType == Sax.RegionType.Country ? true : (bool?)null; + } // Common properties. data.StatusCode = organisation.StatusCode; data.StatusDesc = organisation.StatusDesc; + + // Organisation Logo image url. + if (ItemDataMgr.ItemHasActiveImage(organisation.GetItemId(), Sax.ImageType.OrganisationLogo)) + data.LogoImageUrl = GetOrganisationLogoUrl(organisation.Id, false); } private void PopulateOrganisationResponseItemData(SaxOrganisation organisation, SaxItemAddress address, OrganisationItemData data) { - PopulateOrganisationResponseListData(organisation, data as OrganisationListData); - data.About = organisation.About; - data.Facilities = organisation.Facilities; + PopulateOrganisationResponseListData(organisation, data); + data.About = StringUtil.OnEmpty(organisation.About, null); + data.Facilities = StringUtil.OnEmpty(organisation.Facilities, null); // Contact methods. SaxContactData contactData = new SaxContactData(organisation.ContactData); foreach (Sax.ContactMethod method in contactData.Methods) @@ -82,14 +98,16 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers if (address != null && !address.IsStatusDeleted) { data.Address = new AjaxResponse_Data.AddressData(); - data.Address.Id = address.Id.ToString(); - data.Address.Line1 = address.Line1; - data.Address.Line2 = address.Line2; - data.Address.Line3 = address.Line3; + data.Address.Id = address.Id.ToString(null); + data.Address.Line1 = StringUtil.OnEmpty(address.Line1, null); + data.Address.Line2 = StringUtil.OnEmpty(address.Line2, null); + data.Address.Line3 = StringUtil.OnEmpty(address.Line3, null); data.Address.Town = address.Town; - data.Address.Province = address.Province; - data.Address.Country = address.Country; - data.Address.PostalCode = address.PostalCode; + data.Address.RegionIdProvince = SysId.ToString(address.RegionIdProvince, null); + data.Address.RegionNameProvince = StringUtil.OnEmpty(address.Province, null); + data.Address.RegionIdCountry = SysId.ToString(address.RegionIdCountry, null); + data.Address.RegionNameCountry = StringUtil.OnEmpty(address.Country, null); + data.Address.PostalCode = StringUtil.OnEmpty(address.PostalCode, null); } // Common properties. data.Notes = organisation.Notes; @@ -136,13 +154,19 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers responseData.Statuses.Add(SacsSystem.GetStatusCode(status), Localiser.GetStatusCaption(status)); responseData.DefaultStatusCode = SacsSystem.GetStatusCode(SaxOrganisation.DefaultStatus); + // Store filter values. + SysId filterSportId = model.Filter.OrganisationSportId.ToSysId(); + SessionData_Admin.Filter.SportId = SysId.IsValid(filterSportId) ? filterSportId : null; + SysId filterRegionId = model.Filter.OrganisationRegionId.ToSysId(); + SessionData_Admin.Filter.RegionId = SysId.IsValid(filterRegionId) ? filterRegionId : null; + // Item filter. SaxOrganisation.Params prmOrganisation = new SaxOrganisation.Params(); prmOrganisation.OrganisationFilter = model.Filter.Organisation; prmOrganisation.OrganisationType = model.FilterOrganisationType; prmOrganisation.OrganisationCategory = model.FilterOrganisationCategory; - prmOrganisation.SportIdsIncluded.AddId(model.Filter.OrganisationSportId.ToSysId()); - prmOrganisation.RegionIdsIncluded.AddId(model.Filter.OrganisationRegionId.ToSysId()); + prmOrganisation.SportIdsIncluded.AddId(filterSportId); + prmOrganisation.RegionIdsIncluded.AddId(filterRegionId); prmOrganisation.StatusCat = model.Filter.OrganisationStatusCategoryCode != null ? model.FilterOrganisationStatusCategory : SaxOrganisation.StatusCat.NotDeleted; prmOrganisation.SortColumns.Add(AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.Name)); model.Paging.ItemTotal = (ulong)AccountMgr.GetOrganisationCount(prmOrganisation); @@ -153,10 +177,9 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers ColumnDfn colName = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.Name); ColumnDfn colOrganisationType = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.OrganisationType); ColumnDfn colOrganisationCategory = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.OrganisationCategory); - ColumnDfn colPhoneMain = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.PhoneMain); - ColumnDfn colPhoneAlt = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.PhoneAlt); ColumnDfn colUserCount = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.User_Count); ColumnDfn colMemberCount = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.Member_Count); + ColumnDfn colLogoCount = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.LogoImage_Count); ColumnDfn colSportId = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.SportId); ColumnDfn colSportName = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.Sport_Name); ColumnDfn colRegionId = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.RegionId); @@ -168,9 +191,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers colName, colOrganisationType, colOrganisationCategory, - colPhoneMain, - colPhoneAlt, - colUserCount, colMemberCount, + colUserCount, colMemberCount, colLogoCount, colSportId, colSportName, colRegionId, colRegionName, colStatus @@ -182,22 +203,28 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers while (resultSet != null && resultSet.HasRows && resultSet.NextRecord()) { NamedValue.Map values = resultSet.GetRecordValues(); + SysId organisationId = new SysId(values.GetLong(colId.DataName)); OrganisationListData organisationData = new OrganisationListData() { - Id = values.GetLong(colId.DataName).ToString(), + Id = organisationId.ToString(), Name = values.GetString(colName.DataName), TypeCode = values.GetString(colOrganisationType.DataName), TypeDesc = OrganisationTypeCodes.GetDescForCode(values.GetString(colOrganisationType.DataName)), CategoryCode = values.GetString(colOrganisationCategory.DataName), CategoryDesc = OrganisationCategoryCodes.GetDescForCode(values.GetString(colOrganisationCategory.DataName)), MemberCount = StringUtil.ToLong(values.GetString(colMemberCount.DataName)), - SportId = values.GetLong(colSportId.DataName), - SportName = values.GetString(colSportName.DataName), - RegionIdCountry = values.GetLong(colRegionId.DataName), - RegionNameCountry = values.GetString(colRegionName.DataName), + SportId = SysId.ToString(new SysId(values.GetLong(colSportId.DataName)), null), + SportName = StringUtil.OnEmpty(values.GetString(colSportName.DataName), null), StatusCode = values.GetString(colStatus.DataName), StatusDesc = SacsSystem.GetStatusDescForCode(values.GetString(colStatus.DataName)) }; + // Logo image. + if (StringUtil.ToLong(values.GetString(colLogoCount.DataName)) > 0) + organisationData.LogoImageUrl = GetOrganisationLogoUrl(organisationId, false); + // Primary region. + organisationData.RegionPrimary.Id = SysId.ToString(new SysId(values.GetLong(colRegionId.DataName)), null); + organisationData.RegionPrimary.Name = StringUtil.OnEmpty(values.GetString(colRegionName.DataName), null); + // Add item to list. responseData.Organisations.Add(organisationData); } } @@ -318,8 +345,8 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers organisation.OrganisationCategory = model.OrganisationCategory; if (model.OrganisationSportId != null) organisation.SportIdPrimary = model.OrganisationSportId.ToSysId(); - if (model.OrganisationRegionIdCountry != null) - organisation.RegionIdPrimary = model.OrganisationRegionIdCountry.ToSysId(); + if (model.OrganisationRegionIdPrimary != null) + organisation.RegionIdPrimary = model.OrganisationRegionIdPrimary.ToSysId(); if (model.OrganisationAbout != null) organisation.About = model.OrganisationAbout; if (model.OrganisationFacilities != null) @@ -356,7 +383,10 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers if (model.Address.IsPopulated()) { // Organisation regions. - SaxRegion.Data regionCountry = Internals.GetRegion(organisation.RegionIdPrimary); + SaxRegion.Data regionCountry = Internals.GetRegion(model.Address.RegionIdCountry.ToSysId()); + if (regionCountry == null) + // Default to Organisation's primary region. + regionCountry = Internals.GetRegion(organisation.RegionIdPrimary); if (regionCountry != null && regionCountry.RegionType != Sax.RegionType.Country) regionCountry = null; SaxRegion.Data regionProvince = null; @@ -392,11 +422,6 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers organisationAddress.Country = regionCountry.Name; organisationAddress.RegionIdCountry = regionCountry.Id; } - else - { - if (model.Address.Country != null) - organisationAddress.Country = model.Address.Country; - } organisationAddress.PostalCode = model.Address.PostalCode; organisationAddress.Status = SaxItemAddress.DefaultStatus; @@ -420,7 +445,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers } } } - else + else if (organisationAddress != null && !organisationAddress.IsStatusDeleted) { // Address not populated - delete it. if (!ItemDataMgr.DeleteItemAddress(organisationAddress, DbUpdateInfo)) diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Images-Ajax.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Images-Ajax.cs new file mode 100644 index 0000000..0ab1099 --- /dev/null +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController-Images-Ajax.cs @@ -0,0 +1,308 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Sarand.Net.Standard.Base.App; +using Sarand.Net.Standard.Base.Bus; +using Sarand.Net.Standard.Base.Core; +using Sarand.Net.Standard.Base.Dbas; +using Sarand.Net.Standard.Base.Helpers; +using Sarand.Net.Standard.Base.Utils; +using Saxonite.Net.Core.Web.Admin.Models; +using Saxonite.Net.Standard.Base.App; +using Saxonite.Net.Standard.Base.Bus; +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +namespace Saxonite.Net.Core.Web.Admin.Controllers +{ + //======================================================================= + // Controller: Organisation Images - Ajax + //======================================================================= + partial class OrganisationController + { + //------------------------------------------------------------------- + private class OrganisationImagesData : OrganisationItemData + { + } + private void PopulateOrganisationResponseImagesData(SaxOrganisation organisation, SaxItemAddress address, OrganisationImagesData data) + { + PopulateOrganisationResponseItemData(organisation, address, data); + } + + //------------------------------------------------------------------- + // Get Organisation Images ajax request. + //------------------------------------------------------------------- + private class AjaxResponse_OrganisationImages : AjaxResponse_Data + { + // Construction. + public AjaxResponse_OrganisationImages(SaxModel_Admin model) : base(model) { } + // Organisation details. + public OrganisationImagesData organisation { get; } = new OrganisationImagesData(); + // Reference data. + public List ImageContentTypes { get; set; } + } + //------------------------------------------------------------------- + [HttpPost] + [Route("Organisation/Ajax/GetImages")] + [Route("Organisation/Ajax/GetItemImages")] + public IActionResult Ajax_GetImages(Model_Organisation model) + { + String fn = GetType().Name + ".Ajax_GetImages"; + String messageTitle = Localiser["organisation.caption.organisation"]; + AjaxResponse_OrganisationImages responseData = new AjaxResponse_OrganisationImages(model); + + // Validate session. + if (!ValidUserSession) + return GetAjaxResult_InvalidSession(responseData); + + // Record disposable items as they are created so all can be disposed when needed. + using (DisposableCollection disposableItems = new DisposableCollection()) + { + + // Reference data - Image content types. + responseData.ImageContentTypes = SaxItemImage.GetValidContentTypes(Sax.DataItemType.Organisation); + + // Identify item. + SysId organisationId = model.OrganisationId.ToSysId(); + if (!SysId.IsValid(organisationId)) + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.specified"])); + + // Retrieve item. + SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationId); + disposableItems.Add(organisation); + if (organisation == null) + { + SaLogger.Warn(fn + " Failed to retrieve organisation for Id: '" + model.OrganisationId + "'"); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"])); + } + + // Organisation address. + SaxItemAddress address = ItemDataMgr.GetFirstItemAddressByItemAndType(organisation.GetItemId()); + disposableItems.Add(address); + + // Construct response. + PopulateOrganisationResponseImagesData(organisation, address, responseData.organisation); + + // Complete response. + responseData.Success = true; + } + return Json(responseData); + } + + //------------------------------------------------------------------- + // Account - Upload Contact Image. + // Ref. http://www.binaryintellect.net/articles/474dc031-087f-4b81-b994-9ca2adb949d6.aspx + //------------------------------------------------------------------- + private class AjaxResponse_UploadOrganisationLogoImage : AjaxResponse_Data + { + // Construction. + public AjaxResponse_UploadOrganisationLogoImage(SaxModel_Admin model) : base(model) { } + // Uploaded image URL. + public String ImageUrl { get; set; } + // Upload message. + public String UploadMsg { get; set; } + } + // Ajax request. + [HttpPost] + [Route("Organisation/Ajax/UploadLogo")] + [Route("Organisation/Ajax/UploadLogoImage")] + public IActionResult UploadOrganisationLogoImage(Model_Organisation model) + { + String fn = GetType().Name + ".UploadOrganisationLogoImage"; + AjaxResponse_UploadOrganisationLogoImage responseData = new AjaxResponse_UploadOrganisationLogoImage(model); + String messageTitle = Localiser["organisation.caption.organisation"]; + + // Validate session and referrer. + if (!ValidReferer) return Forbid(); + if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData); + RefreshModel(model); + + // Record disposable items as they are created so all can be disposed when needed. + using (DisposableCollection disposableItems = new DisposableCollection()) + { + // Retrieve organisation. + SysId organisationId = model.OrganisationId.ToSysId(); + if (!SysId.IsValid(organisationId)) + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.specified"])); + 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"])); + } + + // Validate number of images. + if (model.OrganisationLogoImage.IsEmpty()) + { + SaLogger.Warn(fn + " No image file supplied for upload"); + return Json(responseData); + } + if (model.OrganisationLogoImage.Count != 1) + { + SaLogger.Warn(fn + " Invalid number of image files uploaded: " + model.OrganisationLogoImage.Count); + return Json(responseData.SetControlError("OrganisationLogoImage", Localiser["msg.upload.image.single"])); + } + IFormFile file = model.OrganisationLogoImage[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("OrganisationLogoImage", Localiser["msg.upload.image.invalid"])); + + // Validate mime type. + if (!SaxItemImage.GetValidMimeTypes(Sax.DataItemType.UserAccount).Contains(imageConfig.MimeType)) + return Json(responseData.SetControlError("OrganisationLogoImage", Localiser["msg.upload.image.invalid.mime"])); + + // Validate content type. + if (StringUtil.IsEmpty(file.ContentType) || !SaxItemImage.GetValidContentTypes(Sax.DataItemType.UserAccount).ContainsCI(file.ContentType)) + return Json(responseData.SetControlError("OrganisationLogoImage", Localiser["msg.upload.image.invalid.content.type"])); + + // Resize the image if required. + if (!ImageUtils.ConstrainImage(imageConfig, Sax.Config.Organisation.Logo.WidthMax, Sax.Config.Organisation.Logo.HeightMax)) + return Json(responseData.SetControlError("OrganisationLogoImage", Localiser["msg.upload.image.resize.failed"])); + + // Image belongs to the organisation. + ItemId organisationItemId = organisation.GetItemId(); + + // Store uploaded image as 'Upload' Logo - awaiting confirmation that the details are to be saved. + SaxItemImage itemImage = ItemDataMgr.GetItemImage(organisationItemId, Sax.ImageType.OrganisationLogo_Upload, SaxItemImage.StatusCat.Any); + if (itemImage == null) + itemImage = ItemDataMgr.CreateItemImage(organisationItemId, Sax.ImageType.OrganisationLogo_Upload); + itemImage.Filename = file.FileName; + itemImage.Mime = SacsContentTypeHelper.GetMimeTypeFromContentType(file.ContentType).ToString().ToLower(); + itemImage.ContentType = file.ContentType; + itemImage.ImageData = imageConfig.ImageData; + itemImage.Width = imageConfig.Width; + itemImage.Height = imageConfig.Height; + 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("OrganisationLogoImage", 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("OrganisationLogoImage", Localiser["msg.error.item.insert.failed"])); + } + } + + // Return image url with response. + responseData.ImageUrl = GetOrganisationLogoUrl(organisation.Id, true); + } + + // Complete response. + responseData.SetNotification(messageTitle, "Image uploaded."); + responseData.Success = true; + return Json(responseData); + } + + //------------------------------------------------------------------- + // Store Organisation Images ajax request. + //------------------------------------------------------------------- + [HttpPost] + [Route("Organisation/Ajax/StoreImages")] + public IActionResult Ajax_StoreImages(Model_Organisation model) + { + String fn = GetType().Name + ".Ajax_StoreImages"; + String messageTitle = Localiser["organisation.caption.organisation"]; + AjaxResponse_OrganisationImages responseData = new AjaxResponse_OrganisationImages(model); + + // Validate session. + if (!ValidUserSession) + return GetAjaxResult_InvalidSession(responseData); + + // Record disposable items as they are created so all can be disposed when needed. + using (DisposableCollection disposableItems = new DisposableCollection()) + { + + // Validate item properties. + model.ValidateOrganisationData(responseData); + if (responseData.HasControlErrors()) + return Json(responseData); + + // Retrieve organisation. + SysId organisationId = model.OrganisationId.ToSysId(); + SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationId); + disposableItems.Add(organisation); + if (organisation == null) + { + SaLogger.Warn(fn + " Failed to retrieve organisation for Id: '" + model.OrganisationId + "'"); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"])); + } + + // Organisation address. + SaxItemAddress address = ItemDataMgr.GetFirstItemAddressByItemAndType(organisation.GetItemId()); + disposableItems.Add(address); + + // Check for uploaded logo image. + ItemId organisationItemId = organisation.GetItemId(); + SaxItemImage itemImageLogoUploaded = ItemDataMgr.GetItemImage(organisationItemId, Sax.ImageType.OrganisationLogo_Upload, SaxItemImage.StatusCat.ActiveOnly); + disposableItems.Add(itemImageLogoUploaded); + if (itemImageLogoUploaded != null) + { + // Retrieve current OrganisationLogo image. + SaxItemImage itemImageLogo = ItemDataMgr.GetItemImage(organisationItemId, Sax.ImageType.OrganisationLogo, SaxItemImage.StatusCat.Any); + if (itemImageLogo == null) + itemImageLogo = ItemDataMgr.CreateItemImage(organisationItemId, Sax.ImageType.OrganisationLogo); + disposableItems.Add(itemImageLogo); + // Copy properties from uploaded image. + itemImageLogo.Filename = itemImageLogoUploaded.Filename; + itemImageLogo.Mime = itemImageLogoUploaded.Mime; + itemImageLogo.ContentType = itemImageLogoUploaded.ContentType; + itemImageLogo.Width = itemImageLogoUploaded.Width; + itemImageLogo.Height = itemImageLogoUploaded.Height; + itemImageLogo.ImageData = itemImageLogoUploaded.ImageData; + itemImageLogo.Status = SaxItemImage.DefaultStatus; + if (!SysId.IsValid(itemImageLogo.Id)) + { + if (!ItemDataMgr.InsertItemImage(itemImageLogo, DbUpdateInfo)) + { + SaLogger.Warn(fn + " Failed to insert logo image - " + ItemDataMgr.ErrorStateMessage); + return Json(responseData.SetControlError("AccountContactImage", Localiser["msg.error.item.insert.failed"])); + } + } + else + { + if (!ItemDataMgr.UpdateItemImage(itemImageLogo, DbUpdateInfo)) + { + SaLogger.Warn(fn + " Failed to update logo image - " + ItemDataMgr.ErrorStateMessage); + return Json(responseData.SetControlError("AccountContactImage", Localiser["msg.error.item.update.failed"])); + } + } + // Logically delete the uplaoded image. + itemImageLogoUploaded.Status = SaStatus.Deleted; + if (!ItemDataMgr.UpdateItemImage(itemImageLogoUploaded, DbUpdateInfo)) + SaLogger.Warn(fn + " Failed to logically delete uploaded logo image - " + ItemDataMgr.ErrorStateMessage); + } + + // Construct response. + PopulateOrganisationResponseImagesData(organisation, address, responseData.organisation); + } + // Complete response. + responseData.Success = true; + return Json(responseData); + } + + //------------------------------------------------------------------- + } + //======================================================================= +} diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController.cs index 3f18543..cd392bb 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/OrganisationController.cs @@ -32,8 +32,19 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers if (!ValidUserSession) return RedirectToAction("", ""); - // Paging. + // Model. Model_Organisation model = new Model_Organisation(Localiser); + + // Stored Sport filter. + model.Filter.OrganisationSportId = SessionData_Admin.Filter.SportId?.ToString() ?? null; + using (SaxSport filterSport = ReferenceMgr.GetSportById(SessionData_Admin.Filter.SportId)) + model.Filter.OrganisationSportName = Localiser.GetSportName(SessionData_Admin.Filter.SportId, filterSport?.Name ?? ""); + // Stored Region filter. + model.Filter.OrganisationRegionId = SessionData_Admin.Filter.RegionId?.ToString() ?? null; + using (SaxRegion filterRegion = ReferenceMgr.GetRegionById(SessionData_Admin.Filter.RegionId)) + model.Filter.OrganisationRegionName = Localiser.GetRegionName(SessionData_Admin.Filter.RegionId, filterRegion?.Name ?? ""); + + // Paging. model.Paging.PageNo = 1; model.Paging.PageLength = 8; diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/RegionController-Ajax.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/RegionController-Ajax.cs index ccf36fd..6f941d3 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/RegionController-Ajax.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/RegionController-Ajax.cs @@ -21,7 +21,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers // Region data. private class RegionListData : AjaxResponse_Data.ItemData_Base { - public String RegionTypeCode { get; set; } + public int? RegionType { get; set; } = null; public String RegionTypeDesc { get; set; } public String CountryIso2Code { get; set; } public String Name { get; set; } @@ -29,12 +29,13 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers } private class RegionItemData : RegionListData { + public String RegionTypeCode { get; set; } public String RegionIdParent { get; set; } } private void PopulateRegionResponseListData(SaxRegion region, RegionListData data) { data.Id = SysId.ToString(region.Id); - data.RegionTypeCode = RegionTypeCodes.GetCodeForType(region.RegionType); + data.RegionType = (int)region.RegionType; data.RegionTypeDesc = RegionTypeCodes.GetDescForType(region.RegionType); data.CountryIso2Code = region.CountryIso2Code; data.Name = region.Name; @@ -48,6 +49,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers private void PopulateRegionResponseItemData(SaxRegion region, RegionItemData data) { PopulateRegionResponseListData(region, data); + data.RegionTypeCode = RegionTypeCodes.GetCodeForType(region.RegionType); data.RegionIdParent = SysId.ToString(region.RegionIdParent); } @@ -121,7 +123,7 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers RegionListData regionData = new RegionListData() { Id = values.GetString(colId.DataName), - RegionTypeCode = RegionTypeCodes.GetCodeForType(regionType), + RegionType = (int)regionType, RegionTypeDesc = RegionTypeCodes.GetDescForType(regionType), CountryIso2Code = values.GetString(colIso2Code.DataName), Name = values.GetString(colName.DataName), diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/SaxController_Admin.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/SaxController_Admin.cs index 6fe7a39..acabf55 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/SaxController_Admin.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/SaxController_Admin.cs @@ -15,6 +15,7 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; using System.Threading.Tasks; +using System.Web; namespace Saxonite.Net.Core.Web.Admin.Controllers { @@ -119,6 +120,21 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers ClearSessionData(); } + //------------------------------------------------------------------- + // Organisation utilities. + //------------------------------------------------------------------- + /// + /// Construct URL to display Organisation logo. + /// - Option to allow for 'Upload' image if active. + /// + protected String GetOrganisationLogoUrl(SysId organisationId, bool uploadImage) + { + String imageLink = "Image/Organisation/Logo" + (uploadImage ? "/Upload" : "") + "?"; + NameValueCollection parameters = new NameValueCollection(); + parameters.Add(ImageController.ParamName_OrganisationId, SysId.ToString(organisationId)); + return imageLink + ImageController.ParamName_OrganisationLogoEncrypted + "=" + EncryptSessionParams(parameters); + } + //------------------------------------------------------------------- // Keep Logged In. //------------------------------------------------------------------- @@ -187,6 +203,25 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers return keepLoggedInParams; } + //------------------------------------------------------------------- + // Parameter encryption, eg. for images. + //------------------------------------------------------------------- + // Encryption key is the same for all users each time the application is started, + // but a different IV value is generated for each session so that the links/parameters cannot be shared. + public static String Base64Key_AdminApp { get; } = SacsEncryptionHelper.EncodePlainKey("Saxonite&Admin#Encryption-"+DateTime.Today.Format("ddMMyy"), SacsEncryptionHelper.KeyLength); + protected String EncryptSessionParams(NameValueCollection parameters) + { + return HttpUtility.UrlEncode(EncryptParameters(parameters, SessionData_Admin.Base64IV_AdminApp, Base64Key_AdminApp)); + } + public static String EncryptSessionParams(SaxController_Admin controller, NameValueCollection parameters) + { + return controller.EncryptSessionParams(parameters); + } + protected NameValueCollection ExtractSessionParams(String encryptedParams) + { + return DecryptParameters(encryptedParams, SessionData_Admin.Base64IV_AdminApp, Base64Key_AdminApp); + } + //------------------------------------------------------------------- } //======================================================================= diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/UserController-Ajax.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/UserController-Ajax.cs index 6aecf7f..eba7668 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/UserController-Ajax.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/UserController-Ajax.cs @@ -161,6 +161,12 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers foreach (SaStatus status in SaxUserAccount.ValidStatuses.FindAll(item => item != SaStatus.Deleted)) responseData.Statuses.Add(SacsSystem.GetStatusCode(status), Localiser.GetStatusCaption(status)); + // Store filter values. + SysId filterSportId = model.Filter.UserSportId.ToSysId(); + SessionData_Admin.Filter.SportId = SysId.IsValid(filterSportId) ? filterSportId : null; + SysId filterRegionId = model.Filter.UserRegionId.ToSysId(); + SessionData_Admin.Filter.RegionId = SysId.IsValid(filterRegionId) ? filterRegionId : null; + // Item filter. SaxUserAccount.Params prmUserAccount = new SaxUserAccount.Params(); prmUserAccount.UserFilterPartial = model.Filter.User; diff --git a/SaxoniteCoreWebAdmin/Admin/Controllers/UserController.cs b/SaxoniteCoreWebAdmin/Admin/Controllers/UserController.cs index c7c4b8e..bdfd8f6 100644 --- a/SaxoniteCoreWebAdmin/Admin/Controllers/UserController.cs +++ b/SaxoniteCoreWebAdmin/Admin/Controllers/UserController.cs @@ -31,8 +31,19 @@ namespace Saxonite.Net.Core.Web.Admin.Controllers if (!ValidUserSession) return RedirectToAction("", ""); - // Paging. + // Model. Model_User model = new Model_User(Localiser); + + // Stored Sport filter. + model.Filter.UserSportId = SessionData_Admin.Filter.SportId?.ToString() ?? null; + using (SaxSport filterSport = ReferenceMgr.GetSportById(SessionData_Admin.Filter.SportId)) + model.Filter.UserSportName = Localiser.GetSportName(SessionData_Admin.Filter.SportId, filterSport?.Name ?? ""); + // Stored Region filter. + model.Filter.UserRegionId = SessionData_Admin.Filter.RegionId?.ToString() ?? null; + using (SaxRegion filterRegion = ReferenceMgr.GetRegionById(SessionData_Admin.Filter.RegionId)) + model.Filter.UserRegionName = Localiser.GetRegionName(SessionData_Admin.Filter.RegionId, filterRegion?.Name ?? ""); + + // Paging. model.Paging.PageNo = 1; model.Paging.PageLength = 12; diff --git a/SaxoniteCoreWebAdmin/Admin/Models/ModelData_Address.cs b/SaxoniteCoreWebAdmin/Admin/Models/ModelData_Address.cs index 3c6636c..51d94c7 100644 --- a/SaxoniteCoreWebAdmin/Admin/Models/ModelData_Address.cs +++ b/SaxoniteCoreWebAdmin/Admin/Models/ModelData_Address.cs @@ -39,7 +39,7 @@ namespace Saxonite.Net.Core.Web.Admin.Models public String Town { get; set; } public String Province { get; set; } public String RegionIdProvince { get; set; } - public String Country { get; set; } + public String RegionIdCountry { get; set; } public String PostalCode { get; set; } //------------------------------------------------------------------- @@ -47,14 +47,14 @@ namespace Saxonite.Net.Core.Web.Admin.Models /// Have any address values been supplied by the form. public bool IsSpecified() { - if (Id != null || Line1 != null || Town != null || Province != null || Country != null || PostalCode != null) + if (Id != null || Line1 != null || Town != null || Province != null || RegionIdCountry != null || PostalCode != null) return true; return false; } /// Have any address values been populated on the form. public bool IsPopulated() { - if (SysId.IsValid(Id.ToSysId()) || !StringUtil.IsEmpty(Line1) || !StringUtil.IsEmpty(Town) || !StringUtil.IsEmpty(Province) || !StringUtil.IsEmpty(Country) || !StringUtil.IsEmpty(PostalCode)) + if (SysId.IsValid(Id.ToSysId()) || !StringUtil.IsEmpty(Line1) || !StringUtil.IsEmpty(Town) || !StringUtil.IsEmpty(Province) || !StringUtil.IsEmpty(RegionIdCountry) || !StringUtil.IsEmpty(PostalCode)) return true; return false; } @@ -63,7 +63,7 @@ namespace Saxonite.Net.Core.Web.Admin.Models // Validation. //------------------------------------------------------------------- // Address. - public bool ValidateAddressData(SaxController_Base.AjaxResponse_Data responseData) + public bool ValidateAddressData(SaxModel_Admin model, SaxController_Base.AjaxResponse_Data responseData) { // Are any properties populated. bool populated = IsPopulated(); @@ -101,32 +101,37 @@ namespace Saxonite.Net.Core.Web.Admin.Models // Address Town - mandatory if any value populated. if (Town != null) { - responseData.SetControlValue(ControlNameSuffix + "Town", Town); + String controlName = ControlNameSuffix + nameof(Town); + responseData.SetControlValue(controlName, Town); if (StringUtil.IsEmpty(Town) && populated) - responseData.SetControlError(ControlNameSuffix + "Town", Model.Localiser["msg.validation.mandatory.value"]); + responseData.SetControlError(controlName, Model.Localiser["msg.validation.mandatory.value"]); else if (StringUtil.GetLength(Town) > Model.GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.ItemAddress_Town)) - responseData.SetControlError(ControlNameSuffix + "Town", Model.Localiser["msg.validation.maximum.length", Model.GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.ItemAddress_Town)]); + responseData.SetControlError(controlName, Model.Localiser["msg.validation.maximum.length", Model.GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.ItemAddress_Town)]); } // Address Province - optional. if (Province != null) { - responseData.SetControlValue(ControlNameSuffix + "Province", Province); + String controlName = ControlNameSuffix + nameof(Province); + responseData.SetControlValue(controlName, Province); if (!StringUtil.IsEmpty(Province)) { if (StringUtil.GetLength(Province) > Model.GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.ItemAddress_Province)) - responseData.SetControlError(ControlNameSuffix + "Province", Model.Localiser["msg.validation.maximum.length", Model.GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.ItemAddress_Province)]); + responseData.SetControlError(controlName, Model.Localiser["msg.validation.maximum.length", Model.GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.ItemAddress_Province)]); } } - // Address Country - optional. - if (Country != null) + // Address Country - mandatory if any value populated. + if (RegionIdCountry != null) { - responseData.SetControlValue(ControlNameSuffix + "Country", Country); - if (!StringUtil.IsEmpty(Country)) + String controlName = ControlNameSuffix + nameof(RegionIdCountry); + if (!SysId.IsValid(RegionIdCountry.ToSysId()) && populated) + responseData.SetControlError(controlName, Model.Localiser["msg.validation.mandatory.value"]); + else if (SysId.IsValid(RegionIdCountry.ToSysId())) { - if (StringUtil.GetLength(Country) > Model.GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.ItemAddress_Country)) - responseData.SetControlError(ControlNameSuffix + "Country", Model.Localiser["msg.validation.maximum.length", Model.GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.ItemAddress_Country)]); + SaxRegion.Data region = model.Internals.GetRegion(RegionIdCountry.ToSysId()); + if (region == null || region.RegionType != Sax.RegionType.Country) + responseData.SetControlError(controlName, Model.Localiser["msg.validation.invalid.value"]); } } diff --git a/SaxoniteCoreWebAdmin/Admin/Models/Model_Organisation.cs b/SaxoniteCoreWebAdmin/Admin/Models/Model_Organisation.cs index 8683e31..7b30a0f 100644 --- a/SaxoniteCoreWebAdmin/Admin/Models/Model_Organisation.cs +++ b/SaxoniteCoreWebAdmin/Admin/Models/Model_Organisation.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Rendering; using Sarand.Net.Standard.Base.App; using Sarand.Net.Standard.Base.Bus; using Sarand.Net.Standard.Base.Core; @@ -34,7 +35,9 @@ namespace Saxonite.Net.Core.Web.Admin.Models public String OrganisationTypeCode { get; set; } public String OrganisationCategoryCode { get; set; } public String OrganisationSportId { get; set; } + public String OrganisationSportName { get; set; } public String OrganisationRegionId { get; set; } + public String OrganisationRegionName { get; set; } public String OrganisationStatusCategoryCode { get; set; } } public FilterData Filter { get; } = new FilterData(); @@ -75,7 +78,7 @@ namespace Saxonite.Net.Core.Web.Admin.Models public String OrganisationCategoryValue { get; set; } public Sax.OrganisationCategory OrganisationCategory { get { return (Sax.OrganisationCategory)OrganisationCategoryCodes.GetTypeForCode(OrganisationCategoryValue); } } public String OrganisationSportId { get; set; } - public String OrganisationRegionIdCountry { get; set; } + public String OrganisationRegionIdPrimary { get; set; } public String OrganisationAbout { get; set; } public String OrganisationFacilities { get; set; } // Common properties. @@ -92,6 +95,11 @@ namespace Saxonite.Net.Core.Web.Admin.Models // Address. public ModelData_Address Address { get; } + //------------------------------------------------------------------- + // Images. + public List OrganisationLogoImage { get; set; } + + //------------------------------------------------------------------- // Utilities. public bool IsSportMandatory() { @@ -143,15 +151,15 @@ namespace Saxonite.Net.Core.Web.Admin.Models } } - // Organisation region. - if (OrganisationRegionIdCountry != null) + // Organisation primary region. + if (OrganisationRegionIdPrimary != null) { - using (SaxRegion region = ReferenceMgr.GetRegionById(OrganisationRegionIdCountry.ToSysId())) + using (SaxRegion region = ReferenceMgr.GetRegionById(OrganisationRegionIdPrimary.ToSysId())) { if (region == null) - responseData.SetControlError(nameof(OrganisationRegionIdCountry), Localiser["msg.validation.invalid.value"]); + responseData.SetControlError(nameof(OrganisationRegionIdPrimary), Localiser["msg.validation.invalid.value"]); else if (region.RegionType != Sax.RegionType.Country && region.RegionType != Sax.RegionType.Continent && region.RegionType != Sax.RegionType.World) - responseData.SetControlError(nameof(OrganisationRegionIdCountry), "Organisation region must be a country or international region"); + responseData.SetControlError(nameof(OrganisationRegionIdPrimary), "Organisation region must be a country or international region"); } if (!StringUtil.IsEmpty(OrganisationName)) @@ -161,7 +169,7 @@ namespace Saxonite.Net.Core.Web.Admin.Models if (SysId.IsValid(OrganisationId.ToSysId())) prmOrganisation.IdsExcluded.Add(OrganisationId.ToSysId()); prmOrganisation.NameFull = OrganisationName; - prmOrganisation.RegionIdsIncluded.Add(OrganisationRegionIdCountry.ToSysId()); + prmOrganisation.RegionIdsIncluded.Add(OrganisationRegionIdPrimary.ToSysId()); prmOrganisation.StatusCat = SaxOrganisation.StatusCat.Any; if (AccountMgr.GetOrganisationCount(prmOrganisation) > 0) responseData.SetControlError(nameof(OrganisationName), Localiser["msg.validation.duplicate.value"]); @@ -226,7 +234,7 @@ namespace Saxonite.Net.Core.Web.Admin.Models // Validate address fields. if (Address != null) - Address.ValidateAddressData(responseData); + Address.ValidateAddressData(this, responseData); return !responseData.HasControlErrors(); } diff --git a/SaxoniteCoreWebAdmin/Admin/Models/Model_User.cs b/SaxoniteCoreWebAdmin/Admin/Models/Model_User.cs index 7e835c6..b05d94e 100644 --- a/SaxoniteCoreWebAdmin/Admin/Models/Model_User.cs +++ b/SaxoniteCoreWebAdmin/Admin/Models/Model_User.cs @@ -35,7 +35,9 @@ namespace Saxonite.Net.Core.Web.Admin.Models { public String User { get; set; } public String UserSportId { get; set; } + public String UserSportName { get; set; } public String UserRegionId { get; set; } + public String UserRegionName { get; set; } public String UserStatusCategoryCode { get; set; } } public FilterData Filter { get; } = new FilterData(); diff --git a/SaxoniteCoreWebAdmin/Localisation/SaxAdminProperties.resx b/SaxoniteCoreWebAdmin/Localisation/SaxAdminProperties.resx index 59083d7..c3a1804 100644 --- a/SaxoniteCoreWebAdmin/Localisation/SaxAdminProperties.resx +++ b/SaxoniteCoreWebAdmin/Localisation/SaxAdminProperties.resx @@ -120,9 +120,15 @@ Contact Method + + Image Upload + Organisation Address + + Organisation Images + Sportierre @@ -177,6 +183,9 @@ Admin + + Alternatively + Any @@ -258,6 +267,9 @@ Forgotten password? + + Image + Images @@ -291,6 +303,9 @@ Location + + Logo + Logs @@ -342,6 +357,9 @@ Online today + + Organisation + Page {0} of {1} @@ -441,6 +459,9 @@ update + + Upload + User roles @@ -462,6 +483,9 @@ Refreshing data. Please wait ... + + Drag image here + Data is incomplete @@ -489,6 +513,33 @@ Unfortunately, something has gone wrong.\n\nYou will be returned to the home page. + + Please select a file to upload. + + + Maximum image file size is {0}. + + + Unsupported file type: {0}. + + + Uploading {0}. + + + Invalid image file uploaded + + + Invalid image content type. + + + Invalid image mime type. + + + Image resize failed. + + + Only one image can be uploaded. + Value is already in use diff --git a/SaxoniteCoreWebAdmin/Views/Common/ContactMethods.cshtml b/SaxoniteCoreWebAdmin/Views/Common/ContactMethods.cshtml index 57e0227..c64c6c8 100644 --- a/SaxoniteCoreWebAdmin/Views/Common/ContactMethods.cshtml +++ b/SaxoniteCoreWebAdmin/Views/Common/ContactMethods.cshtml @@ -38,7 +38,7 @@
- +
diff --git a/SaxoniteCoreWebAdmin/Views/Common/Controls-ItemAddress.cshtml b/SaxoniteCoreWebAdmin/Views/Common/Controls-ItemAddress.cshtml index 73ed2cc..c107892 100644 --- a/SaxoniteCoreWebAdmin/Views/Common/Controls-ItemAddress.cshtml +++ b/SaxoniteCoreWebAdmin/Views/Common/Controls-ItemAddress.cshtml @@ -97,3 +97,22 @@ + +@* Address country *@ +
+
+
+
+ +
+
+
+
+ +
+
+
+
+
+
+
diff --git a/SaxoniteCoreWebAdmin/Views/Common/Controls-ItemContactMethods.cshtml b/SaxoniteCoreWebAdmin/Views/Common/Controls-ItemContactMethods.cshtml index 06e39d0..5542d30 100644 --- a/SaxoniteCoreWebAdmin/Views/Common/Controls-ItemContactMethods.cshtml +++ b/SaxoniteCoreWebAdmin/Views/Common/Controls-ItemContactMethods.cshtml @@ -49,7 +49,7 @@
@* List of existing contact methods with actions to edit/remove. *@
-
+
diff --git a/SaxoniteCoreWebAdmin/Views/Organisation/Index.cshtml b/SaxoniteCoreWebAdmin/Views/Organisation/Index.cshtml index 8de757e..d2a4b8c 100644 --- a/SaxoniteCoreWebAdmin/Views/Organisation/Index.cshtml +++ b/SaxoniteCoreWebAdmin/Views/Organisation/Index.cshtml @@ -67,7 +67,7 @@
@Html.HiddenFor(model => model.Filter.OrganisationSportId, new { @id = "", @class = "FilterOrganisationSportId" }) -
+
@(Localiser[
@(Localiser[
@@ -81,7 +81,7 @@
@Html.HiddenFor(model => model.Filter.OrganisationRegionId, new { @id = "", @class = "FilterOrganisationRegionId" }) -
+
@(Localiser[
@(Localiser[
@@ -144,12 +144,19 @@
-
-
-
-
-
@Localiser["label.members"]: 
-
+
+ +
+
+
+
+
+
+
+
@Localiser["label.members"]: 
+
+
+
@@ -162,9 +169,19 @@
+ @* Organisation Id form - used in Ajax calls to identify organisation. *@ + @using (@Html.BeginForm(FormMethod.Post, new { @id = "frmOrganisationId", @class = "sax-form" })) + { + @Html.HiddenFor(model => model.OrganisationId, new { @class = "OrganisationId" }) + } + @* Organisation dialog and form. *@ + + @* Organisation Activation dialog and form. *@ + + @* Organisation Address dialog, form and script. *@ + + @* Organisation Images dialog, form and script. *@ + + @* Select Item dialog and script. *@ +
- - - - - diff --git a/SaxoniteCoreWebAdmin/Views/Organisation/Panel-OrganisationAddressDialog.cshtml b/SaxoniteCoreWebAdmin/Views/Organisation/Panel-OrganisationAddressDialog.cshtml index f2279b8..ebfe9bb 100644 --- a/SaxoniteCoreWebAdmin/Views/Organisation/Panel-OrganisationAddressDialog.cshtml +++ b/SaxoniteCoreWebAdmin/Views/Organisation/Panel-OrganisationAddressDialog.cshtml @@ -4,6 +4,8 @@ /* Organisations - Organisation Address dialog panel */ } + +
@@ -16,18 +18,19 @@
- +
- @Html.TextBoxFor(model => model.OrganisationName, new { @id = "ifOrganisationName", @class = "OrganisationName sax-input", @maxlength = @Model.GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.Organisation_Name), disabled = true }) +
+ @* Address controls *@ } diff --git a/SaxoniteCoreWebAdmin/Views/Organisation/Panel-OrganisationDialog.cshtml b/SaxoniteCoreWebAdmin/Views/Organisation/Panel-OrganisationDialog.cshtml index 489a3d7..6f9cbd1 100644 --- a/SaxoniteCoreWebAdmin/Views/Organisation/Panel-OrganisationDialog.cshtml +++ b/SaxoniteCoreWebAdmin/Views/Organisation/Panel-OrganisationDialog.cshtml @@ -10,10 +10,6 @@ sax.dlg.title.selectRegion = '@Localiser.GetJavascriptText("label.select.country")'; sax.dlg.title.selectSport = '@Localiser.GetJavascriptText("sport.caption.sport.select")'; -@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmOrganisationId", @class = "sax-form" })) -{ - @Html.HiddenFor(model => model.OrganisationId, new { @class = "OrganisationId" }) -}
@@ -94,19 +90,19 @@
- @* Organisation country *@ -
+ @* Organisation primary region *@ +
- +
- @Html.HiddenFor(model => model.OrganisationRegionIdCountry, new { @id = "ifOrganisationRegionIdCountry", @class = "OrganisationRegionIdCountry" }) -
-
+ @Html.HiddenFor(model => model.OrganisationRegionIdPrimary, new { @id = "ifOrganisationRegionIdPrimary", @class = "OrganisationRegionIdPrimary" }) +
+
diff --git a/SaxoniteCoreWebAdmin/Views/Organisation/Panel-OrganisationImagesDialog.cshtml b/SaxoniteCoreWebAdmin/Views/Organisation/Panel-OrganisationImagesDialog.cshtml new file mode 100644 index 0000000..b1bb0d6 --- /dev/null +++ b/SaxoniteCoreWebAdmin/Views/Organisation/Panel-OrganisationImagesDialog.cshtml @@ -0,0 +1,125 @@ +@model Model_Organisation +@inject SaxLocaliser Localiser +@{ + /* Organisations - Organisation images dialog panel */ +} + + + + + +
+
+ + @using (@Html.BeginForm(FormMethod.Post, new { @id = "frmOrganisationImages", @class = "sax-form", @onSubmit = "return false;" })) + { + @Html.HiddenFor(model => model.OrganisationId, new { @id = "", @class = "OrganisationId" }) + + @* Organisation name *@ +
+
+
+
+ +
+
+
+
+ +
+
+
+
+
+ + @* Organisation Logo *@ +
+
+
+
+ +
+
+
+
+
+
+
+
+
+ @Localiser["msg.drag.image.here"] +
+
+
+ @Localiser["label.alternatively"]:  + +
+
+ +
+
+
+
+
+
+
+ + } + +
+
+ +
+ @using (@Html.BeginForm(FormMethod.Post, new { @id = "frmSelectOrgLogoImage", @class = "sax-form", @onSubmit = "return false;" })) + { +
+
+
+
@Localiser["label.image"]
+
+
+
+
+
+
+
+
+
+
+ } +
diff --git a/SaxoniteCoreWebAdmin/Views/User/Index.cshtml b/SaxoniteCoreWebAdmin/Views/User/Index.cshtml index acd1de7..32090e5 100644 --- a/SaxoniteCoreWebAdmin/Views/User/Index.cshtml +++ b/SaxoniteCoreWebAdmin/Views/User/Index.cshtml @@ -55,7 +55,7 @@
@Html.HiddenFor(model => model.Filter.UserSportId, new { @id = "", @class = "FilterUserSportId" }) -
+
@(Localiser[
@(Localiser[
@@ -69,7 +69,7 @@
@Html.HiddenFor(model => model.Filter.UserRegionId, new { @id = "", @class = "FilterUserRegionId" }) -
+
@(Localiser[
@(Localiser[
diff --git a/SaxoniteCoreWebAdmin/wwwroot/css/sax-data.css b/SaxoniteCoreWebAdmin/wwwroot/css/sax-data.css index 80eb7b8..0fdd2e3 100644 --- a/SaxoniteCoreWebAdmin/wwwroot/css/sax-data.css +++ b/SaxoniteCoreWebAdmin/wwwroot/css/sax-data.css @@ -36,6 +36,11 @@ font-weight: bold; } +.sax-panel-filter .sax-header ul.nav.sax-burger +{ + margin-top: 2px; +} + .sax-panel-filter .sax-header .sax-action { margin: 2px 0 0 8px; @@ -50,8 +55,8 @@ .sax-panel-filter .sax-content { - margin: 4px 8px; - padding: 4px 8px; + margin: 4px 4px; + padding: 4px 4px; background-color: #f8f8f8; } @@ -67,6 +72,7 @@ { min-width: 75%; max-width: 90%; + width:90%; } .sax-panel-filter .sax-content .sax-button @@ -80,7 +86,7 @@ .sax-results-paging { - margin-right: 8px; + margin-right: 12px; } .sax-results-paging .sax-paging-info diff --git a/SaxoniteCoreWebAdmin/wwwroot/img/animated/progress-basketball-32.gif b/SaxoniteCoreWebAdmin/wwwroot/img/animated/progress-basketball-32.gif new file mode 100644 index 0000000000000000000000000000000000000000..15cb2603d5af1c37dcd37100af3c4182c920f8d8 GIT binary patch literal 21788 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 AHUIzs literal 0 HcmV?d00001 diff --git a/SaxoniteCoreWebAdmin/wwwroot/img/animated/progress-basketball-64.gif b/SaxoniteCoreWebAdmin/wwwroot/img/animated/progress-basketball-64.gif new file mode 100644 index 0000000000000000000000000000000000000000..e17a18b25a4f99fd387a9fc50e97bdfc9a18c90f GIT binary patch literal 41296 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%0AI$(ESBdB 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 DyzFF` literal 0 HcmV?d00001 diff --git a/SaxoniteCoreWebAdmin/wwwroot/js/sax-main.js b/SaxoniteCoreWebAdmin/wwwroot/js/sax-main.js index 4b17fc9..2e88626 100644 --- a/SaxoniteCoreWebAdmin/wwwroot/js/sax-main.js +++ b/SaxoniteCoreWebAdmin/wwwroot/js/sax-main.js @@ -67,6 +67,11 @@ { return (val || '').replace(/[[{}()*+?^$|\]\.\\]/g, "\\$&"); } + sax.haltEvent = function (evt) + { + evt.preventDefault(); + evt.stopPropagation(); + } sax.debug = sax.debugMode ? win.console.log.bind(win.console) : function () { }; //-------------------------------------------------------------- diff --git a/SaxoniteCoreWebAdmin/wwwroot/js/sax-org-add.js b/SaxoniteCoreWebAdmin/wwwroot/js/sax-org-add.js new file mode 100644 index 0000000..23b7e22 --- /dev/null +++ b/SaxoniteCoreWebAdmin/wwwroot/js/sax-org-add.js @@ -0,0 +1,111 @@ +/* + * Admin - Organisation Address scripts. + */ + +(function (win, doc, undef) +{ + "use strict"; + let oa = sax.mdl.organisationAddress = { data: {}, ajax: {} }; + let $pnl = oa.$pnl = {}, $frm = oa.$frm = {}, $dlg = oa.$dlg = {}; + let mdlOrg = function () { return sax.mdl.organisations; } + + //-------------------------------------------------------------- + oa.updateItemAddress = function (item, $item) + { + oa.init(); + $('.OrganisationId', $frm.orgId).val(item.id); + oa.ajax.updateItemAddr = oa.ajax.updateItemAddr || { url: sax.url.base + 'Organisation/Ajax/GetItem', formId: 'frmOrganisationId', callbackFn: oa.onUpdateItemAddress }; + sax.doAjaxRequest(oa.ajax.updateItemAddr, 50); + } + oa.onUpdateItemAddress = function (response) + { + //sax.debug('onUpdateItemAddress', response); + if (!response.success) return; + // Address. + let item = response.organisation; + let addr = item.address || {}; + let frm = $frm.orgAddr; + $('input.OrganisationId', frm).val(item.id); + $('.OrganisationName', frm).val(item.name); + $('input.AddressId', frm).val(addr.id); + $('input.AddressLine1', frm).val(addr.line1); + $('input.AddressLine2', frm).val(addr.line2); + $('input.AddressLine3', frm).val(addr.line3); + $('input.AddressTown', frm).val(addr.town); + $('input.AddressRegionIdProvince', frm).val(addr.regionIdProvince); + $('input.AddressProvince', frm).val(addr.regionNameProvince); + $('input.AddressPostalCode', frm).val(addr.postalCode); + $('input.AddressRegionIdCountry, input.AddressRegionNameCountry', frm).val(''); + if (addr.regionIdCountry) + { + $('input.AddressRegionIdCountry', frm).val(addr.regionIdCountry); + $('input.AddressRegionNameCountry', frm).val(addr.regionNameCountry); + } + else if (item.regionPrimary.country) + { + $('input.AddressRegionIdCountry', frm).val(item.regionPrimary.id); + $('input.AddressRegionNameCountry', frm).val(item.regionPrimary.name); + } + sax.clearControlErrors(frm); + sax.openDialog($dlg.orgAddr, 640, 480); + } + + //-------------------------------------------------------------- + oa.selectOrgAddrRegionCountry = function () + { + if (!mdlOrg().data.regions) + mdlOrg().populateRegions(oa.showSelectOrgAddrRegionCountry); + else oa.showSelectOrgAddrRegionCountry(); + } + oa.showSelectOrgAddrRegionCountry = function () + { + let regions = []; + for (let rgn of mdlOrg().data.regions) + if (rgn.regionType == 30) regions.push(rgn); + sax.data.selectItem.fn.selectItem({ caption: sax.dlg.title.selectCountry, itemId: $('input.AddressRegionIdCountry', $frm.orgAddr).val(), items: regions, fnCallback: oa.onOrgAddrRegionCountrySelected }); + } + oa.onOrgAddrRegionCountrySelected = function (rgn) + { + //sax.debug('onRegionSelected', rgn); + let frm = $frm.orgAddr; + sax.clearControlError($('input.AddressRegionIdCountry', frm).val(rgn.id)); + $('.AddressRegionNameCountry', frm).val(rgn.name); + } + + //-------------------------------------------------------------- + oa.storeItemAddress = function () + { + oa.ajax.storeItemAddr = oa.ajax.storeItemAddr || { url: sax.url.base + 'Organisation/Ajax/StoreItem', formId: 'frmOrganisationAddress', callbackFn: oa.onStoreItemAddress }; + sax.doAjaxRequest(oa.ajax.storeItemAddr, 50); + } + oa.onStoreItemAddress = function (response) + { + //sax.debug('onStoreItemAddress', response); + sax.refreshControlValues(response.controlValues, $frm.orgAddr); + if (!response.success) return; + $dlg.orgAddr.dialog('close'); + mdlOrg().refreshItem(response.organisation); + } + + //-------------------------------------------------------------- + oa.init = function () + { + if ($frm.orgId) return; + $frm.orgId = $('#frmOrganisationId'); + + // Organisation Address dialog. + var opt = sax.dlgOpts(); + opt.buttons[sax.dlg.btn.ok] = function () { oa.storeItemAddress(); }; + opt.buttons[sax.dlg.btn.cancel] = function () { $(this).dialog('close'); }; + $dlg.orgAddr = $('#dlgOrganisationAddress').dialog(opt); + + // Organisation Address form. + $frm.orgAddr = $('#frmOrganisationAddress'); + $frm.orgAddr.on('input', '.sax-input, .sax-textarea', function () { sax.clearControlError(this); }).on('change', '.sax-select', function () { sax.clearControlError(this); }); + $frm.orgAddr.on('dblclick', '.AddressRegionNameCountry', oa.selectOrgAddrRegionCountry).on('click', '.AddressRegionCountrySelect', oa.selectOrgAddrRegionCountry); + } + + //-------------------------------------------------------------- +}(window, window.document)); + +/* The End */ \ No newline at end of file diff --git a/SaxoniteCoreWebAdmin/wwwroot/js/sax-org-img.js b/SaxoniteCoreWebAdmin/wwwroot/js/sax-org-img.js new file mode 100644 index 0000000..8eba681 --- /dev/null +++ b/SaxoniteCoreWebAdmin/wwwroot/js/sax-org-img.js @@ -0,0 +1,143 @@ +/* + * Admin - Organisation Images scripts. + */ + +(function (win, doc, undef) +{ + "use strict"; + let oi = sax.mdl.organisationImages = { data: {}, ajax: {} }; + let $pnl = oi.$pnl = {}, $frm = oi.$frm = {}, $dlg = oi.$dlg = {}; + oi.classFileDrag = 'sax-file-drag'; + let mdlOrg = function () { return sax.mdl.organisations; } + + //-------------------------------------------------------------- + oi.updateItemImages = function (item, $item) + { + oi.init(); + $('.OrganisationId', $frm.orgId).val(item.id); + oi.ajax.getOrgImages = oi.ajax.getOrgImages || { url: sax.url.base + 'Organisation/Ajax/GetImages', formId: 'frmOrganisationId', callbackFn: oi.onUpdateItemImages }; + sax.doAjaxRequest(oi.ajax.getOrgImages, 50); + } + oi.onUpdateItemImages = function (response) + { + //sax.debug('onUpdateItemImages', response); + if (!response.success) return; + oi.data.imageContentTypes = response.imageContentTypes; + let item = response.organisation; + let frm = $frm.orgImg; + $('.OrganisationId', frm).val(item.id); + $('.OrganisationName', frm).val(item.name); + $('img.OrganisationLogo', frm).prop('src', item.logoImageUrl).toggle(!!item.logoImageUrl); + + sax.clearControlErrors(frm); + sax.openDialog($dlg.orgImg, 640, 480); + } + + //-------------------------------------------------------------- + // Drop organisation logo image. + oi.onDropOrgLogoImage = function (evt) + { + //sax.debug('onDropOrgLogoImage', evt); + let files = evt.originalEvent.dataTransfer.files; + if (files.length < 1) return; + if (files.length > 1) return sax.doAlert(sax.dlg.title.imageUpload, sax.msg.uploadImageSingle); + oi.onDropOrgLogoImageFile(files[0], $frm.orgImg); + } + oi.onDropOrgLogoImageFile = function (file, context) + { + //sax.debug('onDropOrgLogoImageFile', file, context); + if (file.size > sax.maxProfileImageSize) return sax.doAlert(sax.dlg.title.imageUpload, sax.msg.uploadFileSizeMax.format(sax.formatFileSize(sax.maxProfileImageSize))); + if ((oi.data.imageContentTypes || []).indexOf(file.type) < 0) return sax.doAlert(sax.dlg.title.imageUpload, sax.msg.uploadFileTypeInvalid.format(sax.getFileType(file))); + $(".UploadMessage", context).text(sax.msg.uploadingFile.format(file.name)); + $('img.ImageUploadProgress', context).fadeIn(200); + sax.clearControlError($pnl.orgLogoImageDrag); + oi.data.orgLogoImageUploadContext = context; + + oi.ajax.uploadOrgLogoImage = sax.ajax.uploadOrgLogoImage || { url: sax.url.base + 'Organisation/Ajax/UploadLogoImage', callbackFn: oi.handleOrgLogoImageUploadResponse, noWaitIcon: true }; + let ajaxData = oi.ajax.uploadOrgLogoImage.formData = new FormData(); + ajaxData.append("__RequestVerificationToken", $('#frmNavigate input[name="__RequestVerificationToken"]').val()); + ajaxData.append("OrganisationId", $('.OrganisationId', $frm.orgImg).val()); + ajaxData.append("OrganisationLogoImage", file); + sax.doAjaxRequest(oi.ajax.uploadOrgLogoImage, 50); + } + oi.handleOrgLogoImageUploadResponse = function (response) + { + //sax.debug('handleOrgLogoImageUploadResponse', response, context); + let context = oi.data.orgLogoImageUploadContext; + if (!response) return; + sax.refreshControlValues(response.controlValues, context); + $(".UploadMessage", context).empty(); + $('img.ImageUploadProgress', context).hide(); + if (response.success) + { + $dlg.selectOrgLogoImage.dialog('close'); + $('img.OrganisationLogo', $pnl.orgLogo).prop('src', response.imageUrl).show(); + } + } + + //-------------------------------------------------------------- + // Select organisation logo image. + oi.selectOrgLogoImage = function () + { + let dlg = $dlg.selectOrgLogoImage; + $('.OrganisationLogoImage', dlg).val(''); + sax.openDialog(dlg, 560, 240); + } + oi.uploadOrgLogoImage = function () + { + let dlg = $dlg.selectOrgLogoImage; + let $fileCtrl = $('.OrganisationLogoImage', dlg); + if (!$fileCtrl.length || !$fileCtrl[0].files.length) + return sax.doAlert(sax.dlg.title.imageUpload, sax.msg.uploadFileSelect); + let file = $fileCtrl[0].files[0]; + oi.onDropOrgLogoImageFile(file, dlg); + } + + //-------------------------------------------------------------- + oi.storeItemImages = function () + { + oi.ajax.storeOrgImages = oi.ajax.storeOrgImages || { url: sax.url.base + 'Organisation/Ajax/StoreImages', formId: 'frmOrganisationImages', callbackFn: oi.onStoreItemImages }; + sax.doAjaxRequest(oi.ajax.storeOrgImages, 50); + } + oi.onStoreItemImages = function (response) + { + //sax.debug('onStoreItemImages', response); + sax.refreshControlValues(response.controlValues, $frm.orgImg); + if (!response.success) return; + $dlg.orgImg.dialog('close'); + mdlOrg().refreshItem(response.organisation); + } + + //-------------------------------------------------------------- + oi.init = function () + { + if ($frm.orgId) return; + $frm.orgId = $('#frmOrganisationId'); + + // Organisation Images dialog and form. + var opt = sax.dlgOpts(); + opt.buttons[sax.dlg.btn.ok] = function () { oi.storeItemImages(); }; + opt.buttons[sax.dlg.btn.cancel] = function () { $(this).dialog('close'); }; + $dlg.orgImg = $('#dlgOrganisationImages').dialog(opt); + $frm.orgImg = $('#frmOrganisationImages', $dlg.orgImg); + + // Organisation logo. + $pnl.orgLogo = $('.sax-form-control.OrganisationLogoImage', $dlg.orgImg); + $pnl.orgLogoImgDrop = $('.sax-panel-drop.OrganisationLogoImageDrop', $pnl.orgLogo) + .on('dragenter', function (evt) { sax.haltEvent(evt); }) + .on('dragover', function (evt) { sax.haltEvent(evt); if (!$pnl.orgLogoImgDrop.hasClass(oi.classFileDrag)) $pnl.orgLogoImgDrop.addClass(oi.classFileDrag); }) + .on('dragleave', function (evt) { sax.haltEvent(evt); $pnl.orgLogoImgDrop.removeClass(oi.classFileDrag); }) + .on('drop', function (evt) { sax.haltEvent(evt); $pnl.orgLogoImgDrop.removeClass(oi.classFileDrag); oi.onDropOrgLogoImage(evt); }) + .on('click', '.sax-button.SelectOrgLogoImage', oi.selectOrgLogoImage); + + // Select image dialog. + var opt = sax.dlgOpts(); + opt.buttons[sax.dlg.btn.ok] = function () { oi.uploadOrgLogoImage(); }; + opt.buttons[sax.dlg.btn.cancel] = function () { $(this).dialog('close'); }; + $dlg.selectOrgLogoImage = $('#dlgSelectOrgLogoImage').dialog(opt); + } + + //-------------------------------------------------------------- +}(window, window.document)); + +/* The End */ \ No newline at end of file diff --git a/SaxoniteCoreWebAdmin/wwwroot/js/sax-org.js b/SaxoniteCoreWebAdmin/wwwroot/js/sax-org.js index 2de99fb..96bb5cc 100644 --- a/SaxoniteCoreWebAdmin/wwwroot/js/sax-org.js +++ b/SaxoniteCoreWebAdmin/wwwroot/js/sax-org.js @@ -5,9 +5,8 @@ (function (win, doc, undef) { "use strict"; - let op = sax.mdl.organisations = sax.mdl.organisations || { data: {}, ajax: {} }; + let op = sax.mdl.organisations = { data: {}, ajax: {} }; let $pnl = op.$pnl = {}, $frm = op.$frm = {}, $dlg = op.$dlg = {}; - sax.itemIdPrefix = 'Itm_'; //-------------------------------------------------------------- op.refreshResults = function (delay) @@ -47,15 +46,21 @@ $items.append(op.populateItem(op.getItemTemplate(), items[itemIdx])); $items.fadeIn(50); } + op.getItemIdProp = function (id) + { + return 'Org_' + id; + } op.populateItem = function ($item, item) { + //sax.debug('populateItem', item); if (!item) return; - $item.prop('id', sax.itemIdPrefix + item.id); + $item.prop('id', op.getItemIdProp(item.id)); + $('img.OrganisationLogo', $item).prop('src', item.logoImageUrl).toggle(!!item.logoImageUrl); $('.OrganisationName', $item).text(item.name); $('.OrganisationType', $item).text(item.typeDesc); $('.OrganisationCategory', $item).text(item.categoryDesc); $('.OrganisationSport', $item).text(item.sportName); - $('.OrganisationRegion', $item).text(item.regionNameCountry); + $('.OrganisationRegion', $item).text(item.regionPrimary.name); $('.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); @@ -224,15 +229,15 @@ } op.showSelectOrgRegion = function () { - sax.data.selectItem.fn.selectItem({ caption: sax.dlg.title.selectRegion, itemId: $('.OrganisationRegionIdCountry', $frm.org).val(), items: op.data.regions, fnCallback: op.onOrgRegionSelected }); + sax.data.selectItem.fn.selectItem({ caption: sax.dlg.title.selectRegion, itemId: $('.OrganisationRegionIdPrimary', $frm.org).val(), items: op.data.regions, fnCallback: op.onOrgRegionSelected }); } op.onOrgRegionSelected = function (rgn) { //sax.debug('onRegionSelected', rgn); let frm = $frm.org; - $('.OrganisationRegionIdCountry', frm).val(rgn.id); - $('.OrganisationRegionCaptionCountry', frm).val(rgn.name); - sax.clearControlError($('.OrganisationRegionCaptionCountry', frm)); + $('.OrganisationRegionIdPrimary', frm).val(rgn.id); + $('.OrganisationRegionNamePrimary', frm).val(rgn.name); + sax.clearControlError($('.OrganisationRegionIdPrimary', frm)); } //-------------------------------------------------------------- @@ -251,7 +256,7 @@ op.clearItem = function () { let frm = $frm.org; - $('.OrganisationId,.OrganisationSportId,.OrganisationRegionIdCountry,input[type="text"],textarea,select', frm).val(''); + $('.OrganisationId,.OrganisationSportId,.OrganisationRegionIdPrimary,input[type="text"],textarea,select', frm).val(''); $pnl.tblconMths.empty(); $('.OrganisationStatus', frm).val(op.data.defaultStatusCode); sax.clearControlErrors(); @@ -285,8 +290,8 @@ $('.OrganisationCategory', frm).val(item.categoryCode); $('.OrganisationSportId', frm).val(item.sportId); $('.OrganisationSportCaption', frm).val(item.sportName); - $('.OrganisationRegionIdCountry', frm).val(item.regionIdCountry); - $('.OrganisationRegionCaptionCountry', frm).val(item.regionNameCountry); + $('.OrganisationRegionIdPrimary', frm).val(item.regionPrimary.id); + $('.OrganisationRegionNamePrimary', frm).val(item.regionPrimary.name); $('.OrganisationAbout', frm).val(item.about); $('.OrganisationFacilities', frm).val(item.facilities); $('.OrganisationNotes', frm).val(item.notes); @@ -374,7 +379,7 @@ } op.refreshItem = function (item) { - var $item = $('#' + sax.itemIdPrefix + item.id); + var $item = $('#' + op.getItemIdProp(item.id)); if (!$item.length) { $item = op.getItemTemplate(); @@ -404,7 +409,7 @@ } popItemValue('.OrganisationName', item.name); popItemValue('.OrganisationSport', item.sportName); - popItemValue('.OrganisationRegion', item.regionNameCountry); + popItemValue('.OrganisationRegion', item.regionPrimary.name); popItemValue('.OrganisationCategory', item.categoryDesc); popItemValue('.OrganisationAddress', item.addressSummary); let $orgMbrs = $('.OrganisationMembers', $dlg.activate).empty(); @@ -440,51 +445,12 @@ //-------------------------------------------------------------- op.updateItemImages = function (item, $item) { - sax.debug('updateItemImages'); + sax.mdl.organisationImages.updateItemImages(item, $item) } - //-------------------------------------------------------------- op.updateItemAddress = function (item, $item) { - sax.debug('updateItemAddress'); - $('.OrganisationId', $frm.orgId).val(item.id); - op.ajax.updateItemAddr = op.ajax.updateItemAddr || { url: sax.url.base + 'Organisation/Ajax/GetItem', formId: 'frmOrganisationId', callbackFn: op.onUpdateItemAddress }; - sax.doAjaxRequest(op.ajax.updateItemAddr, 50); - } - op.onUpdateItemAddress = function (response) - { - sax.debug('onUpdateItemAddress', response); - if (!response.success) return; - // Address. - let item = response.organisation; - let address = item.address || {}; - let frm = $frm.orgAddr; - $('input.OrganisationId', frm).val(item.id); - $('.OrganisationName', frm).val(item.name); - $('input.AddressId', frm).val(address.id); - $('input.AddressLine1', frm).val(address.line1); - $('input.AddressLine2', frm).val(address.line2); - $('input.AddressLine3', frm).val(address.line3); - $('input.AddressTown', frm).val(address.town); - $('input.AddressProvince', frm).val(address.province); - $('input.AddressCountry', frm).val(address.country); - $('input.AddressPostalCode', frm).val(address.postalCode); - sax.clearControlErrors(frm); - sax.openDialog($dlg.orgAddr, 640, 480); - } - op.storeItemAddress = function () - { - sax.debug('storeItemAddress'); - op.ajax.storeItemAddr = op.ajax.storeItemAddr || { url: sax.url.base + 'Organisation/Ajax/StoreItem', formId: 'frmOrganisationAddress', callbackFn: op.onStoreItemAddress }; - sax.doAjaxRequest(op.ajax.storeItemAddr, 50); - } - op.onStoreItemAddress = function (response) - { - sax.debug('onStoreItemAddress', response); - sax.refreshControlValues(response.controlValues, $frm.orgAddr); - if (!response.success) return; - $dlg.orgAddr.dialog('close'); - op.refreshItem(response.organisation); + sax.mdl.organisationAddress.updateItemAddress(item, $item); } //-------------------------------------------------------------- @@ -512,7 +478,7 @@ $frm.org = $('#frmOrganisation'); $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); + $frm.org.on('dblclick', '.OrganisationRegionNamePrimary', op.selectOrgRegion).on('click', '#pbSelectOrganisationRegionPrimary', op.selectOrgRegion); $frm.org.on('click', '.ItemContactMethods .sax-action.sax-add', op.addConMth); $pnl.conMths = $('.ItemContactMethods', $frm.org); $pnl.tblconMths = $('table.sax-data', $pnl.conMths); @@ -523,18 +489,10 @@ opt.buttons[sax.dlg.btn.cancel] = function () { $(this).dialog('close'); }; $dlg.activate = $('#dlgOrganisationActivation').dialog(opt); - // Activate Organisation Address dialog and form. - var opt = sax.dlgOpts(); - opt.buttons[sax.dlg.btn.ok] = function () { op.storeItemAddress(); }; - opt.buttons[sax.dlg.btn.cancel] = function () { $(this).dialog('close'); }; - $dlg.orgAddr = $('#dlgOrganisationAddress').dialog(opt); - - $frm.orgAddr = $('#frmOrganisationAddress'); - $frm.orgAddr.on('input', '.sax-input, .sax-textarea', function () { sax.clearControlError(this); }).on('change', '.sax-select', function () { sax.clearControlError(this); }); - setTimeout(op.refreshResults, 250); }); + //-------------------------------------------------------------- }(window, window.document)); /* The End */ \ No newline at end of file diff --git a/SaxoniteCoreWebOnline/Online/Controllers/AccountController-Ajax-Contact.cs b/SaxoniteCoreWebOnline/Online/Controllers/AccountController-Ajax-Contact.cs index 3ebbb8c..75bc3fd 100644 --- a/SaxoniteCoreWebOnline/Online/Controllers/AccountController-Ajax-Contact.cs +++ b/SaxoniteCoreWebOnline/Online/Controllers/AccountController-Ajax-Contact.cs @@ -105,40 +105,47 @@ namespace Saxonite.Net.Core.Web.Online.Controllers if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData); RefreshModel(model); - // Reference data - Genders. - responseData.Genders.Add("", Localiser["label.not.specified"]); - foreach (TypeCodeDesc gender in Internals.GenderCodes) - responseData.Genders.Add(gender.Type.ToString(), Localiser.GetGenderCaption((Sax.Gender)gender.Type)); - - // Reference data - Months. - for (int month = 1; month <= 12; month++) - responseData.Months.Add(month.ToString(), DateUtil.Format(new DateTime(DateTime.Today.Year, month, 1), "MMMM", SessionCulture)); - - // Profile image content types. - responseData.ImageContentTypes = SaxItemImage.GetValidContentTypes(Sax.DataItemType.UserAccount); - - // Retrieve user details. - SaxUserAccount userAccount = AccountMgr.GetUserAccountById(UserAccountId); - if (userAccount == 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 account for Id: " + SysId.ToString(UserAccountId)); - return Json(responseData.SetGoHome(messageTitle, Localiser["msg.something.gone.wrong"])); + + // Reference data - Genders. + responseData.Genders.Add("", Localiser["label.not.specified"]); + foreach (TypeCodeDesc gender in Internals.GenderCodes) + responseData.Genders.Add(gender.Type.ToString(), Localiser.GetGenderCaption((Sax.Gender)gender.Type)); + + // Reference data - Months. + for (int month = 1; month <= 12; month++) + responseData.Months.Add(month.ToString(), DateUtil.Format(new DateTime(DateTime.Today.Year, month, 1), "MMMM", SessionCulture)); + + // Reference data - Image content types. + responseData.ImageContentTypes = SaxItemImage.GetValidContentTypes(Sax.DataItemType.UserAccount); + + // Retrieve user details. + SaxUserAccount userAccount = AccountMgr.GetUserAccountById(UserAccountId); + disposableItems.Add(userAccount); + if (userAccount == null) + { + SaLogger.Warn(fn + " Failed to retrieve user account for Id: " + SysId.ToString(UserAccountId)); + return Json(responseData.SetGoHome(messageTitle, Localiser["msg.something.gone.wrong"])); + } + + // Logically delete any previous 'Uploaded' Avatar image. + ItemId userItemId = userAccount.GetItemId(); + SaxItemImage itemImageAvatarUploaded = ItemDataMgr.GetItemImage(userItemId, Sax.ImageType.Avatar_Upload, SaxItemImage.StatusCat.ActiveOnly); + disposableItems.Add(itemImageAvatarUploaded); + if (itemImageAvatarUploaded != null) + { + itemImageAvatarUploaded.Status = SaStatus.Deleted; + if (!ItemDataMgr.UpdateItemImage(itemImageAvatarUploaded, DbUpdateInfo)) + SaLogger.Warn(fn + " Failed to logically delete uploaded avatar image - " + ItemDataMgr.ErrorStateMessage); + } + + + // Populate data to response. + responseData.Contact = new ContactResponseData(); + PopulateContactResponseData(model, responseData.Contact, userAccount); } - - // Logically delete any previous 'Uploaded' Avatar image. - ItemId userItemId = userAccount.GetItemId(); - SaxItemImage itemImageAvatarUploaded = ItemDataMgr.GetItemImage(userItemId, Sax.ImageType.AvatarUpload, SaxItemImage.StatusCat.ActiveOnly); - if (itemImageAvatarUploaded != null) - { - itemImageAvatarUploaded.Status = SaStatus.Deleted; - if (!ItemDataMgr.UpdateItemImage(itemImageAvatarUploaded, DbUpdateInfo)) - SaLogger.Warn(fn + " Failed to logically delete uploaded avatar image - " + ItemDataMgr.ErrorStateMessage); - } - - - // Populate data to response. - responseData.Contact = new ContactResponseData(); - PopulateContactResponseData(model, responseData.Contact, userAccount); responseData.Success = true; return Json(responseData); } @@ -168,78 +175,84 @@ namespace Saxonite.Net.Core.Web.Online.Controllers if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData); RefreshModel(model); - // Validate number of images. - if (model.AccountContactImage.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.AccountContactImage.Count != 1) - { - SaLogger.Warn(fn + " Invalid number of image files uploaded: " + model.AccountContactImage.Count); - return Json(responseData.SetControlError("AccountContactImage", Localiser["msg.upload.image.single"])); - } - IFormFile file = model.AccountContactImage[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("AccountContactImage", Localiser["msg.upload.image.invalid"])); - - // Validate mime type. - if (!SaxItemImage.GetValidMimeTypes(Sax.DataItemType.UserAccount).Contains(imageConfig.MimeType)) - return Json(responseData.SetControlError("AccountContactImage", Localiser["msg.upload.image.invalid.mime"])); - - // Validate content type. - if (StringUtil.IsEmpty(file.ContentType) || !SaxItemImage.GetValidContentTypes(Sax.DataItemType.UserAccount).ContainsCI(file.ContentType)) - return Json(responseData.SetControlError("AccountContactImage", Localiser["msg.upload.image.invalid.content.type"])); - - // Resize the image if required. - if (!ImageUtils.ConstrainImage(imageConfig, Sax.Config.Online.Profile.User.Image.WidthMax, Sax.Config.Online.Profile.User.Image.HeightMax)) - return Json(responseData.SetControlError("AccountContactImage", Localiser["msg.upload.image.resize.failed"])); - - // Image belongs to the user's account. - ItemId userItemId = GetUserItemId(UserAccountId); - - // Store uploaded image as 'Upload' Avatar - awaiting confirmation that the contact details are to be saved. - SaxItemImage itemImage = ItemDataMgr.GetItemImage(userItemId, Sax.ImageType.AvatarUpload, SaxItemImage.StatusCat.Any); - if (itemImage == null) - itemImage = ItemDataMgr.CreateItemImage(userItemId, Sax.ImageType.AvatarUpload); - itemImage.Filename = file.FileName; - itemImage.Mime = SacsContentTypeHelper.GetMimeTypeFromContentType(file.ContentType).ToString().ToLower(); - itemImage.ContentType = file.ContentType; - itemImage.ImageData = imageConfig.ImageData; - itemImage.Width = imageConfig.Width; - itemImage.Height = imageConfig.Height; - itemImage.Status = SaxItemImage.DefaultStatus; - - if (!SysId.IsValid(itemImage.Id)) - { - if (!ItemDataMgr.InsertItemImage(itemImage, DbUpdateInfo)) + // Validate number of images. + if (model.AccountContactImage.IsEmpty()) { - SaLogger.Warn(fn + " Failed to insert uploaded image - " + ItemDataMgr.ErrorStateMessage); - return Json(responseData.SetControlError("AccountContactImage", 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.AccountContactImage.Count != 1) { - SaLogger.Warn(fn + " Failed to update uploaded image - " + ItemDataMgr.ErrorStateMessage); - return Json(responseData.SetControlError("AccountContactImage", Localiser["msg.error.item.insert.failed"])); + SaLogger.Warn(fn + " Invalid number of image files uploaded: " + model.AccountContactImage.Count); + return Json(responseData.SetControlError("AccountContactImage", Localiser["msg.upload.image.single"])); } - } + IFormFile file = model.AccountContactImage[0]; - // Complete response. - //responseData.SetNotification(messageTitle, "Image uploaded."); + // 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("AccountContactImage", Localiser["msg.upload.image.invalid"])); + + // Validate mime type. + if (!SaxItemImage.GetValidMimeTypes(Sax.DataItemType.UserAccount).Contains(imageConfig.MimeType)) + return Json(responseData.SetControlError("AccountContactImage", Localiser["msg.upload.image.invalid.mime"])); + + // Validate content type. + if (StringUtil.IsEmpty(file.ContentType) || !SaxItemImage.GetValidContentTypes(Sax.DataItemType.UserAccount).ContainsCI(file.ContentType)) + return Json(responseData.SetControlError("AccountContactImage", Localiser["msg.upload.image.invalid.content.type"])); + + // Resize the image if required. + if (!ImageUtils.ConstrainImage(imageConfig, Sax.Config.User.Image.WidthMax, Sax.Config.User.Image.HeightMax)) + return Json(responseData.SetControlError("AccountContactImage", Localiser["msg.upload.image.resize.failed"])); + + // Image belongs to the user's account. + ItemId userItemId = GetUserItemId(UserAccountId); + + // Store uploaded image as 'Upload' Avatar - awaiting confirmation that the contact details are to be saved. + SaxItemImage itemImage = ItemDataMgr.GetItemImage(userItemId, Sax.ImageType.Avatar_Upload, SaxItemImage.StatusCat.Any); + if (itemImage == null) + itemImage = ItemDataMgr.CreateItemImage(userItemId, Sax.ImageType.Avatar_Upload); + disposableItems.Add(itemImage); + itemImage.Filename = file.FileName; + itemImage.Mime = SacsContentTypeHelper.GetMimeTypeFromContentType(file.ContentType).ToString().ToLower(); + itemImage.ContentType = file.ContentType; + itemImage.ImageData = imageConfig.ImageData; + itemImage.Width = imageConfig.Width; + itemImage.Height = imageConfig.Height; + 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("AccountContactImage", 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("AccountContactImage", Localiser["msg.error.item.insert.failed"])); + } + } + + // Complete response. + //responseData.SetNotification(messageTitle, "Image uploaded."); + } responseData.Success = true; return Json(responseData); } @@ -268,118 +281,126 @@ namespace Saxonite.Net.Core.Web.Online.Controllers if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData); RefreshModel(model); - // Validate data. - model.ValidateContactDetails(responseData); - if (responseData.HasControlErrors()) - return Json(responseData); + // Record disposable items as they are created so all can be disposed when needed. + using (DisposableCollection disposableItems = new DisposableCollection()) + { - // Retrieve user details. - SaxUserAccount userAccount = AccountMgr.GetUserAccountById(UserAccountId); - if (userAccount == null) - { - SaLogger.Warn(fn + " Failed to retrieve user account for Id: " + SysId.ToString(UserAccountId)); - return Json(responseData.SetGoHome(messageTitle, Localiser["msg.something.gone.wrong"])); - } - if (!userAccount.EmailAddress.EqualsCI(SessionData.User.EmailAddress)) - { - SaLogger.Warn(fn + " Unable to update user account '" + userAccount.EmailAddress + ". Email doesn't match session: " + SessionData.User.EmailAddress); - return Json(responseData.SetGoHome(messageTitle, Localiser["msg.something.gone.wrong"])); - } + // Validate data. + model.ValidateContactDetails(responseData); + if (responseData.HasControlErrors()) + return Json(responseData); - // Populate user properties. - userAccount.Changed = false; - if (model.AccountTitle != null) - userAccount.Title = model.AccountTitle; - if (model.AccountFirstName != null) - userAccount.FirstName = model.AccountFirstName; - if (model.AccountLastName != null) - userAccount.LastName = model.AccountLastName; - if (model.AccountAbout != null) - userAccount.About = model.AccountAbout; - if (model.AccountGender != null) - userAccount.Gender = model.GetAccountGender(); - // Date of birth. - if (model.AccountDobYear != null) - userAccount.DateOfBirthYear = StringUtil.ToShort(model.AccountDobYear); - if (userAccount.DateOfBirthYear > 0) - { - if (model.AccountDobMonth != null) - userAccount.DateOfBirthMonth = (byte)StringUtil.ToShort(model.AccountDobMonth); - if (userAccount.DateOfBirthMonth > 0) + // Retrieve user details. + SaxUserAccount userAccount = AccountMgr.GetUserAccountById(UserAccountId); + disposableItems.Add(userAccount); + if (userAccount == null) { - if (model.AccountDobDay != null) - userAccount.DateOfBirthDay = (byte)StringUtil.ToShort(model.AccountDobDay); + SaLogger.Warn(fn + " Failed to retrieve user account for Id: " + SysId.ToString(UserAccountId)); + return Json(responseData.SetGoHome(messageTitle, Localiser["msg.something.gone.wrong"])); } - else userAccount.DateOfBirthDay = 0; - } - else - { - userAccount.DateOfBirthMonth = 0; - userAccount.DateOfBirthDay = 0; - } - // Regions. - if (model.AccountRegionIdCountry != null) - userAccount.RegionIdCountry = model.ExtractSysId(model.AccountRegionIdCountry); - if (model.AccountRegionIdProvince != null) - userAccount.RegionIdProvince = model.ExtractSysId(model.AccountRegionIdProvince); - - // Update User Account. - if (userAccount.Changed) SaLogger.Info(fn + " Updating user account '" + userAccount.EmailAddress + "'. Id: " + SysId.ToString(userAccount.Id), true); - if (AccountMgr.UpdateUserAccount(userAccount, DbUpdateInfo)) - { - // ToDo Refresh Session with updated organisation details. - } - else - { - SaLogger.Warn(fn + " Failed to update user account '" + userAccount.EmailAddress + "': " + AccountMgr.ErrorStateMessage); - return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"])); - } - - // Refresh profile contact details to session. - RefreshSessionProfileContact(userAccount); - - // Check for uploaded profile image. - ItemId userItemId = userAccount.GetItemId(); - SaxItemImage itemImageAvatarUploaded = ItemDataMgr.GetItemImage(userItemId, Sax.ImageType.AvatarUpload, SaxItemImage.StatusCat.ActiveOnly); - if (itemImageAvatarUploaded != null) - { - // Retrieve current Avatar image. - SaxItemImage itemImageAvatar = ItemDataMgr.GetItemImage(userItemId, Sax.ImageType.Avatar, SaxItemImage.StatusCat.Any); - if (itemImageAvatar == null) - itemImageAvatar = ItemDataMgr.CreateItemImage(userItemId, Sax.ImageType.Avatar); - // Copy properties from uploaded image. - itemImageAvatar.Filename = itemImageAvatarUploaded.Filename; - itemImageAvatar.Mime = itemImageAvatarUploaded.Mime; - itemImageAvatar.ContentType = itemImageAvatarUploaded.ContentType; - itemImageAvatar.Width = itemImageAvatarUploaded.Width; - itemImageAvatar.Height = itemImageAvatarUploaded.Height; - itemImageAvatar.ImageData = itemImageAvatarUploaded.ImageData; - itemImageAvatar.Status = SaxItemImage.DefaultStatus; - if (!SysId.IsValid(itemImageAvatar.Id)) + if (!userAccount.EmailAddress.EqualsCI(SessionData.User.EmailAddress)) { - if (!ItemDataMgr.InsertItemImage(itemImageAvatar, DbUpdateInfo)) + SaLogger.Warn(fn + " Unable to update user account '" + userAccount.EmailAddress + ". Email doesn't match session: " + SessionData.User.EmailAddress); + return Json(responseData.SetGoHome(messageTitle, Localiser["msg.something.gone.wrong"])); + } + + // Populate user properties. + userAccount.Changed = false; + if (model.AccountTitle != null) + userAccount.Title = model.AccountTitle; + if (model.AccountFirstName != null) + userAccount.FirstName = model.AccountFirstName; + if (model.AccountLastName != null) + userAccount.LastName = model.AccountLastName; + if (model.AccountAbout != null) + userAccount.About = model.AccountAbout; + if (model.AccountGender != null) + userAccount.Gender = model.GetAccountGender(); + // Date of birth. + if (model.AccountDobYear != null) + userAccount.DateOfBirthYear = StringUtil.ToShort(model.AccountDobYear); + if (userAccount.DateOfBirthYear > 0) + { + if (model.AccountDobMonth != null) + userAccount.DateOfBirthMonth = (byte)StringUtil.ToShort(model.AccountDobMonth); + if (userAccount.DateOfBirthMonth > 0) { - SaLogger.Warn(fn + " Failed to insert avatar image - " + ItemDataMgr.ErrorStateMessage); - return Json(responseData.SetControlError("AccountContactImage", Localiser["msg.error.item.insert.failed"])); + if (model.AccountDobDay != null) + userAccount.DateOfBirthDay = (byte)StringUtil.ToShort(model.AccountDobDay); } + else userAccount.DateOfBirthDay = 0; } else { - if (!ItemDataMgr.UpdateItemImage(itemImageAvatar, DbUpdateInfo)) - { - SaLogger.Warn(fn + " Failed to update avatar image - " + ItemDataMgr.ErrorStateMessage); - return Json(responseData.SetControlError("AccountContactImage", Localiser["msg.error.item.update.failed"])); - } + userAccount.DateOfBirthMonth = 0; + userAccount.DateOfBirthDay = 0; } - // Logically delete the uplaoded image. - itemImageAvatarUploaded.Status = SaStatus.Deleted; - if (!ItemDataMgr.UpdateItemImage(itemImageAvatarUploaded, DbUpdateInfo)) - SaLogger.Warn(fn + " Failed to logically delete uploaded avatar image - " + ItemDataMgr.ErrorStateMessage); - } + // Regions. + if (model.AccountRegionIdCountry != null) + userAccount.RegionIdCountry = model.ExtractSysId(model.AccountRegionIdCountry); + if (model.AccountRegionIdProvince != null) + userAccount.RegionIdProvince = model.ExtractSysId(model.AccountRegionIdProvince); - // Construct response. - responseData.Contact = new ContactResponseData(); - PopulateContactResponseData(model, responseData.Contact, userAccount); + // Update User Account. + if (userAccount.Changed) SaLogger.Info(fn + " Updating user account '" + userAccount.EmailAddress + "'. Id: " + SysId.ToString(userAccount.Id), true); + if (AccountMgr.UpdateUserAccount(userAccount, DbUpdateInfo)) + { + // ToDo Refresh Session with updated organisation details. + } + else + { + SaLogger.Warn(fn + " Failed to update user account '" + userAccount.EmailAddress + "': " + AccountMgr.ErrorStateMessage); + return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"])); + } + + // Refresh profile contact details to session. + RefreshSessionProfileContact(userAccount); + + // Check for uploaded profile image. + ItemId userItemId = userAccount.GetItemId(); + SaxItemImage itemImageAvatarUploaded = ItemDataMgr.GetItemImage(userItemId, Sax.ImageType.Avatar_Upload, SaxItemImage.StatusCat.ActiveOnly); + disposableItems.Add(itemImageAvatarUploaded); + if (itemImageAvatarUploaded != null) + { + // Retrieve current Avatar image. + SaxItemImage itemImageAvatar = ItemDataMgr.GetItemImage(userItemId, Sax.ImageType.Avatar, SaxItemImage.StatusCat.Any); + if (itemImageAvatar == null) + itemImageAvatar = ItemDataMgr.CreateItemImage(userItemId, Sax.ImageType.Avatar); + disposableItems.Add(itemImageAvatar); + // Copy properties from uploaded image. + itemImageAvatar.Filename = itemImageAvatarUploaded.Filename; + itemImageAvatar.Mime = itemImageAvatarUploaded.Mime; + itemImageAvatar.ContentType = itemImageAvatarUploaded.ContentType; + itemImageAvatar.Width = itemImageAvatarUploaded.Width; + itemImageAvatar.Height = itemImageAvatarUploaded.Height; + itemImageAvatar.ImageData = itemImageAvatarUploaded.ImageData; + itemImageAvatar.Status = SaxItemImage.DefaultStatus; + if (!SysId.IsValid(itemImageAvatar.Id)) + { + if (!ItemDataMgr.InsertItemImage(itemImageAvatar, DbUpdateInfo)) + { + SaLogger.Warn(fn + " Failed to insert avatar image - " + ItemDataMgr.ErrorStateMessage); + return Json(responseData.SetControlError("AccountContactImage", Localiser["msg.error.item.insert.failed"])); + } + } + else + { + if (!ItemDataMgr.UpdateItemImage(itemImageAvatar, DbUpdateInfo)) + { + SaLogger.Warn(fn + " Failed to update avatar image - " + ItemDataMgr.ErrorStateMessage); + return Json(responseData.SetControlError("AccountContactImage", Localiser["msg.error.item.update.failed"])); + } + } + // Logically delete the uplaoded image. + itemImageAvatarUploaded.Status = SaStatus.Deleted; + if (!ItemDataMgr.UpdateItemImage(itemImageAvatarUploaded, DbUpdateInfo)) + SaLogger.Warn(fn + " Failed to logically delete uploaded avatar image - " + ItemDataMgr.ErrorStateMessage); + } + + // Construct response. + responseData.Contact = new ContactResponseData(); + PopulateContactResponseData(model, responseData.Contact, userAccount); + } responseData.Success = true; return Json(responseData); } diff --git a/SaxoniteCoreWebOnline/Online/Controllers/AccountController-Ajax-Organisations.cs b/SaxoniteCoreWebOnline/Online/Controllers/AccountController-Ajax-Organisations.cs index 19524b0..1208ddf 100644 --- a/SaxoniteCoreWebOnline/Online/Controllers/AccountController-Ajax-Organisations.cs +++ b/SaxoniteCoreWebOnline/Online/Controllers/AccountController-Ajax-Organisations.cs @@ -24,14 +24,23 @@ namespace Saxonite.Net.Core.Web.Online.Controllers partial class AccountController { //------------------------------------------------------------------- - /// Organisation properteis for list population. + /// Organisation properties for list population. private class ProfileOrganisationListData : AjaxResponse_Data.ItemData_Base { public String Name { get; set; } public String CategoryDesc { get; set; } public String TypeDesc { get; set; } + public String LogoUrl { get; set; } public String SportName { get; set; } - public String RegionNameCountry { get; set; } + // Primary Region. + public class RegionData + { + public String Id { get; set; } + public String Name { get; set; } + public bool? Country { get; set; } = null; + } + public RegionData RegionPrimary { get; } = new RegionData(); + // Associated properties. public long MemberCount { get; set; } public long OpportunityCount { get; set; } public bool? Admin { get; set; } = null; @@ -46,7 +55,6 @@ namespace Saxonite.Net.Core.Web.Online.Controllers public String CategoryCode { get; set; } public String TypeCode { get; set; } public String SportId { get; set; } - public String RegionIdCountry { get; set; } public String About { get; set; } public String Facilities { get; set; } public List ContactMethods { get; set; } @@ -58,7 +66,10 @@ namespace Saxonite.Net.Core.Web.Online.Controllers data.Id = model.EncryptId(organisation.Id); data.Name = organisation.Name; data.SportName = model.GetSportCaption(organisation.SportIdPrimary); - data.RegionNameCountry = model.GetRegionCaption(organisation.RegionIdPrimary); + data.RegionPrimary.Name = model.GetRegionCaption(organisation.RegionIdPrimary); + // Associated properties. + data.MemberCount = AccountMgr.GetOrganisationMemberCount(organisation.Id); + data.OpportunityCount = AccountMgr.GetOrganisationOpportunityCount(organisation.Id); if (userOrganisation != null) { List roleCodes = userOrganisation.GetOrganisationRoleCodes(); @@ -73,7 +84,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers if (roleCodes.ContainsCI(OrganisationRoleCodes.GetCodeForType(Sax.OrganisationRole.ArticleAdmin))) data.ArticleAdmin = true; } - // NB Member and Opportunity counts are only populated for the Organisation list. + // Common properties. data.CreatedOnFmt = Localiser.FormatDate(organisation.CreateStamp.On); data.UpdatedOnFmt = Localiser.FormatDate(organisation.UpdateStamp.On); data.StatusCode = organisation.StatusCode; @@ -88,7 +99,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers data.TypeCode = OrganisationTypeCodes.GetCodeForType(organisation.OrganisationType); data.TypeDesc = Localiser.GetOrganisationTypeCaption(organisation.OrganisationType); data.SportId = model.GetEncryptedSportId(organisation.SportIdPrimary); - data.RegionIdCountry = model.GetEncryptedRegionId(organisation.RegionIdPrimary); + data.RegionPrimary.Id = model.GetEncryptedRegionId(organisation.RegionIdPrimary); data.About = organisation.About; data.Facilities = organisation.Facilities; // Contact methods. @@ -107,8 +118,8 @@ namespace Saxonite.Net.Core.Web.Online.Controllers data.Address.Line2 = address.Line2; data.Address.Line3 = address.Line3; data.Address.Town = address.Town; - data.Address.Province = address.Province; - data.Address.Country = address.Country; + data.Address.RegionNameProvince = address.Province; + data.Address.RegionNameCountry = address.Country; data.Address.PostalCode = address.PostalCode; } } @@ -202,13 +213,12 @@ namespace Saxonite.Net.Core.Web.Online.Controllers Name = values.GetString(colOrganisationName.DataName), CategoryDesc = Localiser.GetOrganisationCategoryCaption(organisationCategory), TypeDesc = Localiser.GetOrganisationTypeCaption(organisationType), - //SportId = model.EncryptId(new SysId(values.GetLong(colOrganisationSportId.DataName))), SportName = values.GetString(colOrganisationSportName.DataName), - //RegionId = model.EncryptId(new SysId(values.GetLong(colOrganisationRegionId.DataName))), - RegionNameCountry = values.GetString(colOrganisationRegionName.DataName), MemberCount = values.GetLong(colMemberCount.DataName), OpportunityCount = values.GetLong(colOpportunityCount.DataName) }; + // Primary region. + organisationData.RegionPrimary.Name = values.GetString(colOrganisationRegionName.DataName); // Roles. List roleCodes = StringListUtil.Split(values.GetString(colRoleCodes.DataName), SaxUserOrganisation.UserOrgnisationRoleDelim); if (roleCodes.ContainsCI(OrganisationRoleCodes.GetCodeForType(Sax.OrganisationRole.Admin))) @@ -411,11 +421,11 @@ namespace Saxonite.Net.Core.Web.Online.Controllers return Json(responseData); // Regions. - SysId regionIdCountry = model.ExtractSysId(model.Organisation.RegionIdCountry); + SysId regionIdCountry = model.ExtractSysId(model.Organisation.RegionIdPrimary); SaxRegion.Data regionCountry = Internals.GetRegion(regionIdCountry); if (SysId.IsValid(regionIdCountry) && regionCountry == null) { - SaLogger.Warn(fn + " Country region not found. Id: " + model.Organisation.RegionIdCountry + ". " + GetLogSuffix()); + SaLogger.Warn(fn + " Country region not found. Id: " + model.Organisation.RegionIdPrimary + ". " + GetLogSuffix()); return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); } SysId regionIdAddressProvince = model.ExtractSysId(model.Organisation.Address.RegionIdProvince); @@ -435,8 +445,8 @@ namespace Saxonite.Net.Core.Web.Online.Controllers organisation.OrganisationCategory = model.Organisation.GetOrganisationCategory(); if (model.Organisation.SportId != null) organisation.SportIdPrimary = model.ExtractSysId(model.Organisation.SportId); - if (model.Organisation.RegionIdCountry != null) - organisation.RegionIdPrimary = model.ExtractSysId(model.Organisation.RegionIdCountry); + if (model.Organisation.RegionIdPrimary != null) + organisation.RegionIdPrimary = model.ExtractSysId(model.Organisation.RegionIdPrimary); if (model.Organisation.About != null) organisation.About = model.Organisation.About; if (model.Organisation.Facilities != null) diff --git a/SaxoniteCoreWebOnline/Online/Controllers/AccountController-Ajax-Sports.cs b/SaxoniteCoreWebOnline/Online/Controllers/AccountController-Ajax-Sports.cs index 908198e..b415bbe 100644 --- a/SaxoniteCoreWebOnline/Online/Controllers/AccountController-Ajax-Sports.cs +++ b/SaxoniteCoreWebOnline/Online/Controllers/AccountController-Ajax-Sports.cs @@ -25,7 +25,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers { //------------------------------------------------------------------- // Data definitions. - private class ProfileSportResponseData_List : AjaxResponse_Data.ItemData_Base + private class ProfileSportResponseListData : AjaxResponse_Data.ItemData_Base { public String SportId { get; set; } public String SportName { get; set; } @@ -33,23 +33,27 @@ namespace Saxonite.Net.Core.Web.Online.Controllers public String SportParticipation { get; set; } public String SportOfficiating { get; set; } } - private class ProfileSportResponseData_Item : ProfileSportResponseData_List + private class ProfileSportResponseItemData : ProfileSportResponseListData { public String SportAbout { get; set; } public String SportParticipationCode { get; set; } public String SportOfficiatingCode { get; set; } } - private void PopulateProfileSportResponseData(Model_Account model, SaxUserSport userSport, SaxSport sport, ProfileSportResponseData_Item data) + private void PopulateProfileSportResponseListData(Model_Account model, SaxUserSport userSport, SaxSport sport, ProfileSportResponseListData data) { data.Id = model.EncryptId(userSport.Id); data.SportId = model.GetEncryptedSportId(userSport.SportId); data.SportName = sport.Name; data.SportSpecialityLabel = model.GetSportSpecialityLabel(userSport.SportId); + data.SportParticipation = Localiser.GetSportParticipationCaption(userSport.Participation); + data.SportOfficiating = Localiser.GetSportOfficiatingCaption(userSport.Officiating); + } + private void PopulateProfileSportResponseItemData(Model_Account model, SaxUserSport userSport, SaxSport sport, ProfileSportResponseItemData data) + { + PopulateProfileSportResponseListData(model, userSport, sport, data); data.SportAbout = userSport.About; data.SportParticipationCode = SportParticipationCodes.GetCodeForType(userSport.Participation); - data.SportParticipation = Localiser.GetSportParticipationCaption(userSport.Participation); data.SportOfficiatingCode = SportOfficiatingCodes.GetCodeForType(userSport.Officiating); - data.SportOfficiating = Localiser.GetSportOfficiatingCaption(userSport.Officiating); data.CreatedOnFmt = DateUtil.Format(userSport.CreateStamp.On, "d-MMM-yyyy"); data.StatusCode = userSport.StatusCode; data.StatusDesc = userSport.StatusDesc; @@ -63,7 +67,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers // Construction. public AjaxResponse_ProfileSportsData(SaxModel_Online model) : base(model) { } // Sport list. - public List Sports { get; } = new List(); + public List Sports { get; } = new List(); } // Ajax request. [HttpPost] @@ -111,7 +115,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers { NamedValue.Map values = resultSet.GetRecordValues(); SysId sportId = new SysId(values.GetLong(colSportId.DataName)); - ProfileSportResponseData_List userSportData = new ProfileSportResponseData_List() + ProfileSportResponseListData userSportData = new ProfileSportResponseListData() { Id = model.EncryptId(new SysId(values.GetLong(colId.DataName))), SportId = model.GetEncryptedSportId(sportId), @@ -142,7 +146,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers // Construction. public AjaxResponse_ProfileSport(SaxModel_Online model) : base(model) { } // Sport details. - public ProfileSportResponseData_Item Sport { get; } = new ProfileSportResponseData_Item(); + public ProfileSportResponseItemData Sport { get; } = new ProfileSportResponseItemData(); } //------------------------------------------------------------------- [HttpPost] @@ -186,7 +190,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers } // Construct response. - PopulateProfileSportResponseData(model, userSport, sport, responseData.Sport); + PopulateProfileSportResponseItemData(model, userSport, sport, responseData.Sport); responseData.Success = true; } return Json(responseData); @@ -318,7 +322,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers RefreshSessionProfileSports(); // Construct response. - PopulateProfileSportResponseData(model, userSport, sport, responseData.Sport); + PopulateProfileSportResponseItemData(model, userSport, sport, responseData.Sport); responseData.Success = true; } return Json(responseData); diff --git a/SaxoniteCoreWebOnline/Online/Controllers/AccountController-Ajax-UserSportExperience.cs b/SaxoniteCoreWebOnline/Online/Controllers/AccountController-Ajax-UserSportExperience.cs index 2aff3da..7a3bafe 100644 --- a/SaxoniteCoreWebOnline/Online/Controllers/AccountController-Ajax-UserSportExperience.cs +++ b/SaxoniteCoreWebOnline/Online/Controllers/AccountController-Ajax-UserSportExperience.cs @@ -29,7 +29,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers { //------------------------------------------------------------------- // Common response data. - private class SportExperienceResponseData_List : AjaxResponse_Data.ItemData_Base + private class SportExperienceResponseListData : AjaxResponse_Data.ItemData_Base { public String UserSportId { get; set; } public String Period { get; set; } @@ -45,7 +45,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers public String RegionCaptionProvince { get; set; } public string RegionRepresentation { get; set; } } - private class SportExperienceResponseData_Detail : SportExperienceResponseData_List + private class SportExperienceResponseItemData : SportExperienceResponseListData { public String PeriodFrom_YMD { get; set; } public String PeriodTo_YMD { get; set; } @@ -53,29 +53,33 @@ namespace Saxonite.Net.Core.Web.Online.Controllers public String RegionIdNation { get; set; } public String RegionIdProvince { get; set; } } - private void PopulateSportExperienceResponseData(Model_UserSportExperience model, SportExperienceResponseData_Detail data, SaxUserSport userSport, SaxUserSportExperience userSportExperience) + private void PopulateSportExperienceResponseListData(Model_UserSportExperience model, SportExperienceResponseListData data, SaxUserSport userSport, SaxUserSportExperience userSportExperience) { data.Id = model.EncryptId(userSportExperience.Id); data.UserSportId = model.EncryptId(userSportExperience.UserSportId); - data.PeriodFrom_YMD = DateUtil.FormatDateForJavascript(userSportExperience.PeriodFrom); data.PeriodFrom = model.FormatDateForForm(userSportExperience.PeriodFrom); - data.PeriodTo_YMD = DateUtil.FormatDateForJavascript(userSportExperience.PeriodTo); data.PeriodTo = model.FormatDateForForm(userSportExperience.PeriodTo); - data.Period = data.PeriodFrom + " - " + data.PeriodTo; - data.OrganisationId = model.EncryptId(userSportExperience.OrganisationId); data.OrganisationName = userSportExperience.OrganisationName; data.Summary = userSportExperience.Summary; data.Team = userSportExperience.Team; data.League = userSportExperience.League; data.Speciality = userSportExperience.Speciality; // Regions. - data.RegionIdNation = model.GetEncryptedRegionId(userSportExperience.RegionIdNation); - data.RegionIdProvince = model.GetEncryptedRegionId(userSportExperience.RegionIdProvince); data.RegionCaptionNation = Localiser.GetRegionName(Internals.GetRegion(userSportExperience.RegionIdNation)); data.RegionCaptionProvince = Localiser.GetRegionName(Internals.GetRegion(userSportExperience.RegionIdProvince)); List locations = new List() { data.RegionCaptionProvince, data.RegionCaptionNation }; data.RegionRepresentation = locations.Concat(", ", "", "", true, true); } + private void PopulateSportExperienceResponseItemData(Model_UserSportExperience model, SportExperienceResponseItemData data, SaxUserSport userSport, SaxUserSportExperience userSportExperience) + { + data.PeriodFrom_YMD = DateUtil.FormatDateForJavascript(userSportExperience.PeriodFrom); + data.PeriodTo_YMD = DateUtil.FormatDateForJavascript(userSportExperience.PeriodTo); + data.Period = data.PeriodFrom + " - " + data.PeriodTo; + data.OrganisationId = model.EncryptId(userSportExperience.OrganisationId); + // Regions. + data.RegionIdNation = model.GetEncryptedRegionId(userSportExperience.RegionIdNation); + data.RegionIdProvince = model.GetEncryptedRegionId(userSportExperience.RegionIdProvince); + } //------------------------------------------------------------------- // Utilites. @@ -88,7 +92,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers // Construction. public AjaxResponse_UserSportExperienceList(SaxModel_Online model) : base(model) { } // Contact details. - public List Experiences { get; } = new List(); + public List Experiences { get; } = new List(); // Sport details. public class SportData { @@ -163,7 +167,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers while (resultSet != null && resultSet.HasRows && resultSet.NextRecord()) { NamedValue.Map values = resultSet.GetRecordValues(); - SportExperienceResponseData_List itemData = new SportExperienceResponseData_List() + SportExperienceResponseListData itemData = new SportExperienceResponseListData() { Id = model.EncryptId(new SysId(values.GetLong(colId.DataName))), UserSportId = model.EncryptId(new SysId(values.GetLong(colUserSportId.DataName))), @@ -194,7 +198,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers // Construction. public AjaxResponse_UserSportExperienceData(SaxModel_Online model) : base(model) { } // Contact details. - public SportExperienceResponseData_Detail Experience { get; set; } + public SportExperienceResponseItemData Experience { get; set; } } // Ajax request. [HttpPost] @@ -223,8 +227,8 @@ namespace Saxonite.Net.Core.Web.Online.Controllers return Json(responseData.SetGoHome(messageTitle, Localiser["msg.something.gone.wrong"])); // Populate data to response. - responseData.Experience = new SportExperienceResponseData_Detail(); - PopulateSportExperienceResponseData(model, responseData.Experience, userSport, userSportExperience); + responseData.Experience = new SportExperienceResponseItemData(); + PopulateSportExperienceResponseItemData(model, responseData.Experience, userSport, userSportExperience); } // Complete response. @@ -240,7 +244,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers // Construction. public AjaxResponse_StoreUserSportExperience(SaxModel_Online model) : base(model) { } // Contact details. - public SportExperienceResponseData_Detail Experience { get; set; } + public SportExperienceResponseItemData Experience { get; set; } } // Ajax request. [HttpPost] @@ -346,8 +350,8 @@ namespace Saxonite.Net.Core.Web.Online.Controllers } // Populate data to response. - responseData.Experience = new SportExperienceResponseData_Detail(); - PopulateSportExperienceResponseData(model, responseData.Experience, userSport, userSportExperience); + responseData.Experience = new SportExperienceResponseItemData(); + PopulateSportExperienceResponseItemData(model, responseData.Experience, userSport, userSportExperience); } // Complete response. diff --git a/SaxoniteCoreWebOnline/Online/Controllers/ActivityController-Ajax.cs b/SaxoniteCoreWebOnline/Online/Controllers/ActivityController-Ajax.cs index beb87a3..85f7095 100644 --- a/SaxoniteCoreWebOnline/Online/Controllers/ActivityController-Ajax.cs +++ b/SaxoniteCoreWebOnline/Online/Controllers/ActivityController-Ajax.cs @@ -40,6 +40,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers public String ItemTypeClass { get; set; } public String ItemId { get; set; } public String Organisation { get; set; } + public String OrganisationLogoUrl { get; set; } public String Author { get; set; } public String Caption { get; set; } public String Blurb { get; set; } @@ -54,6 +55,8 @@ namespace Saxonite.Net.Core.Web.Online.Controllers public ActivityResponseData_Article(SaxController_Online controller, SaxModel_Online model, SaxArticle article, SaxOrganisation organisation) { Organisation = organisation?.Name ?? null; + if (controller.ItemDataMgr.ItemHasActiveImage(organisation?.GetItemId() ?? null, Sax.ImageType.OrganisationLogo)) + OrganisationLogoUrl = controller.GetOrganisationLogoUrl(organisation.Id, false); Author = model.GetUserAccountDisplayName(article.UserAccountIdAuthor); Headline = article.Headline; Summary = article.Summary; @@ -65,6 +68,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers Content.Segments.Add(new ContentData.SegmentData(controller, segment)); } public String Organisation { get; set; } + public String OrganisationLogoUrl { get; set; } public String Author { get; set; } public String Headline { get; set; } public String Summary { get; set; } @@ -106,9 +110,11 @@ namespace Saxonite.Net.Core.Web.Online.Controllers /// Event activity info. private class ActivityResponseData_Event { - public ActivityResponseData_Event(SaxModel_Online model, SaxOrganisationEvent organisationEvent) + public ActivityResponseData_Event(SaxController_Online controller, SaxModel_Online model, SaxOrganisationEvent organisationEvent, SaxOrganisation organisation) { Organisation = model.GetOrganisationName(organisationEvent.OrganisationId); + if (controller.ItemDataMgr.ItemHasActiveImage(organisation?.GetItemId() ?? null, Sax.ImageType.OrganisationLogo)) + OrganisationLogoUrl = controller.GetOrganisationLogoUrl(organisation.Id, false); EventAt = DateUtil.Format(organisationEvent.EventAt, "d-MMM-yy"); if (organisationEvent.EventAt.Hour > 0 || organisationEvent.EventAt.Minute > 0) EventAt += DateUtil.Format(organisationEvent.EventAt, " HH:ss"); @@ -119,6 +125,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers PublishedOnFmt = model.Localiser.FormatDate(organisationEvent.PublishedAt); } public String Organisation { get; set; } + public String OrganisationLogoUrl { get; set; } public String EventAt { get; set; } public String Summary { get; set; } public String Details { get; set; } @@ -129,9 +136,11 @@ namespace Saxonite.Net.Core.Web.Online.Controllers /// Opportunity activity info. private class ActivityResponseData_Opportunity { - public ActivityResponseData_Opportunity(SaxModel_Online model, SaxOrganisationOpportunity opportunity) + public ActivityResponseData_Opportunity(SaxController_Online controller, SaxModel_Online model, SaxOrganisationOpportunity opportunity, SaxOrganisation organisation) { Organisation = model.GetOrganisationName(opportunity.OrganisationId); + if (controller.ItemDataMgr.ItemHasActiveImage(organisation?.GetItemId() ?? null, Sax.ImageType.OrganisationLogo)) + OrganisationLogoUrl = controller.GetOrganisationLogoUrl(organisation.Id, false); Position = opportunity.Position; Outline = opportunity.Outline; Sport = model.GetSportCaption(opportunity.SportIdPrimary); @@ -139,6 +148,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers PublishedOnFmt = model.Localiser.FormatDate(opportunity.PublishedAt); } public String Organisation { get; set; } + public String OrganisationLogoUrl { get; set; } public String Position { get; set; } public String Outline { get; set; } public String Sport { get; set; } @@ -230,6 +240,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers // ToDo Mask activity index Id with cross-reference stored in session data. activityData.ItemId = model.EncryptId(itemData.ItemId.Id); activityData.Organisation = itemData.Organisation; + // ToDo Organisation Logo URL. activityData.Author = itemData.Author; activityData.Caption = itemData.Caption; activityData.Blurb = itemData.Blurb; @@ -350,7 +361,9 @@ namespace Saxonite.Net.Core.Web.Online.Controllers SaLogger.Warn(fn + " Event not found for Activity Id: " + itemId.ToString() + "." + GetLogSuffix()); return Json(responseData); } - responseData.Event = new ActivityResponseData_Event(model, organisationEvent); + SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationEvent.OrganisationId); + disposableItems.Add(organisation); + responseData.Event = new ActivityResponseData_Event(this, model, organisationEvent, organisation); } else if (itemType == Sax.DataItemType.OrganisationOpportunity) { @@ -362,7 +375,9 @@ namespace Saxonite.Net.Core.Web.Online.Controllers SaLogger.Warn(fn + " Opportunity not found for Activity Id: " + itemId.ToString() + "." + GetLogSuffix()); return Json(responseData); } - responseData.Opportunity = new ActivityResponseData_Opportunity(model, opportunity); + SaxOrganisation organisation = AccountMgr.GetOrganisationById(opportunity.OrganisationId); + disposableItems.Add(organisation); + responseData.Opportunity = new ActivityResponseData_Opportunity(this, model, opportunity, organisation); } // Record activty viewing. diff --git a/SaxoniteCoreWebOnline/Online/Controllers/ArticleController-Ajax.cs b/SaxoniteCoreWebOnline/Online/Controllers/ArticleController-Ajax.cs index 84b1e57..366e932 100644 --- a/SaxoniteCoreWebOnline/Online/Controllers/ArticleController-Ajax.cs +++ b/SaxoniteCoreWebOnline/Online/Controllers/ArticleController-Ajax.cs @@ -50,7 +50,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers //------------------------------------------------------------------- // Article data. - private class ArticleData_List : AjaxResponse_Data.ItemData_Base + private class ArticleListData : AjaxResponse_Data.ItemData_Base { public String OrganisationId { get; set; } public String OrganisationName { get; set; } @@ -68,7 +68,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers public String SportCaptionPrimary { get; set; } public String PublishedOnFmt { get; set; } } - private class ArticleData_Full : ArticleData_List + private class ArticleItemData : ArticleListData { public String RegionIdPrimary { get; set; } public String SportIdPrimary { get; set; } @@ -78,7 +78,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers public String PublishedOn_Calendar { get; set; } public ArticleContentData Content { get; } = new ArticleContentData(); } - private void PopulateArticleResponseListData(Model_Article model, SaxArticle article, SaxOrganisation organisation, SaxActivityFeedback activityFeedback, ArticleData_List data) + private void PopulateArticleResponseListData(Model_Article model, SaxArticle article, SaxOrganisation organisation, SaxActivityFeedback activityFeedback, ArticleListData data) { data.Id = model.EncryptId(article.Id); data.OrganisationId = SysId.ToString(article.OrganisationId); @@ -92,9 +92,9 @@ namespace Saxonite.Net.Core.Web.Online.Controllers data.StatusCode = article.StatusCode; data.StatusDesc = article.StatusDesc; } - private void PopulateArticleResponseData(Model_Article model, SaxArticle article, SaxOrganisation organisation, SaxActivityFeedback activityFeedback, ArticleData_Full data) + private void PopulateArticleResponseItemData(Model_Article model, SaxArticle article, SaxOrganisation organisation, SaxActivityFeedback activityFeedback, ArticleItemData data) { - PopulateArticleResponseListData(model, article, organisation, activityFeedback, data as ArticleData_List); + PopulateArticleResponseListData(model, article, organisation, activityFeedback, data as ArticleListData); data.RegionIdPrimary = model.GetEncryptedRegionId(article.RegionIdPrimary); data.SportIdPrimary = model.GetEncryptedSportId(article.SportIdPrimary); data.Published = DateUtil.IsValid(article.PublishedAt) ? true : (bool?)null; @@ -122,19 +122,19 @@ namespace Saxonite.Net.Core.Web.Online.Controllers } } // Feedback. - private void PopulateFeedbackResponseData(SaxActivityFeedback activityFeedback, ArticleData_Full data) + private void PopulateFeedbackResponseData(SaxActivityFeedback activityFeedback, ArticleItemData data) { PopulateFeedbackResponseListData(activityFeedback, data); } - private void PopulateFeedbackResponseListData(SaxActivityFeedback activityFeedback, ArticleData_List data) + private void PopulateFeedbackResponseListData(SaxActivityFeedback activityFeedback, ArticleListData data) { if (activityFeedback == null) return; PopulateFeedbackResponseListData(activityFeedback.ViewCount, activityFeedback.FeedbackCount, activityFeedback.FeedbackTotal, activityFeedback.FeedbackAverage, data); } - private void PopulateFeedbackResponseListData(long viewCount, long feedbackCount, long feedbackTotal, decimal feedbackAverage, ArticleData_List data) + private void PopulateFeedbackResponseListData(long viewCount, long feedbackCount, long feedbackTotal, decimal feedbackAverage, ArticleListData data) { if (viewCount <= 0) return; - data.Feedback = new ArticleData_List.FeedbackData(); + data.Feedback = new ArticleListData.FeedbackData(); data.Feedback.Summary = feedbackCount.ToString() + "/" + viewCount.ToString(); if (feedbackCount > 0) data.Feedback.Summary = feedbackAverage.ToString("N1") + " " + data.Feedback.Summary; @@ -152,7 +152,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers // Construction. public AjaxResponse_ArticleList(SaxModel_Online model) : base(model) { } // Article list. - public List Articles { get; } = new List(); + 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; } @@ -283,7 +283,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers while (resultSet != null && resultSet.HasRows && resultSet.NextRecord()) { NamedValue.Map values = resultSet.GetRecordValues(); - ArticleData_List articleData = new ArticleData_List() + ArticleListData articleData = new ArticleListData() { Id = model.EncryptId(new SysId(values.GetLong(colId.DataName))), OrganisationId = model.EncryptId(new SysId(values.GetLong(colOrganisationId.DataName))), @@ -322,7 +322,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers // Construction. public AjaxResponse_Article(SaxModel_Online model) : base(model) { } // Article details. - public ArticleData_Full Article { get; } = new ArticleData_Full(); + public ArticleItemData Article { get; } = new ArticleItemData(); } //------------------------------------------------------------------- [HttpPost] @@ -371,7 +371,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers // 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.ImageTypesIncluded.Add(Sax.ImageType.Article_Upload); prmItemImage.StatusCat = SaxItemImage.StatusCat.NotDeleted; foreach (SaxItemImage itemImage in ItemDataMgr.GetItemImageList(prmItemImage)) { @@ -387,7 +387,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers disposableItems.Add(activityFeedback); // Construct response. - PopulateArticleResponseData(model, article, organisation, activityFeedback, responseData.Article); + PopulateArticleResponseItemData(model, article, organisation, activityFeedback, responseData.Article); } responseData.Success = true; return Json(responseData); @@ -458,7 +458,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers return Json(responseData.SetControlError("ArticleImage", Localiser["msg.upload.image.invalid.content.type"])); // Resize the image if required. - if (!ImageUtils.ConstrainImage(imageConfig, Sax.Config.Online.Articles.Images.WidthMax, Sax.Config.Online.Articles.Images.HeightMax)) + if (!ImageUtils.ConstrainImage(imageConfig, Sax.Config.Articles.Images.WidthMax, Sax.Config.Articles.Images.HeightMax)) return Json(responseData.SetControlError("ArticleImage", Localiser["msg.upload.image.resize.failed"])); // Image belongs to the article. @@ -473,16 +473,16 @@ namespace Saxonite.Net.Core.Web.Online.Controllers SaxItemImage.Params prmItemImage = new SaxItemImage.Params(); prmItemImage.ItemIdsIncluded.Add(imageItemId); prmItemImage.ImageTypesIncluded.Add(Sax.ImageType.Article); - prmItemImage.ImageTypesIncluded.Add(Sax.ImageType.ArticleUpload); + prmItemImage.ImageTypesIncluded.Add(Sax.ImageType.Article_Upload); 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 = ItemDataMgr.CreateItemImage(imageItemId, Sax.ImageType.Article_Upload); disposableItems.Add(itemImage); - itemImage.ImageType = Sax.ImageType.ArticleUpload; + itemImage.ImageType = Sax.ImageType.Article_Upload; itemImage.Filename = file.FileName; itemImage.Mime = SacsContentTypeHelper.GetMimeTypeFromContentType(file.ContentType).ToString().ToLower(); itemImage.ContentType = file.ContentType; @@ -658,7 +658,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers else prmItemImage.ItemIdsIncluded.Add(new ItemId((short)Sax.DataItemType.UserAccount, sessionData.User.UserAccountId)); prmItemImage.ImageTypesIncluded.Add(Sax.ImageType.Article); - prmItemImage.ImageTypesIncluded.Add(Sax.ImageType.ArticleUpload); + prmItemImage.ImageTypesIncluded.Add(Sax.ImageType.Article_Upload); prmItemImage.StatusCat = SaxItemImage.StatusCat.NotDeleted; List itemImages = ItemDataMgr.GetItemImageList(prmItemImage); disposableItems.AddRange(itemImages); @@ -750,7 +750,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers disposableItems.Add(activityFeedback); // Construct response. - PopulateArticleResponseData(model, article, organisation, activityFeedback, responseData.Article); + PopulateArticleResponseItemData(model, article, organisation, activityFeedback, responseData.Article); responseData.Success = true; } return Json(responseData); diff --git a/SaxoniteCoreWebOnline/Online/Controllers/ImageController.cs b/SaxoniteCoreWebOnline/Online/Controllers/ImageController.cs index eb861f7..3c5ce1a 100644 --- a/SaxoniteCoreWebOnline/Online/Controllers/ImageController.cs +++ b/SaxoniteCoreWebOnline/Online/Controllers/ImageController.cs @@ -30,6 +30,10 @@ namespace Saxonite.Net.Core.Web.Online.Controllers { //------------------------------------------------------------------- // Data members. + public static String ParamName_OrganisationLogoEncrypted { get; } = "ole"; + public static String ParamName_ArticleImageEncrypted { get; } = "aie"; + public static String ParamName_OrganisationId { get; } = "ori"; + public static String ParamName_ItemImageId { get; } = "iii"; //------------------------------------------------------------------- // Construction. @@ -78,7 +82,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers Response.Headers[HeaderNames.CacheControl] = "no-cache, no-store, must-revalidate, max-age=0"; Response.Headers[HeaderNames.Expires] = "-1"; SysId userAccountId = UserAccountId; - return GetAccountContactImage(userAccountId, Sax.ImageType.AvatarUpload); + return GetAccountContactImage(userAccountId, Sax.ImageType.Avatar_Upload); } //------------------------------------------------------------------- @@ -100,7 +104,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers SaxItemImage.Params prmItemImage = new SaxItemImage.Params(); prmItemImage.ItemIdsIncluded.Add(userItemId); prmItemImage.ImageTypesIncluded.Add(Sax.ImageType.Avatar); - prmItemImage.ImageTypesIncluded.Add(Sax.ImageType.AvatarUpload); + prmItemImage.ImageTypesIncluded.Add(Sax.ImageType.Avatar_Upload); prmItemImage.StatusCat = SaxItemImage.StatusCat.ActiveOnly; SaxItemImage itemImage = null; foreach (SaxItemImage itemImageCandidate in ItemDataMgr.GetItemImageList(prmItemImage)) @@ -117,11 +121,76 @@ namespace Saxonite.Net.Core.Web.Online.Controllers return File(itemImage.ImageData, itemImage.ContentType); } + //------------------------------------------------------------------- + // Organisation logo. + //------------------------------------------------------------------- + /// + /// Get the logo for the specified Organisation. + /// Method parameter name (ole) must match name of encrypted parameter. + /// Encrypted parameters: + /// - ori: Organisation Id. + /// + [HttpGet] + [Route("Image/Organisation/Logo")] + public IActionResult GetOrganisationLogo(String ole) + { + return GetOrganisationLogo(ole, false); + } + /// + /// Get the logo for the specified Organisation. + /// Method parameter name (ole) must match name of encrypted parameter. + /// Encrypted parameters: + /// - ori: Organisation Id. + /// + [HttpGet] + [Route("Image/Organisation/Logo/Upload")] + public IActionResult GetOrganisationLogoUpload(String ole) + { + return GetOrganisationLogo(ole, true); + } + private IActionResult GetOrganisationLogo(String encryptedParams, bool allowUploadImage) + { + String fn = GetType().Name + ".GetOrganisationLogo"; + + // Validate session and referrer. + if (!ValidReferer || !ValidUserSession) return Forbid(); + + // Extract encrypted parameters. + NameValueCollection parameters = ExtractOnlineParams(encryptedParams); + if (parameters.Count < 1) return Forbid(); + + // Organisation Id. + SysId organisationId = parameters.Get(ParamName_OrganisationId).ToSysId(); + if (!SysId.IsValid(organisationId)) return Forbid(); + ItemId organisationItemId = new ItemId((short)Sax.DataItemType.Organisation, organisationId); + + // Query Organisation Logo images. + SaxItemImage.Params prmItemImage = new SaxItemImage.Params(); + prmItemImage.ItemIdsIncluded.Add(organisationItemId); + prmItemImage.ImageTypesIncluded.Add(Sax.ImageType.OrganisationLogo); + if (allowUploadImage) + prmItemImage.ImageTypesIncluded.Add(Sax.ImageType.OrganisationLogo_Upload); + prmItemImage.StatusCat = SaxItemImage.StatusCat.ActiveOnly; + SaxItemImage itemImage = null; + foreach (SaxItemImage itemImageCandidate in ItemDataMgr.GetItemImageList(prmItemImage)) + { + // The 'Upload' image will take precedence if allowed. + if (itemImage == null || itemImageCandidate.ImageType == Sax.ImageType.OrganisationLogo_Upload) + itemImage = itemImageCandidate; + } + + if (itemImage == null) + return NotFound(); + + // Construct response. + Response.Headers[HeaderNames.CacheControl] = "no-cache, no-store, must-revalidate, max-age=0"; + Response.Headers[HeaderNames.Expires] = "-1"; + return File(itemImage.ImageData, itemImage.ContentType); + } + //------------------------------------------------------------------- // Article image //------------------------------------------------------------------- - public static String ParamName_ArticleImageEncrypted { get; } = "aie"; - public static String ParamName_ItemImageId { get; } = "iii"; /// /// Get the specified Article Image. /// Method parameter name (aie) must match name of encrypted parameter. diff --git a/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController-Ajax-Events.cs b/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController-Ajax-Events.cs index b4b6d0c..26635d3 100644 --- a/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController-Ajax-Events.cs +++ b/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController-Ajax-Events.cs @@ -21,7 +21,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers { //------------------------------------------------------------------- // Organisation Event data. - private class EventData_List : AjaxResponse_Data.ItemData_Base + private class EventListData : AjaxResponse_Data.ItemData_Base { public String EventDateFmt { get; set; } public String EventTimeFmt { get; set; } @@ -34,7 +34,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers public String PublishedOnFmt { get; set; } public String ExpiresOnFmt { get; set; } } - private class EventData_Detail : EventData_List + private class EventItemData : EventListData { public String EventOn_Calendar { get; set; } public String EventOn_YYYYMMDD { get; set; } @@ -44,7 +44,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers public String PublishedOn_YYYYMMDD { get; set; } public String ExpiresOn_YYYYMMDD { get; set; } } - private void PopulateEventResponseData(Model_Organisation_Events model, SaxOrganisationEvent organisationEvent, EventData_List data) + private void PopulateEventResponseListData(Model_Organisation_Events model, SaxOrganisationEvent organisationEvent, EventListData data) { data.Id = model.EncryptId(organisationEvent.Id); data.EventDateFmt = Localiser.FormatDate(organisationEvent.EventAt); @@ -61,9 +61,9 @@ namespace Saxonite.Net.Core.Web.Online.Controllers data.StatusCode = organisationEvent.StatusCode; data.StatusDesc = organisationEvent.StatusDesc; } - private void PopulateEventResponseData(Model_Organisation_Events model, SaxOrganisationEvent organisationEvent, EventData_Detail data) + private void PopulateEventResponseItemData(Model_Organisation_Events model, SaxOrganisationEvent organisationEvent, EventItemData data) { - PopulateEventResponseData(model, organisationEvent, data as EventData_List); + PopulateEventResponseListData(model, organisationEvent, data as EventListData); data.EventOn_Calendar = model.FormatDateForForm(organisationEvent.EventAt); data.EventOn_YYYYMMDD = DateUtil.Format(organisationEvent.EventAt, "yyyyMMdd"); data.EventAt_HHMM = DateUtil.Format(organisationEvent.EventAt, "HH:mm"); @@ -81,7 +81,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers // Construction. public AjaxResponse_EventList(SaxModel_Online model) : base(model) { } // Event list. - public List Events { get; } = new List(); + public List Events { get; } = new List(); // User profile. public class UserData { @@ -207,7 +207,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers while (resultSet != null && resultSet.HasRows && resultSet.NextRecord()) { NamedValue.Map values = resultSet.GetRecordValues(); - EventData_List eventData = new EventData_List() + EventListData eventData = new EventListData() { Id = model.EncryptId(new SysId(values.GetLong(colId.DataName))), EventDateFmt = Localiser.FormatDate(values.GetDateTime(colEventAt.DataName)), @@ -246,7 +246,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers // Construction. public AjaxResponse_Event(SaxModel_Online model) : base(model) { } // Event details. - public EventData_Detail Event { get; } = new EventData_Detail(); + public EventItemData Event { get; } = new EventItemData(); } //------------------------------------------------------------------- [HttpPost] @@ -293,7 +293,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers SessionData_Online.Filter.SetMemberOrganisation(organisation); // Construct response. - PopulateEventResponseData(model, organisationEvent, responseData.Event); + PopulateEventResponseItemData(model, organisationEvent, responseData.Event); } // Complete response. @@ -428,7 +428,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers ActivityConsole.StoreActivityIndex(organisationEvent, DbUpdateInfo); // Construct response. - PopulateEventResponseData(model, organisationEvent, responseData.Event); + PopulateEventResponseItemData(model, organisationEvent, responseData.Event); } // Complete response. diff --git a/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController-Ajax-Members.cs b/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController-Ajax-Members.cs index e932226..3362403 100644 --- a/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController-Ajax-Members.cs +++ b/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController-Ajax-Members.cs @@ -23,7 +23,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers { //------------------------------------------------------------------- // Organisation Member data. - private class MemberData_List : AjaxResponse_Data.ItemData_Base + private class MemberListData : AjaxResponse_Data.ItemData_Base { public String UserAccountId { get; set; } public bool? CurrentUser { get; set; } = null; @@ -34,13 +34,13 @@ namespace Saxonite.Net.Core.Web.Online.Controllers public List Roles { get; set; } = new List(); public String AdminComment { get; set; } } - private class MemberData_Full : MemberData_List + private class MemberItemData : MemberListData { public String Title { get; set; } public String FirstName { get; set; } public String LastName { get; set; } } - private void PopulateMemberResponseData(Model_Organisation_Members model, SaxUserOrganisation userOrganisation, SaxUserAccount userAccount, SaxOrganisation organisation, MemberData_List data) + private void PopulateMemberResponseListData(Model_Organisation_Members model, SaxUserOrganisation userOrganisation, SaxUserAccount userAccount, SaxOrganisation organisation, MemberListData data) { data.Id = model.EncryptId(userOrganisation.Id); data.UserAccountId = model.EncryptId(userOrganisation.UserAccountId); @@ -64,9 +64,9 @@ namespace Saxonite.Net.Core.Web.Online.Controllers data.StatusDesc = userAccount.StatusDesc; data.CreatedOnFmt = Localiser.FormatDate(userOrganisation.CreateStamp.On); } - private void PopulateMemberResponseData(Model_Organisation_Members model, SaxUserOrganisation userOrganisation, SaxUserAccount userAccount, SaxOrganisation organisation, MemberData_Full data) + private void PopulateMemberResponseItemData(Model_Organisation_Members model, SaxUserOrganisation userOrganisation, SaxUserAccount userAccount, SaxOrganisation organisation, MemberItemData data) { - PopulateMemberResponseData(model, userOrganisation, userAccount, organisation, data as MemberData_List); + PopulateMemberResponseListData(model, userOrganisation, userAccount, organisation, data as MemberListData); data.Title = userAccount.Title; data.FirstName = userAccount.FirstName; data.LastName = userAccount.LastName; @@ -80,7 +80,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers // Construction. public AjaxResponse_MemberList(SaxModel_Online model) : base(model) { } // Member list. - public List Members { get; } = new List(); + public List Members { get; } = new List(); // User profile. public class UserData { @@ -171,7 +171,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers while (resultSet != null && resultSet.HasRows && resultSet.NextRecord()) { NamedValue.Map values = resultSet.GetRecordValues(); - MemberData_List memberData = new MemberData_List() + MemberListData memberData = new MemberListData() { Id = model.EncryptId(new SysId(values.GetLong(colId.DataName))), UserAccountId = model.EncryptId(new SysId(values.GetLong(colUserAccountId.DataName))), @@ -215,7 +215,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers // Construction. public AjaxResponse_Member(SaxModel_Online model) : base(model) { } // Member details. - public MemberData_Full Member { get; } = new MemberData_Full(); + public MemberItemData Member { get; } = new MemberItemData(); } //------------------------------------------------------------------- [HttpPost] @@ -274,7 +274,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers // Make a note of the selected Organisation. SessionData_Online.Filter.SetMemberOrganisation(organisation); - PopulateMemberResponseData(model, userOrganisation, userAccount, organisation, responseData.Member); + PopulateMemberResponseItemData(model, userOrganisation, userAccount, organisation, responseData.Member); } // Construct response. @@ -290,7 +290,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers // Construction. public AjaxResponse_StoreMember(SaxModel_Online model) : base(model) { } // Member details. - public MemberData_Full Member { get; } = new MemberData_Full(); + public MemberItemData Member { get; } = new MemberItemData(); // Feedback. public bool? MemberNameRequired { get; set; } = null; } @@ -507,7 +507,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers } // Construct response. - PopulateMemberResponseData(model, userOrganisation, userAccount, organisation, responseData.Member); + PopulateMemberResponseItemData(model, userOrganisation, userAccount, organisation, responseData.Member); } // Complete response. @@ -523,7 +523,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers // Construction. public AjaxResponse_ActivateMember(SaxModel_Online model) : base(model) { } // Member details. - public MemberData_Full Member { get; } = new MemberData_Full(); + public MemberItemData Member { get; } = new MemberItemData(); } [HttpPost] [Route("Organisation/Ajax/ActivateMember")] @@ -607,7 +607,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers } // Construct response. - PopulateMemberResponseData(model, userOrganisation, userAccount, organisation, responseData.Member); + PopulateMemberResponseItemData(model, userOrganisation, userAccount, organisation, responseData.Member); } // Complete response. @@ -639,7 +639,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers // Construction. public AjaxResponse_RemoveMember(SaxModel_Online model) : base(model) { } // Member details. - public MemberData_Full Member { get; } = new MemberData_Full(); + public MemberItemData Member { get; } = new MemberItemData(); } [HttpPost] [Route("Organisation/Ajax/RemoveMember")] @@ -713,7 +713,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers } // Construct response. - PopulateMemberResponseData(model, userOrganisation, userAccount, organisation, responseData.Member); + PopulateMemberResponseItemData(model, userOrganisation, userAccount, organisation, responseData.Member); } // Complete response. diff --git a/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController-Ajax-Opportunities.cs b/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController-Ajax-Opportunities.cs index 497c5eb..c2db68f 100644 --- a/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController-Ajax-Opportunities.cs +++ b/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController-Ajax-Opportunities.cs @@ -21,7 +21,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers { //------------------------------------------------------------------- // Organisation Opportunity data. - private class OpportunityData_List : AjaxResponse_Data.ItemData_Base + private class OpportunityListData : AjaxResponse_Data.ItemData_Base { public String TypeCode { get; set; } public String TypeDesc { get; set; } @@ -32,14 +32,14 @@ namespace Saxonite.Net.Core.Web.Online.Controllers public String PublishedOnFmt { get; set; } public String ExpiresOnFmt { get; set; } } - private class OpportunityData_Detail : OpportunityData_List + private class OpportunityItemData : OpportunityListData { public String RegionIdPrimary { get; set; } public String SportIdPrimary { get; set; } public String PublishedOn_YYYYMMDD { get; set; } public String ExpiresOn_YYYYMMDD { get; set; } } - private void PopulateOpportunityResponseData(Model_Organisation_Opportunities model, SaxOrganisationOpportunity opportunity, OpportunityData_List data) + private void PopulateOpportunityResponseListData(Model_Organisation_Opportunities model, SaxOrganisationOpportunity opportunity, OpportunityListData data) { data.Id = model.EncryptId(opportunity.Id); data.TypeCode = OpportunityTypeCodes.GetCodeForType(opportunity.OpportunityType); @@ -54,9 +54,9 @@ namespace Saxonite.Net.Core.Web.Online.Controllers data.StatusCode = opportunity.StatusCode; data.StatusDesc = opportunity.StatusDesc; } - private void PopulateOpportunityResponseData(Model_Organisation_Opportunities model, SaxOrganisationOpportunity opportunity, OpportunityData_Detail data) + private void PopulateOpportunityResponseItemData(Model_Organisation_Opportunities model, SaxOrganisationOpportunity opportunity, OpportunityItemData data) { - PopulateOpportunityResponseData(model, opportunity, data as OpportunityData_List); + PopulateOpportunityResponseListData(model, opportunity, data as OpportunityListData); data.RegionIdPrimary = model.GetEncryptedRegionId(opportunity.RegionIdPrimary); data.SportIdPrimary = model.GetEncryptedSportId(opportunity.SportIdPrimary); data.PublishedOn_YYYYMMDD = DateUtil.Format(opportunity.PublishedAt, "yyyyMMdd"); @@ -71,7 +71,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers // Construction. public AjaxResponse_OpportunityList(SaxModel_Online model) : base(model) { } // Opportunity list. - public List Opportunities { get; } = new List(); + public List Opportunities { get; } = new List(); // User profile. public class UserData { @@ -196,7 +196,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers while (resultSet != null && resultSet.HasRows && resultSet.NextRecord()) { NamedValue.Map values = resultSet.GetRecordValues(); - OpportunityData_List opportunityData = new OpportunityData_List() + OpportunityListData opportunityData = new OpportunityListData() { Id = model.EncryptId(new SysId(values.GetLong(colId.DataName))), TypeCode = values.GetString(colOpportunityTypeCode.DataName), @@ -233,7 +233,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers // Construction. public AjaxResponse_Opportunity(SaxModel_Online model) : base(model) { } // Opportunity details. - public OpportunityData_Detail Opportunity { get; } = new OpportunityData_Detail(); + public OpportunityItemData Opportunity { get; } = new OpportunityItemData(); } //------------------------------------------------------------------- [HttpPost] @@ -280,7 +280,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers SessionData_Online.Filter.SetMemberOrganisation(organisation); // Construct response. - PopulateOpportunityResponseData(model, opportunity, responseData.Opportunity); + PopulateOpportunityResponseItemData(model, opportunity, responseData.Opportunity); } // Complete response. @@ -409,7 +409,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers ActivityConsole.StoreActivityIndex(opportunity, DbUpdateInfo); // Construct response. - PopulateOpportunityResponseData(model, opportunity, responseData.Opportunity); + PopulateOpportunityResponseItemData(model, opportunity, responseData.Opportunity); } // Complete response. diff --git a/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController-Ajax-Request.cs b/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController-Ajax-Request.cs index 1fdff8c..0364c94 100644 --- a/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController-Ajax-Request.cs +++ b/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController-Ajax-Request.cs @@ -28,7 +28,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers // Construction. public AjaxResponse_RequestOrganisation(SaxModel_Online model) : base(model) { } // Organisation list. - public List Organisations { get; } = new List(); + public List Organisations { get; } = new List(); } //------------------------------------------------------------------- [HttpPost] @@ -52,11 +52,11 @@ namespace Saxonite.Net.Core.Web.Online.Controllers return Json(responseData); // Check Regions. - SysId regionIdCountry = model.ExtractSysId(model.Organisation.RegionIdCountry); + SysId regionIdCountry = model.ExtractSysId(model.Organisation.RegionIdPrimary); SaxRegion.Data regionCountry = Internals.GetRegion(regionIdCountry); if (SysId.IsValid(regionIdCountry) && regionCountry == null) { - SaLogger.Warn(fn + " Country region not found. Id: " + model.Organisation.RegionIdCountry + ". " + GetLogSuffix()); + SaLogger.Warn(fn + " Country region not found. Id: " + model.Organisation.RegionIdPrimary + ". " + GetLogSuffix()); return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); } SysId regionIdAddressProvince = model.ExtractSysId(model.Organisation.Address.RegionIdProvince); @@ -150,7 +150,6 @@ namespace Saxonite.Net.Core.Web.Online.Controllers organisation.OrganisationCategory = model.Organisation.GetOrganisationCategory(); organisation.SportIdPrimary = organisationSportId; organisation.RegionIdPrimary = regionIdCountry; - organisation.PhoneMain = model.Organisation.PhoneMain; organisation.Status = SaStatus.Awaiting; if (!AccountMgr.InsertOrganisation(organisation, DbUpdateInfo)) { diff --git a/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController-Ajax.cs b/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController-Ajax.cs index e8a7018..b460bb8 100644 --- a/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController-Ajax.cs +++ b/SaxoniteCoreWebOnline/Online/Controllers/OrganisationController-Ajax.cs @@ -22,13 +22,14 @@ namespace Saxonite.Net.Core.Web.Online.Controllers { //------------------------------------------------------------------- // Organisation data. - private class OrganisationResponseData : AjaxResponse_Data.ItemData_Base + private class OrganisationListData : AjaxResponse_Data.ItemData_Base { public String Name { get; set; } public String RegionId { get; set; } public String RegionName { get; set; } public String SportId { get; set; } public String SportName { get; set; } + public String LogoUrl { get; set; } } //------------------------------------------------------------------- @@ -39,7 +40,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers // Construction. public AjaxResponse_OrganisationList(SaxModel_Online model) : base(model) { } // Organisation list. - public List Organisations { get; } = new List(); + public List Organisations { get; } = new List(); } //------------------------------------------------------------------- [HttpPost] @@ -62,11 +63,13 @@ namespace Saxonite.Net.Core.Web.Online.Controllers ColumnDfn colRegionName = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.Region_Name); ColumnDfn colSportId = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.SportId); ColumnDfn colSportName = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.Sport_Name); + ColumnDfn colLogoCount = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.LogoImage_Count); List columns = new List() { colOrganisationId, colOrganisationName, colRegionId, colRegionName, - colSportId, colSportName + colSportId, colSportName, + colLogoCount }; // Organisation filter. @@ -83,15 +86,21 @@ namespace Saxonite.Net.Core.Web.Online.Controllers while (resultSet != null && resultSet.HasRows && resultSet.NextRecord()) { NamedValue.Map values = resultSet.GetRecordValues(); - OrganisationResponseData organisationData = new OrganisationResponseData() + SysId organisationId = new SysId(values.GetLong(colOrganisationId.DataName)); + OrganisationListData organisationData = new OrganisationListData() { - Id = model.EncryptId(new SysId(values.GetLong(colOrganisationId.DataName))), + Id = model.EncryptId(organisationId), Name = values.GetString(colOrganisationName.DataName), RegionId = model.EncryptId(new SysId(values.GetLong(colRegionId.DataName))), RegionName = values.GetString(colRegionName.DataName), SportId = model.EncryptId(new SysId(values.GetLong(colSportId.DataName))), SportName = values.GetString(colSportName.DataName), }; + // Logo. + if (values.GetLong(colLogoCount.DataName) > 0) + organisationData.LogoUrl = GetOrganisationLogoUrl(organisationId, false); + + // Add item to the list. responseData.Organisations.Add(organisationData); } } @@ -112,7 +121,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers // Construction. public AjaxResponse_MemberAdminOrganisationList(SaxModel_Online model) : base(model) { } // Organisation list. - public List Organisations { get; } = new List(); + public List Organisations { get; } = new List(); } //------------------------------------------------------------------- [HttpPost] @@ -158,7 +167,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers while (resultSet != null && resultSet.HasRows && resultSet.NextRecord()) { NamedValue.Map values = resultSet.GetRecordValues(); - OrganisationResponseData organisationData = new OrganisationResponseData() + OrganisationListData organisationData = new OrganisationListData() { Id = model.EncryptId(new SysId(values.GetLong(colOrganisationId.DataName))), Name = values.GetString(colOrganisationName.DataName), @@ -187,7 +196,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers // Construction. public AjaxResponse_EventAdminOrganisationList(SaxModel_Online model) : base(model) { } // Organisation list. - public List Organisations { get; } = new List(); + public List Organisations { get; } = new List(); } //------------------------------------------------------------------- [HttpPost] @@ -233,7 +242,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers while (resultSet != null && resultSet.HasRows && resultSet.NextRecord()) { NamedValue.Map values = resultSet.GetRecordValues(); - OrganisationResponseData organisationData = new OrganisationResponseData() + OrganisationListData organisationData = new OrganisationListData() { Id = model.EncryptId(new SysId(values.GetLong(colOrganisationId.DataName))), Name = values.GetString(colOrganisationName.DataName), @@ -262,7 +271,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers // Construction. public AjaxResponse_OpportunityAdminOrganisationList(SaxModel_Online model) : base(model) { } // Organisation list. - public List Organisations { get; } = new List(); + public List Organisations { get; } = new List(); } //------------------------------------------------------------------- [HttpPost] @@ -308,7 +317,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers while (resultSet != null && resultSet.HasRows && resultSet.NextRecord()) { NamedValue.Map values = resultSet.GetRecordValues(); - OrganisationResponseData organisationData = new OrganisationResponseData() + OrganisationListData organisationData = new OrganisationListData() { Id = model.EncryptId(new SysId(values.GetLong(colOrganisationId.DataName))), Name = values.GetString(colOrganisationName.DataName), diff --git a/SaxoniteCoreWebOnline/Online/Controllers/SaxController_Online.cs b/SaxoniteCoreWebOnline/Online/Controllers/SaxController_Online.cs index ef30a14..da1e9f8 100644 --- a/SaxoniteCoreWebOnline/Online/Controllers/SaxController_Online.cs +++ b/SaxoniteCoreWebOnline/Online/Controllers/SaxController_Online.cs @@ -146,7 +146,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers sessionData.Profile.AddSport(sportId, status); } } - SaLogger.Debug(fn+" Profile Sports: " + sessionData.Profile.SportCount); + SaLogger.Debug(fn + " Profile Sports: " + sessionData.Profile.SportCount); return true; } @@ -169,6 +169,21 @@ namespace Saxonite.Net.Core.Web.Online.Controllers ClearSessionData(); } + //------------------------------------------------------------------- + // Organisation utilities. + //------------------------------------------------------------------- + /// + /// Construct URL to display Organisation logo. + /// - Option to allow for 'Upload' image if active. + /// + public String GetOrganisationLogoUrl(SysId organisationId, bool uploadImage) + { + String imageLink = "Image/Organisation/Logo" + (uploadImage ? "/Upload" : "") + "?"; + NameValueCollection parameters = new NameValueCollection(); + parameters.Add(ImageController.ParamName_OrganisationId, SysId.ToString(organisationId)); + return imageLink + ImageController.ParamName_OrganisationLogoEncrypted + "=" + EncryptOnlineParams(parameters); + } + //------------------------------------------------------------------- // Keep Logged In. //------------------------------------------------------------------- @@ -240,8 +255,9 @@ namespace Saxonite.Net.Core.Web.Online.Controllers //------------------------------------------------------------------- // Parameter encryption, eg. for images. //------------------------------------------------------------------- - // Encryption key is the same for all users but a different IV value is generated for each session so that the links/parameters cannot be shared. - public static String Base64Key_OnlineApp { get; } = SacsEncryptionHelper.EncodePlainKey("", SacsEncryptionHelper.KeyLength); + // Encryption key is the same for all users each time the application is started, + // but a different IV value is generated for each session so that the links/parameters cannot be shared. + public static String Base64Key_OnlineApp { get; } = SacsEncryptionHelper.EncodePlainKey("Saxonite#Online@Encrypt+" + DateTime.Today.Format("ddMMyyyy"), SacsEncryptionHelper.KeyLength); protected String EncryptOnlineParams(NameValueCollection parameters) { return HttpUtility.UrlEncode(EncryptParameters(parameters, SessionData_Online.Base64IV_OnlineApp, Base64Key_OnlineApp)); diff --git a/SaxoniteCoreWebOnline/Online/Controllers/SearchController-Ajax-Organisations.cs b/SaxoniteCoreWebOnline/Online/Controllers/SearchController-Ajax-Organisations.cs index 832cfbe..45577d8 100644 --- a/SaxoniteCoreWebOnline/Online/Controllers/SearchController-Ajax-Organisations.cs +++ b/SaxoniteCoreWebOnline/Online/Controllers/SearchController-Ajax-Organisations.cs @@ -33,6 +33,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers public String CategoryDesc { get; set; } public String SportName { get; set; } public String RegionName { get; set; } + public String LogoUrl { get; set; } } private class AjaxResponse_Search_Organisations : AjaxResponse_Data { @@ -86,11 +87,13 @@ namespace Saxonite.Net.Core.Web.Online.Controllers ColumnDfn colSportName = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.Sport_Name); ColumnDfn colRegionId = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.RegionId); ColumnDfn colRegionName = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.Region_Name); + ColumnDfn colLogoCount = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.LogoImage_Count); List columns = new List() { colId, colName, colCategory, colSportId, colSportName, colRegionId, colRegionName, + colLogoCount }; // Sort columns. @@ -103,15 +106,21 @@ namespace Saxonite.Net.Core.Web.Online.Controllers while (resultSet != null && resultSet.HasRows && resultSet.NextRecord()) { NamedValue.Map values = resultSet.GetRecordValues(); + SysId organisationId = new SysId(values.GetLong(colId.DataName)); Sax.OrganisationCategory category = (Sax.OrganisationCategory)OrganisationCategoryCodes.GetTypeForCode(values.GetString(colCategory.DataName)); OrganisationListData organisationData = new OrganisationListData() { - Id = model.EncryptId(new SysId(values.GetLong(colId.DataName))), + Id = model.EncryptId(organisationId), Name = values.GetString(colName.DataName), CategoryDesc = Localiser.GetOrganisationCategoryCaption(category), SportName = values.GetString(colSportName.DataName), RegionName = values.GetString(colRegionName.DataName), }; + // Logo. + if (values.GetLong(colLogoCount.DataName) > 0) + organisationData.LogoUrl = GetOrganisationLogoUrl(organisationId, false); + + // Add item to the list. responseData.Organisations.Add(organisationData); } } diff --git a/SaxoniteCoreWebOnline/Online/Helpers/SaxContactMethodHelper_Online.cs b/SaxoniteCoreWebOnline/Online/Helpers/SaxContactMethodHelper_Online.cs index 97e4d04..bd534c4 100644 --- a/SaxoniteCoreWebOnline/Online/Helpers/SaxContactMethodHelper_Online.cs +++ b/SaxoniteCoreWebOnline/Online/Helpers/SaxContactMethodHelper_Online.cs @@ -34,18 +34,18 @@ namespace Saxonite.Net.Core.Web.Online.Helpers case Sax.ContactMethod.Email: return "" + ConstructImageElement("action/mail24.png") + value + " "; case Sax.ContactMethod.Website: - return "" + ConstructImageElement("icon/contact/www-24.png") + value + " "; + return "" + ConstructImageElement("icon/contact/www-24.png") + value + " "; case Sax.ContactMethod.Wikipedia: - return "" + ConstructImageElement("icon/contact/wikipedia-24.png") + value + " "; + return "" + ConstructImageElement("icon/contact/wikipedia-24.png") + value + " "; // Social media. case Sax.ContactMethod.LinkedIn: - return "" + ConstructImageElement("icon/contact/linkedin-24.png") + value + " "; + return "" + ConstructImageElement("icon/contact/linkedin-24.png") + value + " "; case Sax.ContactMethod.Twitter: - return "" + ConstructImageElement("icon/contact/twitter-24.png") + value + " "; + return "" + ConstructImageElement("icon/contact/twitter-24.png") + value + " "; case Sax.ContactMethod.Facebook: - return "" + ConstructImageElement("icon/contact/facebook-24.png") + value + " "; + return "" + ConstructImageElement("icon/contact/facebook-24.png") + value + " "; case Sax.ContactMethod.Instagram: - return "" + ConstructImageElement("icon/contact/instagram-24.png") + value + " "; + return "" + ConstructImageElement("icon/contact/instagram-24.png") + value + " "; } return method.ToString() + ": " + value; } diff --git a/SaxoniteCoreWebOnline/Online/Models/ModelData_Organisation.cs b/SaxoniteCoreWebOnline/Online/Models/ModelData_Organisation.cs index a47fd72..d9a7017 100644 --- a/SaxoniteCoreWebOnline/Online/Models/ModelData_Organisation.cs +++ b/SaxoniteCoreWebOnline/Online/Models/ModelData_Organisation.cs @@ -37,10 +37,9 @@ namespace Saxonite.Net.Core.Web.Online.Models public String Category { get; set; } public Sax.OrganisationCategory GetOrganisationCategory() { return (Sax.OrganisationCategory)Model.OrganisationCategoryCodes.GetTypeForCode(Category); } public String SportId { get; set; } - public String RegionIdCountry { get; set; } + public String RegionIdPrimary { get; set; } public String About { get; set; } public String Facilities { get; set; } - public String PhoneMain { get; set; } // Contact Methods. /// Placeholder field value to indicate the number of Contact Methods in form (even if none are populated). public String ContactMethods { get; set; } @@ -60,53 +59,53 @@ namespace Saxonite.Net.Core.Web.Online.Models String fn = this.GetType().Name + ".ValidateOrganisationData"; // Name - mandatory. - responseData.SetControlValue(ControlNameSuffix + "Name", Name); + responseData.SetControlValue(ControlNameSuffix + nameof(Name), Name); if (StringUtil.IsEmpty(Name)) - responseData.SetControlError(ControlNameSuffix + "Name", Model.Localiser["msg.validation.mandatory.value"]); + responseData.SetControlError(ControlNameSuffix + nameof(Name), Model.Localiser["msg.validation.mandatory.value"]); else if (StringUtil.GetLength(Name) > Model.GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.Organisation_Name)) - responseData.SetControlError(ControlNameSuffix + "Name", Model.Localiser["msg.validation.maximum.length", Model.GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.Organisation_Name)]); - else if (SysId.IsValid(Model.ExtractSysId(RegionIdCountry))) + responseData.SetControlError(ControlNameSuffix + nameof(Name), Model.Localiser["msg.validation.maximum.length", Model.GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.Organisation_Name)]); + else if (SysId.IsValid(Model.ExtractSysId(RegionIdPrimary))) { - // Ensure that the organisation name is unique within the country. + // Ensure that the organisation name is unique within the region. SaxOrganisation.Params prmOrganisation = new SaxOrganisation.Params(); if (SysId.IsValid(Model.ExtractSysId(Id))) prmOrganisation.IdsExcluded.Add(Model.ExtractSysId(Id)); prmOrganisation.NameFull = Name; - prmOrganisation.RegionIdsIncluded.Add(Model.ExtractSysId(RegionIdCountry)); + prmOrganisation.RegionIdsIncluded.Add(Model.ExtractSysId(RegionIdPrimary)); prmOrganisation.StatusCat = SaxOrganisation.StatusCat.Any; if (Model.AccountMgr.GetOrganisationCount(prmOrganisation) > 0) - responseData.SetControlError(ControlNameSuffix + "Name", Model.Localiser["msg.validation.duplicate.value"]); + responseData.SetControlError(ControlNameSuffix + nameof(Name), Model.Localiser["msg.validation.duplicate.value"]); } // Organisation Category - mandatory. if (StringUtil.IsEmpty(Category)) - responseData.SetControlError(ControlNameSuffix + "Category", Model.Localiser["msg.validation.mandatory.value"]); + responseData.SetControlError(ControlNameSuffix + nameof(Category), Model.Localiser["msg.validation.mandatory.value"]); // Organisation Type - mandatory. if (StringUtil.IsEmpty(Type)) - responseData.SetControlError(ControlNameSuffix + "Type", Model.Localiser["msg.validation.mandatory.value"]); + responseData.SetControlError(ControlNameSuffix + nameof(Type), Model.Localiser["msg.validation.mandatory.value"]); // Sport - mandatory for certain organisation categories. if (StringUtil.IsEmpty(SportId) && SaxOrganisation.IsSportMandatory(GetOrganisationCategory())) - responseData.SetControlError(ControlNameSuffix + "SportId", Model.Localiser["msg.validation.mandatory.value"]); + responseData.SetControlError(ControlNameSuffix + nameof(SportId), Model.Localiser["msg.validation.mandatory.value"]); else if (!StringUtil.IsEmpty(SportId) && !SysId.IsValid(Model.ExtractSysId(SportId))) - responseData.SetControlError(ControlNameSuffix + "SportId", Model.Localiser["msg.validation.invalid.value"]); + responseData.SetControlError(ControlNameSuffix + nameof(SportId), Model.Localiser["msg.validation.invalid.value"]); - // Region Country - mandatory. - if (StringUtil.IsEmpty(RegionIdCountry)) - responseData.SetControlError(ControlNameSuffix + "RegionIdCountry", Model.Localiser["msg.validation.mandatory.value"]); - else if (!SysId.IsValid(Model.ExtractSysId(RegionIdCountry))) - responseData.SetControlError(ControlNameSuffix + "RegionIdCountry", Model.Localiser["msg.validation.invalid.value"]); + // Primary Region - mandatory. + if (StringUtil.IsEmpty(RegionIdPrimary)) + responseData.SetControlError(ControlNameSuffix + nameof(RegionIdPrimary), Model.Localiser["msg.validation.mandatory.value"]); + else if (!SysId.IsValid(Model.ExtractSysId(RegionIdPrimary))) + responseData.SetControlError(ControlNameSuffix + nameof(RegionIdPrimary), Model.Localiser["msg.validation.invalid.value"]); // About - optional. - responseData.SetControlValue(ControlNameSuffix + "About", About); + responseData.SetControlValue(ControlNameSuffix + nameof(About), About); if (StringUtil.GetLength(About) > Model.GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.Organisation_About)) - responseData.SetControlError(ControlNameSuffix + "About", Model.Localiser["msg.validation.maximum.length", Model.GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.Organisation_About)]); + responseData.SetControlError(ControlNameSuffix + nameof(About), Model.Localiser["msg.validation.maximum.length", Model.GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.Organisation_About)]); // Facilities - optional. - responseData.SetControlValue(ControlNameSuffix + "Facilities", Facilities); + responseData.SetControlValue(ControlNameSuffix + nameof(Facilities), Facilities); if (StringUtil.GetLength(Facilities) > Model.GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.Organisation_Facilities)) - responseData.SetControlError(ControlNameSuffix + "Facilities", Model.Localiser["msg.validation.maximum.length", Model.GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.Organisation_Facilities)]); + responseData.SetControlError(ControlNameSuffix + nameof(Facilities), Model.Localiser["msg.validation.maximum.length", Model.GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.Organisation_Facilities)]); // Contact Methods. if (ContactMethods != null) @@ -131,19 +130,6 @@ namespace Saxonite.Net.Core.Web.Online.Models } } - // Phone Main - optional. - if (PhoneMain != null) - { - responseData.SetControlValue(ControlNameSuffix + "PhoneMain", PhoneMain); - if (!StringUtil.IsEmpty(PhoneMain)) - { - if (!SacsDataValidationHelper.IsValidPhone(PhoneMain)) - responseData.SetControlError(ControlNameSuffix + "PhoneMain", Model.Localiser["msg.validation.invalid.value"]); - else if (StringUtil.GetLength(PhoneMain) > Model.GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.Organisation_PhoneMain)) - responseData.SetControlError(ControlNameSuffix + "PhoneMain", Model.Localiser["msg.validation.maximum.length", Model.GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.Organisation_PhoneMain)]); - } - } - // Address. Address.ValidateAddressData(responseData); diff --git a/SaxoniteCoreWebOnline/Online/Models/Model_Article.cs b/SaxoniteCoreWebOnline/Online/Models/Model_Article.cs index a73dc52..55b5940 100644 --- a/SaxoniteCoreWebOnline/Online/Models/Model_Article.cs +++ b/SaxoniteCoreWebOnline/Online/Models/Model_Article.cs @@ -134,7 +134,7 @@ namespace Saxonite.Net.Core.Web.Online.Models responseData.SetControlError(controlName, Localiser["msg.validation.mandatory.value"]); else if (!DateUtil.IsValid(publishedOn)) responseData.SetControlError(controlName, Localiser["msg.validation.invalid.value"]); - else if (publishedOn.CompareTo(DateUtil.IncrementDate(DateTime.Today, Sax.Config.Online.Articles.PublishMaxDaysAhead)) > 0) + else if (publishedOn.CompareTo(DateUtil.IncrementDate(DateTime.Today, Sax.Config.Articles.PublishMaxDaysAhead)) > 0) responseData.SetControlError(controlName, Localiser["msg.validation.invalid.value"]); } diff --git a/SaxoniteCoreWebOnline/Views/Account/Profile-Organisations.cshtml b/SaxoniteCoreWebOnline/Views/Account/Profile-Organisations.cshtml index a462155..083d723 100644 --- a/SaxoniteCoreWebOnline/Views/Account/Profile-Organisations.cshtml +++ b/SaxoniteCoreWebOnline/Views/Account/Profile-Organisations.cshtml @@ -55,7 +55,7 @@
-
+
@Localiser["label.members"]: 
diff --git a/SaxoniteCoreWebOnline/Views/Article/Index.cshtml b/SaxoniteCoreWebOnline/Views/Article/Index.cshtml index 5c404d4..6e85347 100644 --- a/SaxoniteCoreWebOnline/Views/Article/Index.cshtml +++ b/SaxoniteCoreWebOnline/Views/Article/Index.cshtml @@ -7,7 +7,7 @@
diff --git a/SaxoniteCoreWebOnline/Views/Common/ContactMethods.cshtml b/SaxoniteCoreWebOnline/Views/Common/ContactMethods.cshtml index 08f5659..85b9e5c 100644 --- a/SaxoniteCoreWebOnline/Views/Common/ContactMethods.cshtml +++ b/SaxoniteCoreWebOnline/Views/Common/ContactMethods.cshtml @@ -38,7 +38,7 @@
- +
diff --git a/SaxoniteCoreWebOnline/Views/Common/OrganisationControls-Contact.cshtml b/SaxoniteCoreWebOnline/Views/Common/OrganisationControls-Contact.cshtml index 15e3835..257347b 100644 --- a/SaxoniteCoreWebOnline/Views/Common/OrganisationControls-Contact.cshtml +++ b/SaxoniteCoreWebOnline/Views/Common/OrganisationControls-Contact.cshtml @@ -49,7 +49,7 @@
@* List of existing contact methods with actions to edit/remove. *@
-
+
diff --git a/SaxoniteCoreWebOnline/Views/Common/OrganisationControls-Core.cshtml b/SaxoniteCoreWebOnline/Views/Common/OrganisationControls-Core.cshtml index eebd952..a1307c1 100644 --- a/SaxoniteCoreWebOnline/Views/Common/OrganisationControls-Core.cshtml +++ b/SaxoniteCoreWebOnline/Views/Common/OrganisationControls-Core.cshtml @@ -63,7 +63,7 @@
- +
@@ -73,18 +73,18 @@
@* Organisation primary region *@ -
+
- +
- -
-
+ +
+
diff --git a/SaxoniteCoreWebOnline/Views/Home/Index.cshtml b/SaxoniteCoreWebOnline/Views/Home/Index.cshtml index 7598cf4..67294d9 100644 --- a/SaxoniteCoreWebOnline/Views/Home/Index.cshtml +++ b/SaxoniteCoreWebOnline/Views/Home/Index.cshtml @@ -106,8 +106,13 @@ else
-
-
+
+
+
+
+
+
+
diff --git a/SaxoniteCoreWebOnline/Views/Search/Organisations.cshtml b/SaxoniteCoreWebOnline/Views/Search/Organisations.cshtml index 3d23379..8005a63 100644 --- a/SaxoniteCoreWebOnline/Views/Search/Organisations.cshtml +++ b/SaxoniteCoreWebOnline/Views/Search/Organisations.cshtml @@ -117,29 +117,36 @@ @* Item template *@
-
+
- @* Organisation caption *@ -
-
-
-
-
-
- @* Burger menu *@ - -
-
-
-
-
-
+
+
+ @* Organisation caption *@ +
+
+
+
+
+
+ @* Burger menu *@ + +
+
+
+
+
+
+
+
+
+
+
diff --git a/SaxoniteCoreWebOnline/wwwroot/css/sax-data.css b/SaxoniteCoreWebOnline/wwwroot/css/sax-data.css index 713eb98..1619bf9 100644 --- a/SaxoniteCoreWebOnline/wwwroot/css/sax-data.css +++ b/SaxoniteCoreWebOnline/wwwroot/css/sax-data.css @@ -77,7 +77,7 @@ .sax-results-paging { - margin-right: 8px; + margin-right: 12px; } .sax-results-paging .sax-paging-info @@ -236,7 +236,8 @@ overflow: hidden; } -.sax-panel-item > .sax-header +.sax-panel-item > .sax-header, +.sax-panel-item > .row > .col > .sax-header { padding: 2px 8px; min-height: 24px; @@ -244,14 +245,16 @@ border-bottom: solid 1px #979797; } -.sax-panel-item > .sax-header .sax-caption +.sax-panel-item > .sax-header .sax-caption, +.sax-panel-item > .row > .col > .sax-header .sax-caption { margin: 4px 0; font-size: 1.2rem; font-weight: bold; } -.sax-panel-item > .sax-content +.sax-panel-item > .sax-content, +.sax-panel-item > .row > .col > .sax-content { margin: 4px 8px 2px 8px; padding: 2px 2px; @@ -271,18 +274,21 @@ border-bottom-right-radius: 8px; } -.sax-alpha .sax-panel-item > .sax-header +.sax-alpha .sax-panel-item > .sax-header, +.sax-alpha .sax-panel-item > .row > .col > .sax-header { margin: 0 8px; padding: 4px 0; background-color: #fff; } -.sax-alpha .sax-panel-item > .sax-header .sax-caption +.sax-alpha .sax-panel-item > .sax-header .sax-caption, +.sax-alpha .sax-panel-item > .row > .col > .sax-header .sax-caption { } -.sax-alpha .sax-panel-item > .sax-content +.sax-alpha .sax-panel-item > .sax-content, +.sax-alpha .sax-panel-item > .row > .col > .sax-content { background-color: #fff; } @@ -325,17 +331,25 @@ /* Organisations */ .sax-panel-item.Organisation, -.sax-panel-item.Organisation > .sax-header +.sax-panel-item.Organisation .sax-header { border-color: var(--color-alt3); } .sax-alpha .sax-panel-item.Organisation, -.sax-alpha .sax-panel-item.Organisation > .sax-header +.sax-alpha .sax-panel-item.Organisation .sax-header { border-color: #b0b0b0; } +.sax-panel-item-list .sax-panel-item.Organisation img.OrganisationLogo +{ + max-width: 48px; + max-height: 48px; + margin:4px 4px 2px 0; + border-radius: 2px; +} + /* Members */ .sax-panel-item.Member, diff --git a/SaxoniteCoreWebOnline/wwwroot/css/sax-home.css b/SaxoniteCoreWebOnline/wwwroot/css/sax-home.css index ab7ee36..f3abeae 100644 --- a/SaxoniteCoreWebOnline/wwwroot/css/sax-home.css +++ b/SaxoniteCoreWebOnline/wwwroot/css/sax-home.css @@ -12,12 +12,12 @@ { min-width: 84px; padding: 0 0 0 4px; - vertical-align:top; + vertical-align: top; } .sax-panel-item.Activity .ActivityReaction img { - vertical-align:top; + vertical-align: top; } .sax-panel-item.Activity.sax-activity-reaction-1 .sax-header @@ -67,7 +67,6 @@ border-bottom: 1px solid #b0b0b0; } -#dlgActivity .sax-panel-item.Article .ArticleSummary, #dlgActivity .sax-panel-item.Event .EventAt, #dlgActivity .sax-panel-item.Event .EventSummary, #dlgActivity .sax-panel-item.Opportunity .OpportunityOrganisation, @@ -80,14 +79,29 @@ border-bottom: 1px solid #b0b0b0; } +#dlgActivity .sax-panel-item.Article .ArticleSummary +{ + margin: 0; + padding: 8px 8px; + font-size: 1.25rem; + font-weight: bold; +} + #dlgActivity .sax-panel-item.Article .ArticleOrigin { - padding: 4px 8px; + padding: 6px 8px 2px 8px; font-weight: bold; background-color: #f8f8f8; + border-top: 1px solid #b0b0b0; border-bottom: 1px solid #b0b0b0; } +#dlgActivity .sax-panel-item.Article img.ArticleOrganisationLogo +{ + max-width:64px; + max-height:64px; +} + #dlgActivity .sax-panel-item.Event .EventDetails, #dlgActivity .sax-panel-item.Opportunity .OpportunityOutline { diff --git a/SaxoniteCoreWebOnline/wwwroot/js/sax-home.js b/SaxoniteCoreWebOnline/wwwroot/js/sax-home.js index 1cdbbfc..e3e6d64 100644 --- a/SaxoniteCoreWebOnline/wwwroot/js/sax-home.js +++ b/SaxoniteCoreWebOnline/wwwroot/js/sax-home.js @@ -90,7 +90,7 @@ } sax.showActivityDetails = function (response) { - //sax.debug('showActivityDetails', response); + sax.debug('showActivityDetails', response); let $act = $('.sax-content', sax.$dlg.activity).empty(); let $ar = $('.ActivityReaction', sax.$dlg.activity.parent()).hide(); let item; @@ -103,6 +103,7 @@ $('.ArticleSummary', $itm).text(item.summary).toggle(!!item.summary); $('.ArticleAuthor', $itm).text(item.author); $('.ArticleOrganisation', $itm).text(item.organisation); + $('img.ArticleOrganisationLogo', $itm).prop('src', item.organisationLogoUrl); $('.ArticleOrigin', $itm).toggle(!!(item.author || item.organisation)); let segs = item.content.segments; for (let segIdx = 0; segIdx < segs.length; segIdx++) diff --git a/SaxoniteCoreWebOnline/wwwroot/js/sax-src-org.js b/SaxoniteCoreWebOnline/wwwroot/js/sax-src-org.js index f085c61..64ae3e6 100644 --- a/SaxoniteCoreWebOnline/wwwroot/js/sax-src-org.js +++ b/SaxoniteCoreWebOnline/wwwroot/js/sax-src-org.js @@ -47,6 +47,7 @@ { if (!item) return; $('.sax-panel-messages', $pnl.results).hide().empty(); + $('img.OrganisationLogo', $item).prop('src', item.logoUrl).toggle(!!item.logoUrl); $('.OrganisationName', $item).text(item.name); $('.OrganisationCategory', $item).text(item.categoryDesc); $('.OrganisationSport', $item).text(item.sportName); diff --git a/SaxoniteCoreWebOnline/wwwroot/js/sax-usr-org.js b/SaxoniteCoreWebOnline/wwwroot/js/sax-usr-org.js index 1692f5d..c528959 100644 --- a/SaxoniteCoreWebOnline/wwwroot/js/sax-usr-org.js +++ b/SaxoniteCoreWebOnline/wwwroot/js/sax-usr-org.js @@ -44,7 +44,7 @@ if (!item) return; $('.OrganisationName', $item).text(item.name); $('.OrganisationSport', $item).text(item.sportName); - $('.OrganisationRegionCountry', $item).text(item.regionNameCountry); + $('.OrganisationRegionPrimary', $item).text(item.regionPrimary.name); $('.OrganisationMemberCount', $item).toggle(!!item.memberCount).find('.MemberCount').text(item.memberCount); $('.OrganisationOpportunityCount', $item).toggle(!!item.opportunityCount).find('.MemberCount').text(item.opportunityCount); @@ -94,7 +94,7 @@ { //sax.debug('po.updateOrganisation', $item, item); $('.OrganisationId', $frm.orgId).val(item.id); - + po.data.$item = $item; po.data.item = item; po.ajax.profileOrganisation = po.ajax.profileOrganisation || { url: sax.url.base + 'Account/Ajax/GetProfileOrganisation', formId: 'frmProfileOrganisationId', callbackFn: po.doUpdateOrganisation }; sax.doAjaxRequest(po.ajax.profileOrganisation, 50); } @@ -112,8 +112,8 @@ $('.OrganisationType', $frm.org).val(item.typeCode); $('.OrganisationSportId', $frm.org).val(item.sportId); $('.OrganisationSportCaption', $frm.org).val(item.sportName); - $('.OrganisationRegionIdCountry', $frm.org).val(item.regionIdCountry); - $('.OrganisationRegionCaptionCountry', $frm.org).val(item.regionNameCountry); + $('.OrganisationRegionIdPrimary', $frm.org).val(item.regionPrimary.id); + $('.OrganisationRegionNamePrimary', $frm.org).val(item.regionPrimary.name); $('.OrganisationAbout', $frm.org).val(item.about); $('.OrganisationFacilities', $frm.org).val(item.facilities); $pnl.tblconMths.empty(); @@ -130,8 +130,8 @@ $('input.OrganisationAddressLine2', $frm.org).val(address.line2); $('input.OrganisationAddressLine3', $frm.org).val(address.line3); $('input.OrganisationAddressTown', $frm.org).val(address.town); - $('input.OrganisationAddressProvince', $frm.org).val(address.province); - $('input.OrganisationAddressCountry', $frm.org).val(address.country); + $('input.OrganisationAddressProvince', $frm.org).val(address.regionNameProvince); + $('input.OrganisationAddressCountry', $frm.org).val(address.regionNameCountry); $('input.OrganisationAddressPostalCode', $frm.org).val(address.postalCode); sax.openDialog($dlg.org, 720, 840); } @@ -143,6 +143,7 @@ if (!response) return; sax.refreshControlValues(response.controlValues, $frm.org); if (!response.success) return; + po.populateItem(po.data.$item, response.organisation); $dlg.org.dialog('close'); po.cleanUp(); } @@ -151,6 +152,8 @@ } po.cleanUp = function () { + delete po.data.item; + delete po.data.$item; } //-------------------------------------------------------------- @@ -165,14 +168,14 @@ } //-------------------------------------------------------------- - po.selectOrgRegionCountry = function () + po.selectOrgRegionPrimary = function () { - sax.mdl.selectRegion.fn.selectRegion({ caption: sax.dlg.title.selectRegion, regionId: $('.OrganisationRegionIdCountry', $frm.org).val(), typesInc: [30], fnCallback: po.onSelectOrgRegionCountry }) + sax.mdl.selectRegion.fn.selectRegion({ caption: sax.dlg.title.selectRegion, regionId: $('.OrganisationRegionIdCountry', $frm.org).val(), typesInc: [30], fnCallback: po.onSelectOrgRegionPrimary }) } - po.onSelectOrgRegionCountry = function (region) + po.onSelectOrgRegionPrimary = function (region) { - sax.clearControlError($('.OrganisationRegionIdCountry', $frm.org).val(region.id)); - $('.OrganisationRegionCaptionCountry', $frm.org).val(region ? region.name : ''); + sax.clearControlError($('.OrganisationRegionIdPrimary', $frm.org).val(region.id)); + $('.OrganisationRegionNamePrimary', $frm.org).val(region ? region.name : ''); } //-------------------------------------------------------------- @@ -240,7 +243,7 @@ $dlg.org = $('#dlgProfileOrganisation').dialog(opt); $frm.org = $('#frmProfileOrganisation', $dlg.org); $frm.org.on('dblclick', 'input.OrganisationSportCaption', po.selectOrgSport).on('click', 'button.SelectSport', po.selectOrgSport); - $frm.org.on('dblclick', 'input.OrganisationRegionCaptionCountry', po.selectOrgRegionCountry).on('click', 'button.SelectRegionCountry', po.selectOrgRegionCountry); + $frm.org.on('dblclick', 'input.OrganisationRegionNameCountry', po.selectOrgRegionPrimary).on('click', 'button.SelectOrganisationRegionPrimary', po.selectOrgRegionPrimary); $frm.org.on('click', '.OrganisationContactMethods .sax-action.sax-add', po.addConMth); $pnl.conMths = $('.OrganisationContactMethods', $frm.org); $pnl.tblconMths = $('table.sax-data', $pnl.conMths); diff --git a/SaxoniteStandardBase/Base/App/Sax-Config.cs b/SaxoniteStandardBase/Base/App/Sax-Config.cs index d067fe6..0602afd 100644 --- a/SaxoniteStandardBase/Base/App/Sax-Config.cs +++ b/SaxoniteStandardBase/Base/App/Sax-Config.cs @@ -43,6 +43,46 @@ namespace Saxonite.Net.Standard.Base.App } } } + public static class User + { + public static class Image + { + // Physical image maximum dimensions - reduced on upload. + public static ushort WidthMax { get; } = 240; + public static ushort HeightMax { get; } = 320; + } + } + public static class Organisation + { + public static class Logo + { + // Physical image maximum dimensions - reduced on upload. + public static ushort WidthMax { get; } = 240; + public static ushort HeightMax { get; } = 240; + // Maximum image display dimensions - organisation list. + public static ushort DisplayWidthMax_List { get; } = 48; + public static ushort DisplayHeightMax_List { get; } = 48; + // Maximum image display dimensions - organisation edit etc. + public static ushort DisplayWidthMax_Item { get; } = 120; + public static ushort DisplayHeightMax_Item { get; } = 120; + } + } + public static class Articles + { + public static class Segments + { + public static ushort Max { get; } = 12; + public static ushort CharsMax { get; } = 8000; + } + public static class Images + { + // Physical image maximum dimensions - reduced on upload. + public static ushort WidthMax { get; } = 800; + public static ushort HeightMax { get; } = 640; + } + public static ushort PublishMaxDaysBefore { get; } = 7; + public static ushort PublishMaxDaysAhead { get; } = 10; + } public class Online { public static class Session @@ -73,45 +113,6 @@ namespace Saxonite.Net.Standard.Base.App public static int Periodic { get; } = SacsSystem.DebugMode ? 120 : 1800; } } - public static class Profile - { - public static class User - { - public static class Image - { - public static ushort WidthMax { get; } = 320; - public static ushort HeightMax { get; } = 400; - } - } - } - public static class Links - { - public static class Targets - { - public static String Website { get; } = "sax-lnk-web"; - public static String Wikipedia { get; } = "sax-lnk-wikip"; - public static String Facebook { get; } = "sax-lnk-fbook"; - public static String Twitter { get; } = "sax-lnk-twit"; - public static String Instagram { get; } = "sax-lnk-insta"; - public static String LinkedIn { get; } = "sax-lnk-lnkin"; - public static String Article { get; } = "sax-art-lnk"; - } - } - public static class Articles - { - public static class Segments - { - public static ushort Max { get; } = 12; - public static ushort CharsMax { get; } = 8000; - } - public static class Images - { - public static ushort WidthMax { get; } = 800; - public static ushort HeightMax { get; } = 640; - } - public static ushort PublishMaxDaysBefore { get; } = 7; - public static ushort PublishMaxDaysAhead { get; } = 10; - } public static class Search { public static class Users @@ -140,6 +141,19 @@ namespace Saxonite.Net.Standard.Base.App } } } + public static class Links + { + public static class Targets + { + public static String Website { get; } = "sax-lnk-web"; + public static String Wikipedia { get; } = "sax-lnk-wikip"; + public static String Facebook { get; } = "sax-lnk-fbook"; + public static String Twitter { get; } = "sax-lnk-twit"; + public static String Instagram { get; } = "sax-lnk-insta"; + public static String LinkedIn { get; } = "sax-lnk-lnkin"; + public static String Article { get; } = "sax-art-lnk"; + } + } } //------------------------------------------------------------------- diff --git a/SaxoniteStandardBase/Base/App/Sax-Version.cs b/SaxoniteStandardBase/Base/App/Sax-Version.cs index e177f21..c3813b9 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.6.3"; + private static String version { get; } = "1.2.6.4"; // Retrieve version. public static String GetFullVersion () { return version; } public static String GetAppVersion () diff --git a/SaxoniteStandardBase/Base/App/Sax.cs b/SaxoniteStandardBase/Base/App/Sax.cs index d33a21f..d69c509 100644 --- a/SaxoniteStandardBase/Base/App/Sax.cs +++ b/SaxoniteStandardBase/Base/App/Sax.cs @@ -191,11 +191,15 @@ namespace Saxonite.Net.Standard.Base.App /// User's profile image. Avatar = 100, /// User's profile image, uploaded awaiting confirmation. - AvatarUpload = 101, + Avatar_Upload = 101, + /// Organisation's main logo. + OrganisationLogo = 120, + /// Organisation's main logo, uploaded awaiting confirmation. + OrganisationLogo_Upload = 121, /// Article image. Article = 200, /// Article image uploaded, awaiting confirmation. - ArticleUpload = 201, + Article_Upload = 201, } //------------------------------------------------------------------- diff --git a/SaxoniteStandardBase/Base/App/SaxDbDfn_Core.cs b/SaxoniteStandardBase/Base/App/SaxDbDfn_Core.cs index 8d81304..df0f01e 100644 --- a/SaxoniteStandardBase/Base/App/SaxDbDfn_Core.cs +++ b/SaxoniteStandardBase/Base/App/SaxDbDfn_Core.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using System.Linq; //--------------------------------------------------------------------------- -// Code generated by Sarand Database Utilities Wed 18-May-2022 18:33:47 +// Code generated by Sarand Database Utilities Wed 25-May-2022 20:26:17 //--------------------------------------------------------------------------- namespace Saxonite.Base.App { @@ -164,8 +164,6 @@ namespace Saxonite.Base.App Organisation_Facilities, Organisation_ContactData, Organisation_CountryIso2Code, - Organisation_PhoneMain, - Organisation_PhoneAlt, Organisation_Notes, Organisation_Flags, Organisation_Status, @@ -380,7 +378,7 @@ namespace Saxonite.Base.App //------------------------------------------------------------------- // Data members. private static readonly int tableCount = 19; - private static readonly int columnCount = 332; + private static readonly int columnCount = 330; private static ulong maxTextLen = 0; //------------------------------------------------------------------- @@ -692,8 +690,6 @@ namespace Saxonite.Base.App new ColumnDfn(7,"Facilities",0,-9,false,2000), new ColumnDfn(7,"ContactData",0,-9,false,0), new ColumnDfn(7,"CountryIso2Code",0,12,false,2), - new ColumnDfn(7,"PhoneMain",0,-9,false,50), - new ColumnDfn(7,"PhoneAlt",0,-9,false,50), new ColumnDfn(7,"Notes",0,-9,false,2000), new ColumnDfn(7,"Flags",3,5,false,5), new ColumnDfn(7,"Status",0,12,false,3), diff --git a/SaxoniteStandardBase/Base/App/SaxSystem-CodeTypes.cs b/SaxoniteStandardBase/Base/App/SaxSystem-CodeTypes.cs index f4c184f..bd34be2 100644 --- a/SaxoniteStandardBase/Base/App/SaxSystem-CodeTypes.cs +++ b/SaxoniteStandardBase/Base/App/SaxSystem-CodeTypes.cs @@ -171,9 +171,11 @@ namespace Saxonite.Net.Standard.Base.App //--------------------------------------------------------------- // Image types. imageTypeCodes.Add(Sax.ImageType.Avatar, "Avatar", "Avatar"); - imageTypeCodes.Add(Sax.ImageType.AvatarUpload, "AvatarUpload", "Avatar - upload"); + imageTypeCodes.Add(Sax.ImageType.Avatar_Upload, "AvatarUpload", "Avatar - upload"); + imageTypeCodes.Add(Sax.ImageType.OrganisationLogo, "OrganisationLogo", "Organisation logo"); + imageTypeCodes.Add(Sax.ImageType.OrganisationLogo_Upload, "OrganisationLogoUpload", "Organisation logo - upload"); imageTypeCodes.Add(Sax.ImageType.Article, "Article", "Article"); - imageTypeCodes.Add(Sax.ImageType.ArticleUpload, "ArticleUpload", "Article - upload"); + imageTypeCodes.Add(Sax.ImageType.Article_Upload, "ArticleUpload", "Article - upload"); //--------------------------------------------------------------- // Organisation types. diff --git a/SaxoniteStandardBase/Base/Bus/SaxOrganisation.cs b/SaxoniteStandardBase/Base/Bus/SaxOrganisation.cs index e012d69..1c8488b 100644 --- a/SaxoniteStandardBase/Base/Bus/SaxOrganisation.cs +++ b/SaxoniteStandardBase/Base/Bus/SaxOrganisation.cs @@ -25,8 +25,6 @@ namespace Saxonite.Net.Standard.Base.Bus OrganisationType_Desc, OrganisationCategory, OrganisationCategory_Desc, - PhoneMain, - PhoneAlt, // Sport. SportId, Sport_Name, @@ -38,6 +36,8 @@ namespace Saxonite.Net.Standard.Base.Bus User_Count, /// Number of users who are 'members' of the organisation. Member_Count, + /// Number of active logo images. + LogoImage_Count, // Common. Notes, Flags, @@ -60,8 +60,6 @@ namespace Saxonite.Net.Standard.Base.Bus public String About { get; set; } = String.Empty; public String Facilities { get; set; } = String.Empty; public String ContactData { get; set; } = String.Empty; - public String PhoneMain { get; set; } = String.Empty; - public String PhoneAlt { get; set; } = String.Empty; } //------------------------------------------------------------------- @@ -116,16 +114,6 @@ namespace Saxonite.Net.Standard.Base.Bus get { return data.ContactData; } set { CheckChanged(data.ContactData, value); data.ContactData = value; } } - public String PhoneMain - { - get { return data.PhoneMain; } - set { CheckChanged(data.PhoneMain, value); data.PhoneMain = value; } - } - public String PhoneAlt - { - get { return data.PhoneAlt; } - set { CheckChanged(data.PhoneAlt, value); data.PhoneAlt = value; } - } // Population. internal void PopulateFrom(Data data) { @@ -137,8 +125,6 @@ namespace Saxonite.Net.Standard.Base.Bus About = data.About; Facilities = data.Facilities; ContactData = data.ContactData; - PhoneMain = data.PhoneMain; - PhoneAlt = data.PhoneAlt; base.PopulateFrom(data); } public Data Clone() @@ -162,8 +148,6 @@ namespace Saxonite.Net.Standard.Base.Bus public ValidValue About { get; set; } = ValidValue.Valid; public ValidValue Facilities { get; set; } = ValidValue.Valid; public ValidValue ContactData { get; set; } = ValidValue.Valid; - public ValidValue PhoneMain { get; set; } = ValidValue.Valid; - public ValidValue PhoneAlt { get; set; } = ValidValue.Valid; } //------------------------------------------------------------------- @@ -280,16 +264,6 @@ namespace Saxonite.Net.Standard.Base.Bus get { return data.ContactData; } set { data.ContactData = value; } } - public String PhoneMain - { - get { return data.PhoneMain; } - set { data.PhoneMain = value; } - } - public String PhoneAlt - { - get { return data.PhoneAlt; } - set { data.PhoneAlt = value; } - } //------------------------------------------------------------------- // Flags - positions 1-16 available. diff --git a/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-Organisation.cs b/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-Organisation.cs index ba9b872..4cc2f50 100644 --- a/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-Organisation.cs +++ b/SaxoniteStandardBase/Base/Mgr/SaxAccountMgr-Organisation.cs @@ -18,6 +18,7 @@ namespace Saxonite.Net.Standard.Base.Mgr // Account Mgr - Organisation //******************************************************************* // Utilities. + /// Get number of active members for Organisation. public long GetOrganisationMemberCount(SysId organisationId) { if (!SysId.IsValid(organisationId)) return 0; @@ -28,6 +29,15 @@ namespace Saxonite.Net.Standard.Base.Mgr prmUserOrganisation.StatusCat = SaxUserOrganisation.StatusCat.ActiveOnly; return GetUserOrganisationCount(prmUserOrganisation); } + /// Get number of active opportunities for organisation. + public long GetOrganisationOpportunityCount(SysId organisationId) + { + if (!SysId.IsValid(organisationId)) return 0; + SaxOrganisationOpportunity.Params prmOrganisationOpportunity = new SaxOrganisationOpportunity.Params(); + prmOrganisationOpportunity.OrganisationIdsIncluded.Add(organisationId); + prmOrganisationOpportunity.StatusCat = SaxOrganisationOpportunity.StatusCat.ActiveOnly; + return ActivityMgr.GetOrganisationOpportunityCount(prmOrganisationOpportunity); + } //------------------------------------------------------------------- // Create item. @@ -114,7 +124,7 @@ namespace Saxonite.Net.Standard.Base.Mgr return SetErrorState("Sport must be specified."); } } - // Region. + // Primary Region. if (errField.RegionIdPrimary != SacsHierarchyItem.ValidValue.Ignore) { if (!SysId.IsValid(data.RegionIdPrimary)) @@ -123,6 +133,7 @@ namespace Saxonite.Net.Standard.Base.Mgr return SetErrorState("Region must be specified."); } } + // About. if (errField.About != SacsHierarchyItem.ValidValue.Ignore && !IsValidValue(data.About, "about", (long)SaxDbDfn_Core.GetColTextLen(SaxDbDfn_Core.ColumnIdx.Organisation_About))) { @@ -135,18 +146,8 @@ namespace Saxonite.Net.Standard.Base.Mgr errField.Facilities = SacsHierarchyItem.ValidValue.Invalid; return false; } - // Main phone. - if (errField.PhoneMain != SacsHierarchyItem.ValidValue.Ignore && !IsValidValue(data.PhoneMain, "phone", (long)SaxDbDfn_Core.GetColTextLen(SaxDbDfn_Core.ColumnIdx.Organisation_PhoneMain))) - { - errField.PhoneMain = SacsHierarchyItem.ValidValue.Invalid; - return false; - } - // Alt phone. - if (errField.PhoneAlt != SacsHierarchyItem.ValidValue.Ignore && !IsValidValue(data.PhoneAlt, "alt. phone", (long)SaxDbDfn_Core.GetColTextLen(SaxDbDfn_Core.ColumnIdx.Organisation_PhoneAlt))) - { - errField.PhoneAlt = SacsHierarchyItem.ValidValue.Invalid; - return false; - } + + // Contact data. // Common properties. if (errField.Notes != SacsHierarchyItem.ValidValue.Ignore && !IsValidValue(data.Notes, "notes", (long)SaxDbDfn_Core.GetColTextLen(SaxDbDfn_Core.ColumnIdx.Organisation_Notes))) diff --git a/SaxoniteStandardBase/Base/Mgr/SaxItemDataMgr-ItemImage.cs b/SaxoniteStandardBase/Base/Mgr/SaxItemDataMgr-ItemImage.cs index 05a81a2..abd9b2a 100644 --- a/SaxoniteStandardBase/Base/Mgr/SaxItemDataMgr-ItemImage.cs +++ b/SaxoniteStandardBase/Base/Mgr/SaxItemDataMgr-ItemImage.cs @@ -19,6 +19,16 @@ namespace Saxonite.Net.Standard.Base.Mgr // Item Data Mgr - Item Image //******************************************************************* // Utilities. + public bool ItemHasActiveImage(ItemId itemId, Sax.ImageType imageType) + { + ClearErrorState(); + if (!ItemId.IsValid(itemId) || (int)imageType < 1) return false; + SaxItemImage.Params prmItemImage = new SaxItemImage.Params(); + prmItemImage.ItemIdsIncluded.Add(itemId); + prmItemImage.ImageTypesIncluded.Add(imageType); + prmItemImage.StatusCat = SaxItemImage.StatusCat.ActiveOnly; + return GetItemImageCount(prmItemImage) == 1; + } //------------------------------------------------------------------- // Create item. diff --git a/SaxoniteStandardDbas/Dbas/Internal/ActivityFeedback_Db.cs b/SaxoniteStandardDbas/Dbas/Internal/ActivityFeedback_Db.cs index b4505bd..1dd6bf3 100644 --- a/SaxoniteStandardDbas/Dbas/Internal/ActivityFeedback_Db.cs +++ b/SaxoniteStandardDbas/Dbas/Internal/ActivityFeedback_Db.cs @@ -9,7 +9,7 @@ using System.Data.Common; using System.Linq; //--------------------------------------------------------------------------- -// Code generated by Sarand Database Utilities Wed 18-May-2022 18:33:47 +// Code generated by Sarand Database Utilities Wed 25-May-2022 20:26:17 //--------------------------------------------------------------------------- namespace Saxonite.Net.Standard.Dbas.Internal { diff --git a/SaxoniteStandardDbas/Dbas/Internal/ActivityIndex_Db.cs b/SaxoniteStandardDbas/Dbas/Internal/ActivityIndex_Db.cs index f9cd237..5d90a8a 100644 --- a/SaxoniteStandardDbas/Dbas/Internal/ActivityIndex_Db.cs +++ b/SaxoniteStandardDbas/Dbas/Internal/ActivityIndex_Db.cs @@ -9,7 +9,7 @@ using System.Data.Common; using System.Linq; //--------------------------------------------------------------------------- -// Code generated by Sarand Database Utilities Wed 18-May-2022 18:33:47 +// Code generated by Sarand Database Utilities Wed 25-May-2022 20:26:17 //--------------------------------------------------------------------------- namespace Saxonite.Net.Standard.Dbas.Internal { diff --git a/SaxoniteStandardDbas/Dbas/Internal/Article_Db.cs b/SaxoniteStandardDbas/Dbas/Internal/Article_Db.cs index 990ec53..b7be96d 100644 --- a/SaxoniteStandardDbas/Dbas/Internal/Article_Db.cs +++ b/SaxoniteStandardDbas/Dbas/Internal/Article_Db.cs @@ -9,7 +9,7 @@ using System.Data.Common; using System.Linq; //--------------------------------------------------------------------------- -// Code generated by Sarand Database Utilities Wed 18-May-2022 18:33:47 +// Code generated by Sarand Database Utilities Wed 25-May-2022 20:26:17 //--------------------------------------------------------------------------- namespace Saxonite.Net.Standard.Dbas.Internal { diff --git a/SaxoniteStandardDbas/Dbas/Internal/ItemAddress_Db.cs b/SaxoniteStandardDbas/Dbas/Internal/ItemAddress_Db.cs index 158f103..820d84e 100644 --- a/SaxoniteStandardDbas/Dbas/Internal/ItemAddress_Db.cs +++ b/SaxoniteStandardDbas/Dbas/Internal/ItemAddress_Db.cs @@ -9,7 +9,7 @@ using System.Data.Common; using System.Linq; //--------------------------------------------------------------------------- -// Code generated by Sarand Database Utilities Wed 18-May-2022 18:33:47 +// Code generated by Sarand Database Utilities Wed 25-May-2022 20:26:17 //--------------------------------------------------------------------------- namespace Saxonite.Net.Standard.Dbas.Internal { diff --git a/SaxoniteStandardDbas/Dbas/Internal/ItemImage_Db.cs b/SaxoniteStandardDbas/Dbas/Internal/ItemImage_Db.cs index 3c4025b..981c22f 100644 --- a/SaxoniteStandardDbas/Dbas/Internal/ItemImage_Db.cs +++ b/SaxoniteStandardDbas/Dbas/Internal/ItemImage_Db.cs @@ -9,7 +9,7 @@ using System.Data.Common; using System.Linq; //--------------------------------------------------------------------------- -// Code generated by Sarand Database Utilities Wed 18-May-2022 18:33:47 +// Code generated by Sarand Database Utilities Wed 25-May-2022 20:26:17 //--------------------------------------------------------------------------- namespace Saxonite.Net.Standard.Dbas.Internal { diff --git a/SaxoniteStandardDbas/Dbas/Internal/ItemSport_Db.cs b/SaxoniteStandardDbas/Dbas/Internal/ItemSport_Db.cs index 365cc50..880cdcc 100644 --- a/SaxoniteStandardDbas/Dbas/Internal/ItemSport_Db.cs +++ b/SaxoniteStandardDbas/Dbas/Internal/ItemSport_Db.cs @@ -9,7 +9,7 @@ using System.Data.Common; using System.Linq; //--------------------------------------------------------------------------- -// Code generated by Sarand Database Utilities Wed 18-May-2022 18:33:47 +// Code generated by Sarand Database Utilities Wed 25-May-2022 20:26:17 //--------------------------------------------------------------------------- namespace Saxonite.Net.Standard.Dbas.Internal { diff --git a/SaxoniteStandardDbas/Dbas/Internal/OrganisationEvent_Db.cs b/SaxoniteStandardDbas/Dbas/Internal/OrganisationEvent_Db.cs index b64c01a..8a71935 100644 --- a/SaxoniteStandardDbas/Dbas/Internal/OrganisationEvent_Db.cs +++ b/SaxoniteStandardDbas/Dbas/Internal/OrganisationEvent_Db.cs @@ -9,7 +9,7 @@ using System.Data.Common; using System.Linq; //--------------------------------------------------------------------------- -// Code generated by Sarand Database Utilities Wed 18-May-2022 18:33:47 +// Code generated by Sarand Database Utilities Wed 25-May-2022 20:26:17 //--------------------------------------------------------------------------- namespace Saxonite.Net.Standard.Dbas.Internal { diff --git a/SaxoniteStandardDbas/Dbas/Internal/OrganisationOpportunity_Db.cs b/SaxoniteStandardDbas/Dbas/Internal/OrganisationOpportunity_Db.cs index 938eb63..e85e106 100644 --- a/SaxoniteStandardDbas/Dbas/Internal/OrganisationOpportunity_Db.cs +++ b/SaxoniteStandardDbas/Dbas/Internal/OrganisationOpportunity_Db.cs @@ -9,7 +9,7 @@ using System.Data.Common; using System.Linq; //--------------------------------------------------------------------------- -// Code generated by Sarand Database Utilities Wed 18-May-2022 18:33:47 +// Code generated by Sarand Database Utilities Wed 25-May-2022 20:26:17 //--------------------------------------------------------------------------- namespace Saxonite.Net.Standard.Dbas.Internal { diff --git a/SaxoniteStandardDbas/Dbas/Internal/Organisation_Db.cs b/SaxoniteStandardDbas/Dbas/Internal/Organisation_Db.cs index 975f32e..2e96918 100644 --- a/SaxoniteStandardDbas/Dbas/Internal/Organisation_Db.cs +++ b/SaxoniteStandardDbas/Dbas/Internal/Organisation_Db.cs @@ -9,7 +9,7 @@ using System.Data.Common; using System.Linq; //--------------------------------------------------------------------------- -// Code generated by Sarand Database Utilities Wed 18-May-2022 18:33:47 +// Code generated by Sarand Database Utilities Wed 25-May-2022 20:26:17 //--------------------------------------------------------------------------- namespace Saxonite.Net.Standard.Dbas.Internal { @@ -29,8 +29,6 @@ namespace Saxonite.Net.Standard.Dbas.Internal public ColumnDataString _Facilities { get; } = new ColumnDataString("Facilities"); public ColumnDataString _ContactData { get; } = new ColumnDataString("ContactData"); public ColumnDataString _CountryIso2Code { get; } = new ColumnDataString("CountryIso2Code"); - public ColumnDataString _PhoneMain { get; } = new ColumnDataString("PhoneMain"); - public ColumnDataString _PhoneAlt { get; } = new ColumnDataString("PhoneAlt"); public ColumnDataString _Notes { get; } = new ColumnDataString("Notes"); public ColumnDataInt16 _Flags { get; } = new ColumnDataInt16("Flags"); public ColumnDataString _Status { get; } = new ColumnDataString("Status"); @@ -56,8 +54,6 @@ namespace Saxonite.Net.Standard.Dbas.Internal "Facilities", "ContactData", "CountryIso2Code", - "PhoneMain", - "PhoneAlt", "Notes", "Flags", "Status", @@ -109,8 +105,6 @@ namespace Saxonite.Net.Standard.Dbas.Internal public String _Facilities { get { return Cols._Facilities.Value; } set { Cols._Facilities.Value = value; } } public String _ContactData { get { return Cols._ContactData.Value; } set { Cols._ContactData.Value = value; } } public String _CountryIso2Code { get { return Cols._CountryIso2Code.Value; } set { Cols._CountryIso2Code.Value = value; } } - public String _PhoneMain { get { return Cols._PhoneMain.Value; } set { Cols._PhoneMain.Value = value; } } - public String _PhoneAlt { get { return Cols._PhoneAlt.Value; } set { Cols._PhoneAlt.Value = value; } } public String _Notes { get { return Cols._Notes.Value; } set { Cols._Notes.Value = value; } } public Int16 _Flags { get { return Cols._Flags.Value; } set { Cols._Flags.Value = value; } } public String _Status { get { return Cols._Status.Value; } set { Cols._Status.Value = value; } } @@ -151,8 +145,6 @@ namespace Saxonite.Net.Standard.Dbas.Internal _Facilities = GetFieldString(dbDataReader, "Facilities", _Facilities); _ContactData = GetFieldString(dbDataReader, "ContactData", _ContactData); _CountryIso2Code = GetFieldString(dbDataReader, "CountryIso2Code", _CountryIso2Code); - _PhoneMain = GetFieldString(dbDataReader, "PhoneMain", _PhoneMain); - _PhoneAlt = GetFieldString(dbDataReader, "PhoneAlt", _PhoneAlt); _Notes = GetFieldString(dbDataReader, "Notes", _Notes); _Flags = GetFieldInt16(dbDataReader, "Flags", _Flags); _Status = GetFieldString(dbDataReader, "Status", _Status); @@ -192,8 +184,6 @@ namespace Saxonite.Net.Standard.Dbas.Internal if (columnNames.ContainsCI("Facilities")) AddCommandParameter(dbCommand, "@Facilities", _Facilities); if (columnNames.ContainsCI("ContactData")) AddCommandParameter(dbCommand, "@ContactData", _ContactData); if (columnNames.ContainsCI("CountryIso2Code")) AddCommandParameter(dbCommand, "@CountryIso2Code", _CountryIso2Code); - if (columnNames.ContainsCI("PhoneMain")) AddCommandParameter(dbCommand, "@PhoneMain", _PhoneMain); - if (columnNames.ContainsCI("PhoneAlt")) AddCommandParameter(dbCommand, "@PhoneAlt", _PhoneAlt); if (columnNames.ContainsCI("Notes")) AddCommandParameter(dbCommand, "@Notes", _Notes); if (columnNames.ContainsCI("Flags")) AddCommandParameter(dbCommand, "@Flags", _Flags); if (columnNames.ContainsCI("Status")) AddCommandParameter(dbCommand, "@Status", _Status); @@ -248,8 +238,6 @@ namespace Saxonite.Net.Standard.Dbas.Internal if (columnNames.ContainsCI("Facilities")) AddCommandParameter(dbCommand, "@Facilities", _Facilities); if (columnNames.ContainsCI("ContactData")) AddCommandParameter(dbCommand, "@ContactData", _ContactData); if (columnNames.ContainsCI("CountryIso2Code")) AddCommandParameter(dbCommand, "@CountryIso2Code", _CountryIso2Code); - if (columnNames.ContainsCI("PhoneMain")) AddCommandParameter(dbCommand, "@PhoneMain", _PhoneMain); - if (columnNames.ContainsCI("PhoneAlt")) AddCommandParameter(dbCommand, "@PhoneAlt", _PhoneAlt); if (columnNames.ContainsCI("Notes")) AddCommandParameter(dbCommand, "@Notes", _Notes); if (columnNames.ContainsCI("Flags")) AddCommandParameter(dbCommand, "@Flags", _Flags); if (columnNames.ContainsCI("Status")) AddCommandParameter(dbCommand, "@Status", _Status); diff --git a/SaxoniteStandardDbas/Dbas/Internal/Region_Db.cs b/SaxoniteStandardDbas/Dbas/Internal/Region_Db.cs index fa7dcbe..caec974 100644 --- a/SaxoniteStandardDbas/Dbas/Internal/Region_Db.cs +++ b/SaxoniteStandardDbas/Dbas/Internal/Region_Db.cs @@ -9,7 +9,7 @@ using System.Data.Common; using System.Linq; //--------------------------------------------------------------------------- -// Code generated by Sarand Database Utilities Wed 18-May-2022 18:33:47 +// Code generated by Sarand Database Utilities Wed 25-May-2022 20:26:17 //--------------------------------------------------------------------------- namespace Saxonite.Net.Standard.Dbas.Internal { diff --git a/SaxoniteStandardDbas/Dbas/Internal/Sport_Db.cs b/SaxoniteStandardDbas/Dbas/Internal/Sport_Db.cs index 17ea304..1919536 100644 --- a/SaxoniteStandardDbas/Dbas/Internal/Sport_Db.cs +++ b/SaxoniteStandardDbas/Dbas/Internal/Sport_Db.cs @@ -9,7 +9,7 @@ using System.Data.Common; using System.Linq; //--------------------------------------------------------------------------- -// Code generated by Sarand Database Utilities Wed 18-May-2022 18:33:47 +// Code generated by Sarand Database Utilities Wed 25-May-2022 20:26:17 //--------------------------------------------------------------------------- namespace Saxonite.Net.Standard.Dbas.Internal { diff --git a/SaxoniteStandardDbas/Dbas/Internal/UserAccount_Db.cs b/SaxoniteStandardDbas/Dbas/Internal/UserAccount_Db.cs index a8f442a..a9ce4a3 100644 --- a/SaxoniteStandardDbas/Dbas/Internal/UserAccount_Db.cs +++ b/SaxoniteStandardDbas/Dbas/Internal/UserAccount_Db.cs @@ -9,7 +9,7 @@ using System.Data.Common; using System.Linq; //--------------------------------------------------------------------------- -// Code generated by Sarand Database Utilities Wed 18-May-2022 18:33:47 +// Code generated by Sarand Database Utilities Wed 25-May-2022 20:26:17 //--------------------------------------------------------------------------- namespace Saxonite.Net.Standard.Dbas.Internal { diff --git a/SaxoniteStandardDbas/Dbas/Internal/UserActivity_Db.cs b/SaxoniteStandardDbas/Dbas/Internal/UserActivity_Db.cs index c86dd61..08492e8 100644 --- a/SaxoniteStandardDbas/Dbas/Internal/UserActivity_Db.cs +++ b/SaxoniteStandardDbas/Dbas/Internal/UserActivity_Db.cs @@ -9,7 +9,7 @@ using System.Data.Common; using System.Linq; //--------------------------------------------------------------------------- -// Code generated by Sarand Database Utilities Wed 18-May-2022 18:33:47 +// Code generated by Sarand Database Utilities Wed 25-May-2022 20:26:17 //--------------------------------------------------------------------------- namespace Saxonite.Net.Standard.Dbas.Internal { diff --git a/SaxoniteStandardDbas/Dbas/Internal/UserOnline_Db.cs b/SaxoniteStandardDbas/Dbas/Internal/UserOnline_Db.cs index 7a64d4d..7deeccf 100644 --- a/SaxoniteStandardDbas/Dbas/Internal/UserOnline_Db.cs +++ b/SaxoniteStandardDbas/Dbas/Internal/UserOnline_Db.cs @@ -9,7 +9,7 @@ using System.Data.Common; using System.Linq; //--------------------------------------------------------------------------- -// Code generated by Sarand Database Utilities Wed 18-May-2022 18:33:47 +// Code generated by Sarand Database Utilities Wed 25-May-2022 20:26:17 //--------------------------------------------------------------------------- namespace Saxonite.Net.Standard.Dbas.Internal { diff --git a/SaxoniteStandardDbas/Dbas/Internal/UserOrganisation_Db.cs b/SaxoniteStandardDbas/Dbas/Internal/UserOrganisation_Db.cs index ddf655e..cee118c 100644 --- a/SaxoniteStandardDbas/Dbas/Internal/UserOrganisation_Db.cs +++ b/SaxoniteStandardDbas/Dbas/Internal/UserOrganisation_Db.cs @@ -9,7 +9,7 @@ using System.Data.Common; using System.Linq; //--------------------------------------------------------------------------- -// Code generated by Sarand Database Utilities Wed 18-May-2022 18:33:47 +// Code generated by Sarand Database Utilities Wed 25-May-2022 20:26:17 //--------------------------------------------------------------------------- namespace Saxonite.Net.Standard.Dbas.Internal { diff --git a/SaxoniteStandardDbas/Dbas/Internal/UserRole_Db.cs b/SaxoniteStandardDbas/Dbas/Internal/UserRole_Db.cs index 9bdd645..7c77815 100644 --- a/SaxoniteStandardDbas/Dbas/Internal/UserRole_Db.cs +++ b/SaxoniteStandardDbas/Dbas/Internal/UserRole_Db.cs @@ -9,7 +9,7 @@ using System.Data.Common; using System.Linq; //--------------------------------------------------------------------------- -// Code generated by Sarand Database Utilities Wed 18-May-2022 18:33:47 +// Code generated by Sarand Database Utilities Wed 25-May-2022 20:26:17 //--------------------------------------------------------------------------- namespace Saxonite.Net.Standard.Dbas.Internal { diff --git a/SaxoniteStandardDbas/Dbas/Internal/UserSession_Db.cs b/SaxoniteStandardDbas/Dbas/Internal/UserSession_Db.cs index 7acb1b9..661b4ed 100644 --- a/SaxoniteStandardDbas/Dbas/Internal/UserSession_Db.cs +++ b/SaxoniteStandardDbas/Dbas/Internal/UserSession_Db.cs @@ -9,7 +9,7 @@ using System.Data.Common; using System.Linq; //--------------------------------------------------------------------------- -// Code generated by Sarand Database Utilities Wed 18-May-2022 18:33:47 +// Code generated by Sarand Database Utilities Wed 25-May-2022 20:26:17 //--------------------------------------------------------------------------- namespace Saxonite.Net.Standard.Dbas.Internal { diff --git a/SaxoniteStandardDbas/Dbas/Internal/UserSportExperience_Db.cs b/SaxoniteStandardDbas/Dbas/Internal/UserSportExperience_Db.cs index 63360d7..4b0e948 100644 --- a/SaxoniteStandardDbas/Dbas/Internal/UserSportExperience_Db.cs +++ b/SaxoniteStandardDbas/Dbas/Internal/UserSportExperience_Db.cs @@ -9,7 +9,7 @@ using System.Data.Common; using System.Linq; //--------------------------------------------------------------------------- -// Code generated by Sarand Database Utilities Wed 18-May-2022 18:33:47 +// Code generated by Sarand Database Utilities Wed 25-May-2022 20:26:17 //--------------------------------------------------------------------------- namespace Saxonite.Net.Standard.Dbas.Internal { diff --git a/SaxoniteStandardDbas/Dbas/Internal/UserSport_Db.cs b/SaxoniteStandardDbas/Dbas/Internal/UserSport_Db.cs index bba1c45..0ababe9 100644 --- a/SaxoniteStandardDbas/Dbas/Internal/UserSport_Db.cs +++ b/SaxoniteStandardDbas/Dbas/Internal/UserSport_Db.cs @@ -9,7 +9,7 @@ using System.Data.Common; using System.Linq; //--------------------------------------------------------------------------- -// Code generated by Sarand Database Utilities Wed 18-May-2022 18:33:47 +// Code generated by Sarand Database Utilities Wed 25-May-2022 20:26:17 //--------------------------------------------------------------------------- namespace Saxonite.Net.Standard.Dbas.Internal { diff --git a/SaxoniteStandardDbas/Dbas/Mgr/SaxAccountMgr_Dbas-Organisation.cs b/SaxoniteStandardDbas/Dbas/Mgr/SaxAccountMgr_Dbas-Organisation.cs index b27680f..257c867 100644 --- a/SaxoniteStandardDbas/Dbas/Mgr/SaxAccountMgr_Dbas-Organisation.cs +++ b/SaxoniteStandardDbas/Dbas/Mgr/SaxAccountMgr_Dbas-Organisation.cs @@ -219,8 +219,6 @@ namespace Saxonite.Net.Standard.Dbas.Mgr dbOrganisation._About = StringUtil.EmptyOnNull(organisation.About); dbOrganisation._Facilities = StringUtil.EmptyOnNull(organisation.Facilities); dbOrganisation._ContactData = StringUtil.EmptyOnNull(organisation.ContactData); - dbOrganisation._PhoneMain = StringUtil.EmptyOnNull(organisation.PhoneMain); - dbOrganisation._PhoneAlt = StringUtil.EmptyOnNull(organisation.PhoneAlt); dbOrganisation._Notes = StringUtil.EmptyOnNull(organisation.Notes); dbOrganisation._Status = SacsSystem.GetStatusCode(organisation.Status); dbOrganisation._Flags = (short)organisation.Flags; @@ -246,8 +244,6 @@ namespace Saxonite.Net.Standard.Dbas.Mgr organisation.About = dbOrganisation._About; organisation.Facilities = dbOrganisation._Facilities; organisation.ContactData = dbOrganisation._ContactData; - organisation.PhoneMain = dbOrganisation._PhoneMain; - organisation.PhoneAlt = dbOrganisation._PhoneAlt; organisation.Notes = dbOrganisation._Notes; organisation.Status = SacsSystem.GetStatusForCode(dbOrganisation._Status); organisation.SetCreateStamp(dbOrganisation._CreateOn, new SysId(dbOrganisation._CreateBy), dbOrganisation._CreateHost); @@ -264,6 +260,7 @@ namespace Saxonite.Net.Standard.Dbas.Mgr private static readonly String prefixOrganisation_Region = "Org_Rgn_"; private static readonly String colOrganisation_User_Count = "Org_Usr_Count"; private static readonly String colOrganisation_Member_Count = "Org_Mbr_Count"; + private static readonly String colOrganisation_LogoImage_Count = "Org_LogoImg_Count"; private void SetOrganisationColDfn (ColumnDfn col, String caption, int width, SaxDbDfn_Core.ColumnIdx dbCol, ColumnAlignment align = ColumnAlignment.Default, DataValue.DataType dataType = DataValue.DataType.String, @@ -306,12 +303,6 @@ namespace Saxonite.Net.Standard.Dbas.Mgr case SaxOrganisation.Column.OrganisationCategory_Desc: SetOrganisationColDfn(columnDfn, "Category", 80, SaxDbDfn_Core.ColumnIdx.Organisation_OrganisationCategory, ColumnAlignment.Center, DataValue.DataType.String); break; - case SaxOrganisation.Column.PhoneMain: - SetOrganisationColDfn(columnDfn, "Phone", 100, SaxDbDfn_Core.ColumnIdx.Organisation_PhoneMain); - break; - case SaxOrganisation.Column.PhoneAlt: - SetOrganisationColDfn(columnDfn, "Alt. phone", 100, SaxDbDfn_Core.ColumnIdx.Organisation_PhoneAlt); - break; //----------------------------------------------------------- // Sport. @@ -343,6 +334,10 @@ namespace Saxonite.Net.Standard.Dbas.Mgr SetOrganisationColDfn(columnDfn, "Members", 80, SaxDbDfn_Core.ColumnIdx.Unknown, ColumnAlignment.Center, DataValue.DataType.Long); columnDfn.DataName = colOrganisation_Member_Count; break; + case SaxOrganisation.Column.LogoImage_Count: + SetOrganisationColDfn(columnDfn, "Logos", 80, SaxDbDfn_Core.ColumnIdx.Unknown, ColumnAlignment.Center, DataValue.DataType.Long); + columnDfn.DataName = colOrganisation_LogoImage_Count; + break; //----------------------------------------------------------- // Common properties. @@ -606,10 +601,6 @@ namespace Saxonite.Net.Standard.Dbas.Mgr query.Columns.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.Organisation_Id, aliasOrganisation)); if (columns.HasId(SaxOrganisation.Column.Name)) query.Columns.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.Organisation_Name, aliasOrganisation)); - if (columns.HasId(SaxOrganisation.Column.PhoneMain)) - query.Columns.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.Organisation_PhoneMain, aliasOrganisation)); - if (columns.HasId(SaxOrganisation.Column.PhoneAlt)) - query.Columns.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.Organisation_PhoneAlt, aliasOrganisation)); if (columns.HasId(SaxOrganisation.Column.OrganisationType) || columns.HasId(SaxOrganisation.Column.OrganisationType_Desc)) query.Columns.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.Organisation_OrganisationType, aliasOrganisation)); if (columns.HasId(SaxOrganisation.Column.OrganisationCategory) || columns.HasId(SaxOrganisation.Column.OrganisationCategory_Desc)) @@ -674,6 +665,14 @@ namespace Saxonite.Net.Standard.Dbas.Mgr queryUserCount.Columns.Add("Count(*)"); queryUserCount.Filters.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.UserOrganisation_OrganisationId, aliasUserCount) + "=" + SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.Organisation_Id, aliasOrganisation)); queryUserCount.Filters.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.UserOrganisation_Status, aliasUserCount) + "='" + SacsSystem.GetStatusCode(SaStatus.Active) + "'"); + // Join to User Account. + String aliasUserAccount = "aUsrOrg_UsrAcc"; + DbQueryJoin joinUserAccount = new DbQueryJoin(DbJoinType.Inner); + joinUserAccount.Table = new StringPair(SaxDbDfn_Core.GetTableName(SaxDbDfn_Core.TableIdx.UserAccount), aliasUserAccount); + joinUserAccount.Filters.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.UserAccount_Id, aliasUserAccount) + "=" + SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.UserOrganisation_UserAccountId, aliasUserCount)); + joinUserAccount.Filters.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.UserAccount_Status, aliasUserAccount) + "='" + SacsSystem.GetStatusCode(SaStatus.Active) + "'"); + queryUserCount.Joins.Add(joinUserAccount); + // Construct sub-query. query.Columns.Add("(" + queryUserCount.BuildQuery() + ")", colOrganisation_User_Count); } @@ -688,9 +687,32 @@ namespace Saxonite.Net.Standard.Dbas.Mgr queryMemberCount.Filters.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.UserOrganisation_OrganisationId, aliasMemberCount) + "=" + SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.Organisation_Id, aliasOrganisation)); queryMemberCount.Filters.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.UserOrganisation_OrganisationRoleCount, aliasMemberCount) + ">0"); queryMemberCount.Filters.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.UserOrganisation_Status, aliasMemberCount) + "='" + SacsSystem.GetStatusCode(SaStatus.Active) + "'"); + // Join to User Account. + String aliasUserAccount = "aUsrOrg_UsrAcc"; + DbQueryJoin joinUserAccount = new DbQueryJoin(DbJoinType.Inner); + joinUserAccount.Table = new StringPair(SaxDbDfn_Core.GetTableName(SaxDbDfn_Core.TableIdx.UserAccount), aliasUserAccount); + joinUserAccount.Filters.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.UserAccount_Id, aliasUserAccount) + "=" + SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.UserOrganisation_UserAccountId, aliasMemberCount)); + joinUserAccount.Filters.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.UserAccount_Status, aliasUserAccount) + "='" + SacsSystem.GetStatusCode(SaStatus.Active) + "'"); + queryMemberCount.Joins.Add(joinUserAccount); + // Construct sub-query. query.Columns.Add("(" + queryMemberCount.BuildQuery() + ")", colOrganisation_Member_Count); } + //--------------------------------------------------------------------- + // Logo Image count. + if (columns.HasId(SaxOrganisation.Column.LogoImage_Count)) + { + String aliasLogoCount = "aImg_Logo"; + DbQuery queryLogoCount = new DbQuery(CoreDatabase.DbmsType); + queryLogoCount.Tables.Add(SaxDbDfn_Core.GetTableName(SaxDbDfn_Core.TableIdx.ItemImage), aliasLogoCount); + queryLogoCount.Columns.Add("Count(*)"); + queryLogoCount.Filters.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.ItemImage_ItemType, aliasLogoCount) + "=" + (int)Sax.DataItemType.Organisation); + queryLogoCount.Filters.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.ItemImage_ItemId, aliasLogoCount) + "=" + SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.Organisation_Id, aliasOrganisation)); + queryLogoCount.Filters.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.ItemImage_Status, aliasLogoCount) + "='" + SacsSystem.GetStatusCode(SaStatus.Active) + "'"); + // Construct sub-query. + query.Columns.Add("(" + queryLogoCount.BuildQuery() + ")", colOrganisation_LogoImage_Count); + } + //--------------------------------------------------------------------- // Populate filter. query.Filters.AddRange(GetOrganisationQueryFilter(prm)); diff --git a/SaxoniteStandardDbas/Dbas/Mgr/SaxAccountMgr_Dbas-UserOrganisation.cs b/SaxoniteStandardDbas/Dbas/Mgr/SaxAccountMgr_Dbas-UserOrganisation.cs index 02ab4fc..51662bb 100644 --- a/SaxoniteStandardDbas/Dbas/Mgr/SaxAccountMgr_Dbas-UserOrganisation.cs +++ b/SaxoniteStandardDbas/Dbas/Mgr/SaxAccountMgr_Dbas-UserOrganisation.cs @@ -760,6 +760,15 @@ namespace Saxonite.Net.Standard.Dbas.Mgr qryOrgMbr.Columns.Add("Count(*)"); qryOrgMbr.Filters.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.UserOrganisation_OrganisationId, aliasOrgMbr) + "=" + SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.UserOrganisation_OrganisationId, aliasUserOrganisation)); qryOrgMbr.Filters.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.UserOrganisation_OrganisationRoleCount, aliasOrgMbr) + ">0"); + qryOrgMbr.Filters.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.UserOrganisation_Status, aliasOrgMbr) + "='" + SacsSystem.GetStatusCode(SaStatus.Active) + "'"); + // Join to User Account. + String aliasUserAccount = "aUsrOrg_UsrAcc"; + DbQueryJoin joinUserAccount = new DbQueryJoin(DbJoinType.Inner); + joinUserAccount.Table = new StringPair(SaxDbDfn_Core.GetTableName(SaxDbDfn_Core.TableIdx.UserAccount), aliasUserAccount); + joinUserAccount.Filters.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.UserAccount_Id, aliasUserAccount) + "=" + SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.UserOrganisation_UserAccountId, aliasOrgMbr)); + joinUserAccount.Filters.Add(SaxDbDfn_Core.GetColumnNameQual(SaxDbDfn_Core.ColumnIdx.UserAccount_Status, aliasUserAccount) + "='" + SacsSystem.GetStatusCode(SaStatus.Active) + "'"); + qryOrgMbr.Joins.Add(joinUserAccount); + // Construct sub-query. query.Columns.Add("(" + qryOrgMbr.BuildQuery() + ")", dataName_UserOrganisation_Organisation_Member_Count); } diff --git a/SaxoniteStandardDbas/Resources/Database/SaxDbDfn_Core.xml b/SaxoniteStandardDbas/Resources/Database/SaxDbDfn_Core.xml index 26cbd03..cdb51bc 100644 --- a/SaxoniteStandardDbas/Resources/Database/SaxDbDfn_Core.xml +++ b/SaxoniteStandardDbas/Resources/Database/SaxDbDfn_Core.xml @@ -1555,7 +1555,7 @@ false false 1 - 9 + 11 false @@ -2571,40 +2571,6 @@ dbo
Organisation
11 - PhoneMain - VarChar - - -9 - nvarchar - false - 50 - 100 - 0 - NoNulls - false - - - - dbo - Organisation
- 12 - PhoneAlt - VarChar - - -9 - nvarchar - false - 50 - 100 - 0 - NoNulls - false - -
- - dbo - Organisation
- 13 Notes VarChar @@ -2621,7 +2587,7 @@ dbo Organisation
- 14 + 12 Flags SmallInt @@ -2638,7 +2604,7 @@ dbo Organisation
- 15 + 13 Status VarChar @@ -2655,7 +2621,7 @@ dbo Organisation
- 16 + 14 CreateOn DateTime @@ -2672,7 +2638,7 @@ dbo Organisation
- 17 + 15 CreateBy Int @@ -2689,7 +2655,7 @@ dbo Organisation
- 18 + 16 CreateHost VarChar @@ -2706,7 +2672,7 @@ dbo Organisation
- 19 + 17 UpdateOn DateTime @@ -2723,7 +2689,7 @@ dbo Organisation
- 20 + 18 UpdateBy Int @@ -2740,7 +2706,7 @@ dbo Organisation
- 21 + 19 UpdateHost VarChar @@ -2765,8 +2731,8 @@ false false false - 1 - 14 + 2 + 16 false @@ -5823,7 +5789,7 @@ false false 1 - 16 + 20 false