using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.WebUtilities; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using MTWorkHR.Application.Identity; using MTWorkHR.Application.Mapper; using MTWorkHR.Application.Models; using MTWorkHR.Core.Global; using MTWorkHR.Core.IRepositories; using MTWorkHR.Core.UnitOfWork; using MTWorkHR.Application.Services.Interfaces; using MTWorkHR.Core.Email; using MTWorkHR.Core.Entities; using MTWorkHR.Infrastructure.UnitOfWorks; using MTWorkHR.Infrastructure.Entities; using static Org.BouncyCastle.Crypto.Engines.SM2Engine; using System.Web; using System.Data; using MTWorkHR.Core.IDto; using System.Linq.Dynamic.Core; using MTWorkHR.Core.Entities.Base; using MTWorkHR.Infrastructure.EmailService; using Countries.NET.Database; using Microsoft.AspNetCore.Http; using System.Collections; using System.Linq; namespace MTWorkHR.Application.Services { public class UserService : IUserService { private readonly RoleManager _roleManager; private readonly ApplicationUserManager _userManager; private readonly IUnitOfWork _unitOfWork; private readonly IUserRoleRepository> _userRole; private readonly AppSettingsConfiguration _configuration; private readonly IMailSender _emailSender; private readonly GlobalInfo _globalInfo; private readonly IFileService _fileService; private readonly IOTPService _oTPService; public UserService(ApplicationUserManager userManager, IUnitOfWork unitOfWork , RoleManager roleManager, GlobalInfo globalInfo, AppSettingsConfiguration configuration, IMailSender emailSender , IUserRoleRepository> userRole, IFileService fileService, IOTPService oTPService) { _userManager = userManager; _unitOfWork = unitOfWork; _roleManager = roleManager; _userRole = userRole; _configuration = configuration; _emailSender = emailSender; _globalInfo = globalInfo; _fileService = fileService; _oTPService = oTPService; } public async Task GetById() { return await GetById(_globalInfo.UserId); } public async Task GetById(string id) { var entity = await _userManager.Users .Include(x => x.UserRoles) .Include(x => x.UserAddress).ThenInclude(x=> x.City) .Include(x => x.UserAddress).ThenInclude(x=> x.Country) .Include(x => x.UserAttachments) .Include(x => x.JobTitle) .Include(x => x.Industry) .Include(x => x.University) .Include(x => x.Country) .Include(x => x.Qualification) .FirstOrDefaultAsync(x => x.Id == id); var response = MapperObject.Mapper.Map(entity); if (response.UserAttachments != null) foreach (var attach in response.UserAttachments.Where(a => a.Content != null)) { //var stream = new MemoryStream(attach.Content); //IFormFile file = new FormFile(stream, 0, stream.Length, Path.GetFileNameWithoutExtension(attach.FileName), attach.FileName); using (var stream = new MemoryStream(attach.Content)) { var file = new FormFile(stream, 0, stream.Length, Path.GetFileNameWithoutExtension(attach.FileName), attach.FileName) { Headers = new HeaderDictionary(), ContentType = attach.ContentType, }; System.Net.Mime.ContentDisposition cd = new System.Net.Mime.ContentDisposition { FileName = file.FileName }; file.ContentDisposition = cd.ToString(); switch (attach.AttachmentTypeId) { case 1: response.CVAttach = file; break; case 2: response.PassportAttach = file; break; case 3: response.EduCertificateAttach = file; break; case 4: response.ExperienceCertificateAttach= file; break; case 5: response.ProfCertificateAttach = file; break; case 6: response.CommercialRegAttach = file; break; case 7: response.TaxDeclarationAttach = file; break; case 8: response.IdAttach = file; break; case 9: response.ProfileImage = file; break; } attach.Content = new byte[0]; } } var attendance = await _unitOfWork.Attendance.GetAttendanceByUserId(id, DateTime.Now.Date); response.IsCheckedIn = attendance != null && attendance.CheckInTime.HasValue; response.IsCheckedOut = attendance != null && attendance.CheckOutTime.HasValue; return response; } public async Task GetUserById(string id) { var entity = await _userManager.Users .FirstOrDefaultAsync(x => x.Id == id); var response = MapperObject.Mapper.Map(entity); return response; } public async Task GetUserFullName(string userId) { var entity = await GetUserById(userId); var name = entity == null ? "" : entity.FirstName + " " + entity.LastName; return name; } public async Task GetUserWithAttachmentById(string id) { var entity = await _userManager.Users.Include(u=> u.UserAttachments) .FirstOrDefaultAsync(x => x.Id == id); var response = MapperObject.Mapper.Map(entity); return response; } public async Task GetProfileImage(string userId) { string imagePath = null; var user = await _userManager.Users.Include(u => u.UserAttachments) .FirstOrDefaultAsync(x => x.Id == userId); if (user != null) { var imageAtt = user.UserAttachments?.FirstOrDefault(a => a.AttachmentTypeId == 9); imagePath = imageAtt != null ? imageAtt.FilePath : ""; } return imagePath; } //public async Task> GetAll(PagingInputDto pagingInput) //{ // var employees = await _userManager.GetUsersInRoleAsync("Employee"); // return employees.Select(e => new UserDto // { // Email = e.Email, // FirstName = e.FirstName, // LastName = e.LastName, // Id = e.Id // }).ToList(); //} public virtual async Task> GetAll(UserPagingInputDto PagingInputDto) { var companies = await _unitOfWork.Company.GetAllAsync(); var query = _userManager.Users .Include(u => u.Qualification) .Include(u => u.JobTitle) .Include(u => u.University) .Include(u => u.Industry) .Include(u => u.Country) .Where(u => _globalInfo.CompanyId == null || u.CompanyId != _globalInfo.CompanyId); var filter = PagingInputDto.Filter?.Trim(); // Filtering by text fields and handling full name search if (!string.IsNullOrEmpty(filter)) { var filters = filter.Split(" "); var firstNameFilter = filters[0]; var lastNameFilter = filters.Length > 1 ? filters[1] : ""; query = query.Where(u => u.UserName.Contains(filter) || u.Email.Contains(filter) || u.FavoriteName.Contains(filter) || u.Position.Contains(filter) || u.PhoneNumber.Contains(filter) || u.FirstName.Contains(firstNameFilter) && (string.IsNullOrEmpty(lastNameFilter) || u.LastName.Contains(lastNameFilter)) || u.LastName.Contains(lastNameFilter) && (string.IsNullOrEmpty(firstNameFilter) || u.FirstName.Contains(firstNameFilter)) ); } // Applying additional filters if (PagingInputDto.IndustryId?.Count > 0) query = query.Where(u => u.IndustryId.HasValue && PagingInputDto.IndustryId.Contains(u.IndustryId.Value)); if (PagingInputDto.QualificationId.HasValue) query = query.Where(u => u.QualificationId == PagingInputDto.QualificationId); if (PagingInputDto.JobTitleId.HasValue) query = query.Where(u => u.JobTitleId == PagingInputDto.JobTitleId); if (PagingInputDto.UniversityId.HasValue) query = query.Where(u => u.UniversityId == PagingInputDto.UniversityId); if (PagingInputDto.CountryId?.Count > 0) query = query.Where(u => u.CountryId.HasValue && PagingInputDto.CountryId.Contains(u.CountryId.Value)); if (PagingInputDto.UserTypeId?.Count > 0) query = query.Where(u => PagingInputDto.UserTypeId.Contains(u.UserType)); if (PagingInputDto.Employed.HasValue) query = query.Where(u => PagingInputDto.Employed.Value ? u.CompanyId != null : u.CompanyId == null); // Ordering by specified field and direction var orderByField = !string.IsNullOrEmpty(PagingInputDto.OrderByField) ? PagingInputDto.OrderByField : "UserName"; var orderByDirection = PagingInputDto.OrderType?.ToLower() == "desc" ? "desc" : "asc"; query = query.OrderBy($"{orderByField} {orderByDirection}"); // Pagination var total = await query.CountAsync(); var result = await query .Skip((PagingInputDto.PageNumber - 1) * PagingInputDto.PageSize) .Take(PagingInputDto.PageSize) .ToListAsync(); var list = MapperObject.Mapper.Map>(result); var ss = list .Join(companies.Item1, // Join the list with companies.Item1 u => u.CompanyId, // Key selector for User (CompanyId) c => c.Id, // Key selector for Company (Id) (u, c) => { // Project the join result u.CompanyName = c.CompanyName; // Assuming 'Name' is the CompanyName in Company entity return u; // Return the updated UserAllDto with CompanyName filled }) .ToList(); return new PagingResultDto { Result = list, Total = total }; } public async Task> GetAllEmployees() { var employees = await _userManager.GetUsersInRoleAsync("Employee"); return employees.Select(e => new UserDto { Email = e.Email, FirstName = e.FirstName, LastName = e.LastName, Id = e.Id }).ToList(); } public async Task> GetAllCompanyEmployees() { var employees = await _userManager.GetUsersInRoleAsync("Employee"); var res = employees.Where(e => e.CompanyId == _globalInfo.CompanyId).ToList(); var response = MapperObject.Mapper.Map>(res); return response; } public async Task Delete(string id) { var user = await _userManager.FindByIdAsync(id); if (user != null) { user.IsDeleted = true; await _userManager.UpdateAsync(user); } } public async Task Create(UserDto input) { var emailExists = await _userManager.FindByEmailAsync(input.Email); if (emailExists != null) throw new AppException(ExceptionEnum.RecordEmailAlreadyExist); var phoneExists = await _userManager.FindByAnyAsync(input.PhoneNumber); if (phoneExists != null) throw new AppException(ExceptionEnum.RecordPhoneAlreadyExist); var userExists = await _userManager.FindByAnyAsync(input.UserName); if (userExists != null) throw new AppException(ExceptionEnum.RecordNameAlreadyExist); //loop for given list of attachment, and move each file from Temp path to Actual path // _fileService.UploadFiles(files); if (input.UserAttachments == null ) input.UserAttachments = new List(); if (input.ProfileImage != null) { input.UserAttachments.Add(new AttachmentDto { FileData = input.ProfileImage, OriginalName = input.ProfileImage?.Name, FileName = input.ProfileImage?.FileName, AttachmentTypeId = 9 }); } if (input.CVAttach != null) { input.UserAttachments.Add(new AttachmentDto { FileData = input.CVAttach, OriginalName = input.CVAttach?.Name,FileName = input.CVAttach?.FileName, AttachmentTypeId = 1 }); } if (input.PassportAttach != null) { input.UserAttachments.Add(new AttachmentDto { FileData = input.PassportAttach, OriginalName = input.PassportAttach?.Name, FileName = input.PassportAttach?.FileName, AttachmentTypeId = 2 }); } if (input.EduCertificateAttach != null) { input.UserAttachments.Add(new AttachmentDto { FileData = input.EduCertificateAttach, OriginalName = input.EduCertificateAttach?.Name, FileName = input.EduCertificateAttach?.FileName, AttachmentTypeId = 3 }); } if (input.ExperienceCertificateAttach != null) { input.UserAttachments.Add(new AttachmentDto { FileData = input.ExperienceCertificateAttach, OriginalName = input.ExperienceCertificateAttach?.Name, FileName = input.ExperienceCertificateAttach?.FileName, AttachmentTypeId = 4 }); } if (input.ProfCertificateAttach != null) { input.UserAttachments.Add(new AttachmentDto { FileData = input.ProfCertificateAttach, OriginalName = input.ProfCertificateAttach?.Name, FileName = input.ProfCertificateAttach?.FileName, AttachmentTypeId = 5 }); } var files = input.UserAttachments.Select(a=> a.FileData).ToList(); List attachs = input.UserAttachments.ToList(); _fileService.CopyFileToCloud(ref attachs); //if (!res) // throw new AppException(ExceptionEnum.CouldNotMoveFiles); input.UserAttachments = attachs; var user = MapperObject.Mapper.Map(input); if(user.UserType == 0) { user.UserType = (int)UserTypeEnum.Employee;//default if not selected } _unitOfWork.BeginTran(); user.CreateDate = DateTime.Now; //saving user var result = await _userManager.CreateAsync(user, input.Password); if (!result.Succeeded) { if(result.Errors != null && result.Errors.Count() > 0) { var msg = result.Errors.Select(a => a.Description ).Aggregate((a,b) => a + " /r/n " + b); throw new AppException(msg); } throw new AppException(ExceptionEnum.RecordCreationFailed); } input.Id = user.Id; //saving userRoles if(input.UserRoles == null || input.UserRoles.Count == 0) { if(user.UserType == (int)UserTypeEnum.Employee) { var employeeRole = await _roleManager.FindByNameAsync("Employee"); if (employeeRole != null) { await _userManager.AddToRoleAsync(user, "Employee"); } } else if (user.UserType == (int)UserTypeEnum.Contractor) { var employeeRole = await _roleManager.FindByNameAsync("Contractor"); if (employeeRole != null) { await _userManager.AddToRoleAsync(user, "Contractor"); } } } else { var userRoles = MapperObject.Mapper.Map>>(input.UserRoles); foreach (var role in userRoles) { role.UserId = user.Id; if (await _roleManager.FindByIdAsync(role.RoleId) == null) throw new AppException(ExceptionEnum.RecordNotExist); var roleOb = input.UserRoles?.FirstOrDefault(r => r.RoleId == role.RoleId); var roleName = roleOb != null ? roleOb.RoleName : "Employee"; await _userManager.AddToRoleAsync(user, roleName); } } // await _userRole.AddRangeAsync(userRoles); await _unitOfWork.CompleteAsync(); _unitOfWork.CommitTran(); try { var resultPassReset = await GetConfirmEmailURL(user.Id); var sendMailResult = await _emailSender.SendEmail(new EmailMessage { Subject = "Register Confirmation", To = input.Email, Body = "Please Set Your Password (this link will expired after 24 hours)" , url = resultPassReset.Item1, userId = user.Id }); if (!sendMailResult) { throw new AppException("User created, but could not send the email!"); } } catch { throw new AppException("User created, but could not send the email!"); } return input; } public async Task Download(string filePath) { var file = await _fileService.Download(filePath); return file; } public async Task ConfirmEmail(ConfirmEmailDto input) { var user = await _userManager.FindByIdAsync(input.UserId); if (user == null) throw new AppException(ExceptionEnum.UserNotExist); var result = await _userManager.ConfirmEmailAsync(user, input.Token); return result.Succeeded; } private async Task> GetResetPasswordURL(string userId) { var user = await _userManager.Users.FirstOrDefaultAsync(x => !x.IsDeleted && x.Id.Equals(userId)); if (user == null) throw new AppException(ExceptionEnum.UserNotExist); string code = await _userManager.GeneratePasswordResetTokenAsync(user); var route = "auth/ConfirmEmail"; var origin = _configuration.JwtSettings.Audience; var endpointUri = new Uri(string.Concat($"{origin}/", route)); var userURL = QueryHelpers.AddQueryString(endpointUri.ToString(), "userId", user.Id); var passwordResetURL = QueryHelpers.AddQueryString(userURL.ToString(), "token", code); return new Tuple(passwordResetURL, user.Email); } private async Task> GetConfirmEmailURL(string userId) { var user = await _userManager.Users.FirstOrDefaultAsync(x => !x.IsDeleted && x.Id.Equals(userId)); if (user == null) throw new AppException(ExceptionEnum.UserNotExist); string token = await _userManager.GenerateEmailConfirmationTokenAsync(user); string codeHtmlVersion = HttpUtility.UrlEncode(token); var route = "auth/ConfirmEmail"; var origin = _configuration.JwtSettings.Audience; var endpointUri = new Uri(string.Concat($"{origin}/", route)); var userURL = QueryHelpers.AddQueryString(endpointUri.ToString(), "userId", user.Id); var confirmEmailUrl = QueryHelpers.AddQueryString(userURL.ToString(), "token", codeHtmlVersion); return new Tuple(confirmEmailUrl, user.Email); } public async Task Update(UserUpdateDto input) { try { var entity = _userManager.Users.Include(x => x.UserAttachments).FirstOrDefault(x=> x.Id == input.Id); if (entity == null) throw new AppException(ExceptionEnum.UserNotExist); if (input.UserAttachments == null) input.UserAttachments = new List(); var oldAttachList = entity.UserAttachments; if (input.ProfileImage != null) { var oldAttach = oldAttachList.Where(x => x.AttachmentTypeId == 9 || x.OriginalName == input.ProfileImage?.Name).FirstOrDefault(); if(oldAttach != null) entity.UserAttachments.Remove(oldAttach); input.UserAttachments.Add(new AttachmentDto { FileData = input.ProfileImage, OriginalName = input.ProfileImage?.Name, FileName = input.ProfileImage?.FileName, AttachmentTypeId = 9 }); } if (input.CVAttach != null) { var oldAttach = oldAttachList.Where(x => x.AttachmentTypeId == 1 || x.OriginalName == input.CVAttach?.Name).FirstOrDefault(); if (oldAttach != null) entity.UserAttachments.Remove(oldAttach); input.UserAttachments.Add(new AttachmentDto { FileData = input.CVAttach, OriginalName = input.CVAttach?.Name, FileName = input.CVAttach?.FileName, AttachmentTypeId = 1 }); } if (input.PassportAttach != null) { var oldAttach = oldAttachList.Where(x => x.AttachmentTypeId == 2 || x.OriginalName == input.PassportAttach?.Name).FirstOrDefault(); if (oldAttach != null) entity.UserAttachments.Remove(oldAttach); input.UserAttachments.Add(new AttachmentDto { FileData = input.PassportAttach, OriginalName = input.PassportAttach?.Name, FileName = input.PassportAttach?.FileName, AttachmentTypeId = 2 }); } if (input.EduCertificateAttach != null) { var oldAttach = oldAttachList.Where(x => x.AttachmentTypeId == 3 || x.OriginalName == input.EduCertificateAttach?.Name).FirstOrDefault(); if (oldAttach != null) entity.UserAttachments.Remove(oldAttach); input.UserAttachments.Add(new AttachmentDto { FileData = input.EduCertificateAttach, OriginalName = input.EduCertificateAttach?.Name, FileName = input.EduCertificateAttach?.FileName, AttachmentTypeId = 3 }); } if (input.ExperienceCertificateAttach != null) { var oldAttach = oldAttachList.Where(x => x.AttachmentTypeId == 4 || x.OriginalName == input.ExperienceCertificateAttach?.Name).FirstOrDefault(); if (oldAttach != null) entity.UserAttachments.Remove(oldAttach); input.UserAttachments.Add(new AttachmentDto { FileData = input.ExperienceCertificateAttach, OriginalName = input.ExperienceCertificateAttach?.Name, FileName = input.ExperienceCertificateAttach?.FileName, AttachmentTypeId = 4 }); } if (input.ProfCertificateAttach != null) { var oldAttach = oldAttachList.Where(x => x.AttachmentTypeId == 5 || x.OriginalName == input.ProfCertificateAttach?.Name).FirstOrDefault(); if (oldAttach != null) entity.UserAttachments.Remove(oldAttach); input.UserAttachments.Add(new AttachmentDto { FileData = input.ProfCertificateAttach, OriginalName = input.ProfCertificateAttach?.Name, FileName = input.ProfCertificateAttach?.FileName, AttachmentTypeId = 5 }); } List attachs = input.UserAttachments.ToList(); _fileService.CopyFileToCloud(ref attachs); input.UserAttachments = attachs; //if (!await _fileService.CopyFileToActualFolder(input.UserAttachments.ToList())) // throw new AppException(ExceptionEnum.CouldNotMoveFiles); MapperObject.Mapper.Map(input, entity); _unitOfWork.BeginTran(); entity.UpdateDate = DateTime.Now; //saving user var result = await _userManager.UpdateAsync(entity); if (!result.Succeeded) throw new AppException(ExceptionEnum.RecordUpdateFailed); //**saving userRoles //add new user roles //var exsitedRolesIds = await _userRole.GetUserRoleIdsByUserID(input.Id); //if (input.UserRoles == null) // input.UserRoles = new List(); //var newAddedRoles = MapperObject.Mapper.Map>>(input.UserRoles.Where(x => !exsitedRolesIds.Contains(x.RoleId))); //newAddedRoles.ForEach(x => x.UserId = input.Id); //await _userRole.AddRangeAsync(newAddedRoles); ////delete removed roles //var rolesIds = input.UserRoles.Select(x => x.RoleId).ToArray(); //var removedRoles = await _userRole.GetRemovedUserRoleIdsByUserID(input.Id, rolesIds); //await _userRole.DeleteAsync(removedRoles.AsEnumerable()); await _unitOfWork.CompleteAsync(); _unitOfWork.CommitTran(); } catch (Exception e) { throw e; } var userResponse = await GetById(input.Id); var user = MapperObject.Mapper.Map(userResponse); return user; } public async Task IsExpiredToken(ConfirmEmailDto input) { var user = await _userManager.Users.IgnoreQueryFilters().FirstOrDefaultAsync(x => x.Id == input.UserId); if (user == null) throw new AppException(ExceptionEnum.UserNotExist); var purpose = UserManager.ResetPasswordTokenPurpose; var result = await _userManager.VerifyUserTokenAsync(user, "Default", purpose, input.Token); return !result; } public async Task ResetPassword(ResetPasswordDto input) { var user = await _userManager.FindByIdAsync(_globalInfo.UserId); if (user == null) throw new AppException(ExceptionEnum.UserNotExist); if (!await _userManager.CheckPasswordAsync(user, input.OldPassword)) throw new AppException(ExceptionEnum.WrongCredentials); var token = await _userManager.GeneratePasswordResetTokenAsync(user); var result = await _userManager.ResetPasswordAsync(user, token, input.NewPassword); if (!result.Succeeded) throw new AppException(ExceptionEnum.RecordUpdateFailed); return true; } public async Task ForgetPasswordMail(string email) //Begin forget password { var foundUser = await _userManager.FindByEmailAsync(email); if (foundUser != null) { string oneTimePassword = await _oTPService.RandomOneTimePassword(foundUser.Id); await _oTPService.SentOTPByMail(foundUser.Id, foundUser.Email, oneTimePassword); ForgetPasswordResponseDto res = new ForgetPasswordResponseDto { UserId = foundUser.Id}; return res; } else { throw new AppException(ExceptionEnum.UserNotExist); } } public async Task VerifyOTP(VerifyOTPDto input) { if (! await _oTPService.VerifyOTP(input.UserId, input.OTP)) throw new AppException(ExceptionEnum.WrongOTP); return true; } public async Task ForgetPassword(ForgetPasswordDto input) { var user = await _userManager.Users.IgnoreQueryFilters().FirstOrDefaultAsync(x => x.Id == input.UserId); if (user == null) throw new AppException(ExceptionEnum.UserNotExist); string resetToken = await _userManager.GeneratePasswordResetTokenAsync(user); var result = await _userManager.ResetPasswordAsync(user, resetToken, input.Password); if (!result.Succeeded) { if (result.Errors != null && result.Errors.Count() > 0) { var msg = result.Errors.Select(a => a.Description).Aggregate((a, b) => a + " /r/n " + b); throw new AppException(msg); } throw new AppException(ExceptionEnum.RecordCreationFailed); } return result.Succeeded; } public async Task StopUser(string userId) { var entity = await _userManager.Users.FirstOrDefaultAsync(x => x.Id == userId); if (entity == null) throw new AppException(ExceptionEnum.UserNotExist); if (!entity.IsStopped) { entity.IsStopped = true; await _unitOfWork.CompleteAsync(); } } public async Task ActiveUser(string userId) { var entity = await _userManager.Users.FirstOrDefaultAsync(x => x.Id == userId); if (entity == null) throw new AppException(ExceptionEnum.UserNotExist); entity.IsStopped = false; entity.AccessFailedCount = 0; entity.LockoutEnabled = false; entity.LockoutEnd = null; await _unitOfWork.CompleteAsync(); } } }