using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Saxonite.Net.Core.Web.Online.Models; using Saxonite.Net.Core.Web.Base.App; using Saxonite.Net.Core.Web.Base.Controllers; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; using Saxonite.Net.Standard.Base.Bus; using Sarand.Net.Standard.Base.App; using Sarand.Net.Standard.Base.Bus; using Sarand.Net.Standard.Base.Utils; using Saxonite.Net.Standard.Base.App; using Microsoft.AspNetCore.Http; using Sarand.Net.Standard.Base.Helpers; using System.IO; using System.Drawing; using Sarand.Net.Standard.Base.Core; using Saxonite.Net.Standard.Base.Data; using static Saxonite.Net.Standard.Base.App.Sax.Config; namespace Saxonite.Net.Core.Web.Online.Controllers { //======================================================================= // Online Account Controller - Contact - Ajax actions. //======================================================================= partial class AccountController { //------------------------------------------------------------------- // Common response data. private class ContactResponseData { public String EmailAddress { get; set; } public String DisplayName { get; set; } public String Title { get; set; } public String FirstName { get; set; } public String LastName { get; set; } public String About { get; set; } public byte Gender { get; set; } public String GenderCaption { get; set; } public byte DobDay { get; set; } public byte DobMonth { get; set; } public short DobYear { get; set; } public bool? HasImage { get; set; } = null; // Regions. public String RegionIdCountry { get; set; } public String RegionCaptionCountry { get; set; } public String RegionIdProvince { get; set; } public String RegionCaptionProvince { get; set; } public String LocationCaption { get; set; } } private void PopulateContactResponseData(SaxModel_Online model, ContactResponseData data, SaxUserAccount userAccount) { data.EmailAddress = userAccount.EmailAddress; data.DisplayName = userAccount.GetDisplayName(); data.Title = userAccount.Title; data.FirstName = userAccount.FirstName; data.LastName = userAccount.LastName; data.About = userAccount.About; data.Gender = (byte)userAccount.Gender; data.GenderCaption = Localiser.GetGenderCaption(userAccount.Gender); data.DobDay = (byte)userAccount.DateOfBirthDay; data.DobMonth = (byte)userAccount.DateOfBirthMonth; data.DobYear = (short)userAccount.DateOfBirthYear; if (ItemDataMgr.ItemHasActiveImage(userAccount.GetItemId(), Sax.ImageType.Avatar)) data.HasImage = true; // Regions. data.RegionIdCountry = model.GetEncryptedRegionId(userAccount.RegionIdCountry); data.RegionIdProvince = model.GetEncryptedRegionId(userAccount.RegionIdProvince); data.RegionCaptionCountry = Localiser.GetRegionName(Internals.GetRegion(userAccount.RegionIdCountry)); data.RegionCaptionProvince = Localiser.GetRegionName(Internals.GetRegion(userAccount.RegionIdProvince)); List locations = new List() { data.RegionCaptionProvince, data.RegionCaptionCountry }; data.LocationCaption = locations.Concat(", ", "", "", true, true); } //------------------------------------------------------------------- // Utilites. private ItemId GetUserItemId(SysId userAccountId) { return new ItemId((short)Sax.DataItemType.UserAccount, userAccountId); } //------------------------------------------------------------------- // Account - Get Contact Details. //------------------------------------------------------------------- private class AjaxResponse_AccountContactData : AjaxResponse_Data { // Construction. public AjaxResponse_AccountContactData(SaxModel_Online model) : base(model) { } // Contact details. public ContactResponseData Contact { get; set; } // Reference data. public List Genders { get; } = new List(); public List Months { get; } = new List(); public List ImageContentTypes { get; set; } } // Ajax request. [HttpPost] [Route("Account/Ajax/GetProfileContact")] public IActionResult GetProfileContact(Model_Account model) { String fn = GetType().Name + ".GetProfileContact"; AjaxResponse_AccountContactData responseData = new AjaxResponse_AccountContactData(model); String messageTitle = Localiser["account.caption.profile.contact"]; // 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()) { // 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(SessionUserAccountId); disposableItems.Add(userAccount); if (userAccount == null) { SaLogger.Warn(fn + " Failed to retrieve user account for Id: " + SysId.ToString(SessionUserAccountId)); 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); } 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_UploadContactImage : AjaxResponse_Data { // Construction. public AjaxResponse_UploadContactImage(SaxModel_Online model) : base(model) { } // Upload message. public String UploadMsg { get; set; } } // Ajax request. [HttpPost] [Route("Account/Ajax/UploadContactImage")] public IActionResult UploadContactImage(Model_Account model) { String fn = GetType().Name + ".UploadContactImage"; AjaxResponse_UploadContactImage responseData = new AjaxResponse_UploadContactImage(model); String messageTitle = Localiser["account.caption.profile.contact"]; // 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()) { // Validate number of images. if (model.AccountContactImage.IsEmpty()) { 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.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(SessionUserAccountId); // 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); } //------------------------------------------------------------------- // Account - Store Contact Details. //------------------------------------------------------------------- private class AjaxResponse_StoreAccountContactDetails : AjaxResponse_Data { // Construction. public AjaxResponse_StoreAccountContactDetails(SaxModel_Online model) : base(model) { } // Contact details. public ContactResponseData Contact { get; set; } } // Ajax request. [HttpPost] [Route("Account/Ajax/StoreProfileContact")] public IActionResult StoreProfileContact(Model_Account model) { String fn = GetType().Name + ".StoreProfileContact"; AjaxResponse_StoreAccountContactDetails responseData = new AjaxResponse_StoreAccountContactDetails(model); String messageTitle = Localiser["account.caption.profile.contact"]; // 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()) { // Validate data. model.ValidateContactDetails(responseData); if (responseData.HasControlErrors()) return Json(responseData); // Retrieve user details. SaxUserAccount userAccount = AccountMgr.GetUserAccountById(SessionUserAccountId); disposableItems.Add(userAccount); if (userAccount == null) { SaLogger.Warn(fn + " Failed to retrieve user account for Id: " + SysId.ToString(SessionUserAccountId)); 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"])); } // 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) { if (model.AccountDobDay != null) userAccount.DateOfBirthDay = (byte)StringUtil.ToShort(model.AccountDobDay); } 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.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); } //------------------------------------------------------------------- } //======================================================================= }