using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Oqtane.Enums; using Oqtane.Infrastructure; using Oqtane.Models; using Oqtane.Security; using Oqtane.Shared; using SZUAbsolventenverein.Module.PremiumArea.Models; using SZUAbsolventenverein.Module.PremiumArea.Repository; namespace SZUAbsolventenverein.Module.PremiumArea.Services { public class ServerEngineerApplicationService : IEngineerApplicationService { private readonly IEngineerApplicationRepository _repository; private readonly IPremiumService _premiumService; private readonly IUserPermissions _userPermissions; private readonly ILogManager _logger; private readonly IHttpContextAccessor _accessor; private readonly Alias _alias; public ServerEngineerApplicationService(IEngineerApplicationRepository repository, IPremiumService premiumService, IUserPermissions userPermissions, ITenantManager tenantManager, ILogManager logger, IHttpContextAccessor accessor) { _repository = repository; _premiumService = premiumService; _userPermissions = userPermissions; _logger = logger; _accessor = accessor; _alias = tenantManager.GetAlias(); } public Task> GetApplicationsAsync(int ModuleId) { var user = _accessor.HttpContext.User; if (_userPermissions.IsAuthorized(user, _alias.SiteId, EntityNames.Module, ModuleId, PermissionNames.Edit)) // Admin/Edit { return Task.FromResult(_repository.GetEngineerApplications(ModuleId).ToList()); } // Check if Premium if (IsUserPremium(user)) { // Return Approved AND Published // Repository GetEngineerApplications(ModuleId, status) usually filters by status. // If I want both, I might need 2 calls or update Repository. // Simple approach: Get All and filter here? No, repository likely filters. // Let's call twice and combine for now, or assume Repository supports "Published". var approved = _repository.GetEngineerApplications(ModuleId, "Approved"); var published = _repository.GetEngineerApplications(ModuleId, "Published"); return Task.FromResult(approved.Union(published).ToList()); } return Task.FromResult(new List()); } public Task> GetApplicationsAsync(int ModuleId, string status) { var user = _accessor.HttpContext.User; if (_userPermissions.IsAuthorized(user, _alias.SiteId, EntityNames.Module, ModuleId, PermissionNames.Edit)) { return Task.FromResult(_repository.GetEngineerApplications(ModuleId, status).ToList()); } if ((status == "Approved" || status == "Published") && IsUserPremium(user)) { return Task.FromResult(_repository.GetEngineerApplications(ModuleId, status).ToList()); } return Task.FromResult(new List()); } public Task GetApplicationAsync(int ApplicationId, int ModuleId) { var app = _repository.GetEngineerApplication(ApplicationId); if (app == null || app.ModuleId != ModuleId) return Task.FromResult(null); var user = _accessor.HttpContext.User; var userId = _accessor.HttpContext.GetUserId(); // Allow if Admin OR Owner OR (Premium AND (Approved OR Published)) bool isAdmin = _userPermissions.IsAuthorized(user, _alias.SiteId, EntityNames.Module, ModuleId, PermissionNames.Edit); bool isOwner = (userId != -1 && app.UserId == userId); bool isPremiumViewer = ((app.Status == "Approved" || app.Status == "Published") && IsUserPremium(user)); if (isAdmin || isOwner || isPremiumViewer) { return Task.FromResult(app); } return Task.FromResult(null); } public Task AddApplicationAsync(EngineerApplication Application) { var user = _accessor.HttpContext.User; var userId = _accessor.HttpContext.GetUserId(); if (userId == -1) // Not logged in { _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Add Attempt (Anonymous)"); return Task.FromResult(null); } // Check if allowed to view module (Registered Users usually can View) if (_userPermissions.IsAuthorized(user, _alias.SiteId, EntityNames.Module, Application.ModuleId, PermissionNames.View)) { Application.UserId = userId; // Auto-publish if file uploaded (Checked by Status=Published from client) // If client sends "Published", we accept it. if (string.IsNullOrEmpty(Application.Status)) Application.Status = "Draft"; // Set ApprovedOn if Published? user asked for removal of admin approval. if (Application.Status == "Published") { Application.SubmittedOn = DateTime.UtcNow; Application.ApprovedOn = DateTime.UtcNow; // Effectively approved. } Application = _repository.AddEngineerApplication(Application); _logger.Log(LogLevel.Information, this, LogFunction.Create, "Application Added {Application}", Application); return Task.FromResult(Application); } return Task.FromResult(null); } public Task UpdateApplicationAsync(EngineerApplication Application) { var existing = _repository.GetEngineerApplication(Application.ApplicationId); if (existing == null) return Task.FromResult(null); var user = _accessor.HttpContext.User; var userId = _accessor.HttpContext.GetUserId(); bool isAdmin = _userPermissions.IsAuthorized(user, _alias.SiteId, EntityNames.Module, Application.ModuleId, PermissionNames.Edit); if (isAdmin || (existing.UserId == userId && existing.Status == "Draft")) // Only owner can edit if Draft { if (!isAdmin) { Application.Status = existing.Status == "Draft" && Application.Status == "Submitted" ? "Published" : existing.Status; // Auto-publish on submit // If client sends "Published" (which Apply.razor does), apply it. if (Application.Status == "Published" && existing.Status == "Draft") { Application.SubmittedOn = DateTime.UtcNow; Application.ApprovedOn = DateTime.UtcNow; } } Application = _repository.UpdateEngineerApplication(Application); _logger.Log(LogLevel.Information, this, LogFunction.Update, "Application Updated {Application}", Application); return Task.FromResult(Application); } return Task.FromResult(null); } public Task DeleteApplicationAsync(int ApplicationId, int ModuleId) { var existing = _repository.GetEngineerApplication(ApplicationId); var user = _accessor.HttpContext.User; var userId = _accessor.HttpContext.GetUserId(); bool isAdmin = _userPermissions.IsAuthorized(user, _alias.SiteId, EntityNames.Module, ModuleId, PermissionNames.Edit); if (existing != null && (isAdmin || existing.UserId == userId)) { _repository.DeleteEngineerApplication(ApplicationId); _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Application Deleted {Id}", ApplicationId); } return Task.CompletedTask; } // Custom Methods not just CRUD public Task ApproveApplicationAsync(int ApplicationId, int ModuleId) { var user = _accessor.HttpContext.User; if (_userPermissions.IsAuthorized(user, _alias.SiteId, EntityNames.Module, ModuleId, PermissionNames.Edit)) { var app = _repository.GetEngineerApplication(ApplicationId); if (app != null) { app.Status = "Approved"; // Keep Approved status for Admin explicitly approving (locking) app.ApprovedOn = DateTime.UtcNow; // app.ReportReason = null; // Optional: keep history? // app.ReportCount = 0; _repository.UpdateEngineerApplication(app); // Grant Premium _premiumService.GrantPremium(app.UserId, 12, "engineer_application", $"AppId:{app.ApplicationId}"); _logger.Log(LogLevel.Information, this, LogFunction.Update, "Application Approved {Id}", ApplicationId); } } return Task.CompletedTask; } public Task RejectApplicationAsync(int ApplicationId, int ModuleId, string Reason) { var user = _accessor.HttpContext.User; if (_userPermissions.IsAuthorized(user, _alias.SiteId, EntityNames.Module, ModuleId, PermissionNames.Edit)) { var app = _repository.GetEngineerApplication(ApplicationId); if (app != null) { app.Status = "Rejected"; _repository.UpdateEngineerApplication(app); _logger.Log(LogLevel.Information, this, LogFunction.Update, "Application Rejected {Id}", ApplicationId); } } return Task.CompletedTask; } public Task ReportApplicationAsync(int ApplicationId, int ModuleId, string Reason) { // Allow any View authorized user to report? // Or only Premium users? // Users who can VIEW the application can report it. // Since we restrict View to Premium (or Owner/Admin), we check that. // First, get the application to check existence var app = _repository.GetEngineerApplication(ApplicationId); if (app == null || app.ModuleId != ModuleId) return Task.CompletedTask; var user = _accessor.HttpContext.User; // Check if user is allowed to View this app bool canView = false; // Admin/Edit if (_userPermissions.IsAuthorized(user, _alias.SiteId, EntityNames.Module, ModuleId, PermissionNames.Edit)) canView = true; // Premium else if (IsUserPremium(user) && (app.Status == "Approved" || app.Status == "Published")) canView = true; if (canView) { _repository.UpdateEngineerApplication(app); // Send Notification? _logger.Log(LogLevel.Information, this, LogFunction.Update, "Application Reported {Id}", ApplicationId); } return Task.CompletedTask; } private bool IsUserPremium(System.Security.Claims.ClaimsPrincipal user) { // Oqtane's GetUserId() extension returns -1 if not found. // We need to parse User object myself or use "User.Identity.Name" to find user if needed. // But _premiumService.IsPremium(userId) asks for Int. // I'll assume I can get UserId. // Since Oqtane is weird with Claims sometimes, I'll use the Helper Extension `GetUserId()`. // But wait, "GetUserId" is an extension on HttpContext or ClaimsPrincipal? // In Oqtane.Shared.UserSecurity class? Or Oqtane.Extensions? // Usually `_accessor.HttpContext.GlobalUserId()` ?? // Let's rely on standard DI/Context. // NOTE: IsUserPremium check requires querying the DB via PremiumService. // This could be expensive on every list call. Caching might be better but for now DB is fine. // To get UserId from ClaimsPrincipal: // var userId = int.Parse(user.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Value); // I will use `_accessor.HttpContext.GetUserId()` from `Oqtane.Infrastructure` or similar if available. // Since I am already using `_accessor`, I'll rely on Oqtane extensions being available. int userId = -1; // Trying standard Oqtane way which is usually: if (user.Identity.IsAuthenticated) { // Using Oqtane.Shared.PrincipalExtensions? // Let's just try to parse NameIdentifier if GetUserId() is not available in my context. // But wait, `ModuleControllerBase` has `User` property. // Here I am in a Service. // I will write a helper to safely get UserId. var claim = user.Claims.FirstOrDefault(item => item.Type == System.Security.Claims.ClaimTypes.NameIdentifier); if (claim != null) { int.TryParse(claim.Value, out userId); } } if (userId != -1) { return _premiumService.IsPremium(userId); } return false; } } // Quick helper for GetUserId if not present in Usings public static class ClaimsPrincipalExtensions { public static int GetUserId(this HttpContext context) { if (context?.User?.Identity?.IsAuthenticated == true) { var claim = context.User.Claims.FirstOrDefault(item => item.Type == System.Security.Claims.ClaimTypes.NameIdentifier); if (claim != null && int.TryParse(claim.Value, out int userId)) { return userId; } } return -1; } } }