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()); } var userId = _accessor.HttpContext.GetUserId(); var results = new List(); // Always include the user's own applications (needed for Apply.razor) if (userId != -1) { var ownApps = _repository.GetEngineerApplications(ModuleId) .Where(a => a.UserId == userId).ToList(); results.AddRange(ownApps); } // Check if Premium - also show approved/published apps from others if (IsUserPremium(user)) { var approved = _repository.GetEngineerApplications(ModuleId, "Approved"); var published = _repository.GetEngineerApplications(ModuleId, "Published"); results.AddRange(approved.Union(published)); // Remove duplicates (own apps might already be in approved/published) results = results.GroupBy(a => a.ApplicationId).Select(g => g.First()).ToList(); } return Task.FromResult(results); } 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); bool isOwner = (userId != -1 && existing.UserId == userId); if (isAdmin || isOwner) { if (!isAdmin) { // Owner can update their own application // Accept "Published" status from client (auto-publish without admin approval) if (Application.Status == "Published") { Application.SubmittedOn ??= DateTime.UtcNow; Application.ApprovedOn ??= DateTime.UtcNow; } else { // Keep existing status if client didn't explicitly set to Published Application.Status = existing.Status; } } Application = _repository.UpdateEngineerApplication(Application); _logger.Log(LogLevel.Information, this, LogFunction.Update, "Application Updated {Application}", Application); return Task.FromResult(Application); } _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Update Attempt. UserId: {UserId}, AppOwner: {OwnerId}, IsAdmin: {IsAdmin}", userId, existing.UserId, isAdmin); 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) { if (!user.Identity.IsAuthenticated) return false; // Check 1: Oqtane role "Premium Member" (matches the UI-level check in ApplicationList.razor) if (user.IsInRole("Premium Member")) return true; // Check 2: Custom UserPremium DB table (for premium granted via GrantPremium/payment) int userId = -1; 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; } } }