Files
Saxonite/SaxoniteCoreWebOnline/Online/Controllers/AccountController-Ajax-Contact.cs
T
2024-08-20 17:51:32 +01:00

427 lines
18 KiB
C#

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 ?? 0);
data.DobMonth = (byte)(userAccount.DateOfBirthMonth ?? 0);
data.DobYear = (short)(userAccount.DateOfBirthYear ?? 0);
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<String> locations = new List<string>() { 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<CodeDesc> Genders { get; } = new List<CodeDesc>();
public List<CodeDesc> Months { get; } = new List<CodeDesc>();
public List<String> 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<SacsHierarchyItem> disposableItems = new DisposableCollection<SacsHierarchyItem>())
{
// 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<SacsHierarchyItem> disposableItems = new DisposableCollection<SacsHierarchyItem>())
{
// 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 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"]));
Sarand.Net.Standard.Base.Core.Sacs.MimeType mimeTypeExpected = SacsContentTypeHelper.GetMimeTypeFromContentType(file.ContentType);
// Validate the image.
ImageUtils.ImageConfig imageConfig = null;
if (mimeTypeExpected == Sarand.Net.Standard.Base.Core.Sacs.MimeType.Svg)
imageConfig = ImageUtils.VerifySvgImageData(imageData);
else
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"]));
if (imageConfig.MimeType != mimeTypeExpected)
{
SaLogger.Warn($"{SysUtil.Fn} Image upload - inconsistent Mime. Filename: {file.FileName}. Mime: {imageConfig.MimeType}. Expected Mime: {mimeTypeExpected}.");
return Json(responseData.SetControlError("OrganisationLogoImage", Localiser["msg.upload.image.invalid"]));
}
// 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<SacsHierarchyItem> disposableItems = new DisposableCollection<SacsHierarchyItem>())
{
// 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);
}
//-------------------------------------------------------------------
}
//=======================================================================
}