DB Migrtation geändert und PDF upload funktioniert
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
@@ -33,7 +34,9 @@ namespace SZUAbsolventenverein.Module.PremiumArea.Controllers
|
||||
private readonly IHttpContextAccessor _accessor;
|
||||
private readonly IWebHostEnvironment _environment;
|
||||
|
||||
public EngineerApplicationController(IEngineerApplicationService service, IFileRepository files, IFolderRepository folders, IUserManager users, IUserPremiumRepository premiums, ILogManager logger, IHttpContextAccessor accessor, IWebHostEnvironment environment) : base(logger, accessor)
|
||||
public EngineerApplicationController(IEngineerApplicationService service, IFileRepository files,
|
||||
IFolderRepository folders, IUserManager users, IUserPremiumRepository premiums, ILogManager logger,
|
||||
IHttpContextAccessor accessor, IWebHostEnvironment environment) : base(logger, accessor)
|
||||
{
|
||||
_service = service;
|
||||
_files = files;
|
||||
@@ -56,7 +59,8 @@ namespace SZUAbsolventenverein.Module.PremiumArea.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized EngineerApplication Get Attempt {ModuleId}", moduleid);
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security,
|
||||
"Unauthorized EngineerApplication Get Attempt {ModuleId}", moduleid);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
return null;
|
||||
}
|
||||
@@ -72,9 +76,10 @@ namespace SZUAbsolventenverein.Module.PremiumArea.Controllers
|
||||
{
|
||||
return await _service.GetApplicationsAsync(ModuleId, status);
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized EngineerApplication GetByStatus Attempt {ModuleId}", moduleid);
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security,
|
||||
"Unauthorized EngineerApplication GetByStatus Attempt {ModuleId}", moduleid);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
return null;
|
||||
}
|
||||
@@ -85,17 +90,18 @@ namespace SZUAbsolventenverein.Module.PremiumArea.Controllers
|
||||
[Authorize(Policy = PolicyNames.ViewModule)]
|
||||
public async Task<EngineerApplication> Get(int id, string moduleid)
|
||||
{
|
||||
int ModuleId;
|
||||
if (int.TryParse(moduleid, out ModuleId) && IsAuthorizedEntityId(EntityNames.Module, ModuleId))
|
||||
{
|
||||
return await _service.GetApplicationAsync(id, ModuleId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized EngineerApplication Get Attempt {Id} {ModuleId}", id, moduleid);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
return null;
|
||||
}
|
||||
int ModuleId;
|
||||
if (int.TryParse(moduleid, out ModuleId) && IsAuthorizedEntityId(EntityNames.Module, ModuleId))
|
||||
{
|
||||
return await _service.GetApplicationAsync(id, ModuleId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security,
|
||||
"Unauthorized EngineerApplication Get Attempt {Id} {ModuleId}", id, moduleid);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// POST api/<controller>
|
||||
@@ -105,11 +111,44 @@ namespace SZUAbsolventenverein.Module.PremiumArea.Controllers
|
||||
{
|
||||
if (ModelState.IsValid && IsAuthorizedEntityId(EntityNames.Module, Application.ModuleId))
|
||||
{
|
||||
return await _service.AddApplicationAsync(Application);
|
||||
try
|
||||
{
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Create,
|
||||
"DEBUG: Attempting to save application. UserId: {UserId}, ModuleId: {ModuleId}, FileId: {FileId}",
|
||||
Application.UserId, Application.ModuleId, Application.FileId);
|
||||
|
||||
// Manual validation before EF Core sees it
|
||||
if (Application.UserId == 0)
|
||||
_logger.Log(LogLevel.Warning, this, LogFunction.Create, "DEBUG: UserId is 0!");
|
||||
if (Application.FileId == null || Application.FileId == 0)
|
||||
_logger.Log(LogLevel.Warning, this, LogFunction.Create, "DEBUG: FileId is null or 0!");
|
||||
|
||||
var result = await _service.AddApplicationAsync(Application);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "DEBUG: Save successful!");
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var innerMessage = ex.InnerException?.Message ?? "No inner exception";
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Create, ex,
|
||||
"CRITICAL DB ERROR: {Message}. Inner: {Inner}", ex.Message, innerMessage);
|
||||
|
||||
// Force output to console so the user sees it immediately
|
||||
Console.WriteLine("========================================");
|
||||
Console.WriteLine($"!!! DATABASE INSERT FAILED !!!");
|
||||
Console.WriteLine($"Error: {ex.Message}");
|
||||
Console.WriteLine($"Inner: {innerMessage}");
|
||||
Console.WriteLine($"Stack: {ex.StackTrace}");
|
||||
Console.WriteLine("========================================");
|
||||
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized EngineerApplication Post Attempt {Application}", Application);
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security,
|
||||
"Unauthorized EngineerApplication Post Attempt {Application}", Application);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
return null;
|
||||
}
|
||||
@@ -120,13 +159,26 @@ namespace SZUAbsolventenverein.Module.PremiumArea.Controllers
|
||||
[Authorize(Policy = PolicyNames.ViewModule)] // Users can Edit own (Service checks ownership)
|
||||
public async Task<EngineerApplication> Put(int id, [FromBody] EngineerApplication Application)
|
||||
{
|
||||
if (ModelState.IsValid && Application.ApplicationId == id && IsAuthorizedEntityId(EntityNames.Module, Application.ModuleId))
|
||||
if (ModelState.IsValid && Application.ApplicationId == id &&
|
||||
IsAuthorizedEntityId(EntityNames.Module, Application.ModuleId))
|
||||
{
|
||||
return await _service.UpdateApplicationAsync(Application);
|
||||
try
|
||||
{
|
||||
return await _service.UpdateApplicationAsync(Application);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Update, ex,
|
||||
"Error updating application: {Message}. Inner: {Inner}", ex.Message,
|
||||
ex.InnerException?.Message);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized EngineerApplication Put Attempt {Application}", Application);
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security,
|
||||
"Unauthorized EngineerApplication Put Attempt {Application}", Application);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
return null;
|
||||
}
|
||||
@@ -144,12 +196,12 @@ namespace SZUAbsolventenverein.Module.PremiumArea.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized EngineerApplication Delete Attempt {Id} {ModuleId}", id, moduleid);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security,
|
||||
"Unauthorized EngineerApplication Delete Attempt {Id} {ModuleId}", id, moduleid);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
}
|
||||
}
|
||||
|
||||
// POST api/<controller>/approve/5
|
||||
[HttpPost("approve/{id}")]
|
||||
[Authorize(Policy = PolicyNames.EditModule)]
|
||||
public async Task Approve(int id, string moduleid)
|
||||
@@ -157,16 +209,57 @@ namespace SZUAbsolventenverein.Module.PremiumArea.Controllers
|
||||
int ModuleId;
|
||||
if (int.TryParse(moduleid, out ModuleId) && IsAuthorizedEntityId(EntityNames.Module, ModuleId))
|
||||
{
|
||||
// We need to clear IsReported flag as well.
|
||||
// Since the Service handles the logic, we should probably update it there.
|
||||
// But if I can't find it easily, I can do it here if I get the app first.
|
||||
// _service.ApproveApplicationAsync might just set Status="Approved".
|
||||
// I should verify where the service logic is.
|
||||
await _service.ApproveApplicationAsync(id, ModuleId);
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("log")]
|
||||
[AllowAnonymous]
|
||||
public IActionResult GetLog()
|
||||
{
|
||||
try
|
||||
{
|
||||
var repo = (EngineerApplicationRepository)_service.GetType().GetField("_repository",
|
||||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
|
||||
.GetValue(_service);
|
||||
var factory = (IDbContextFactory<PremiumAreaContext>)repo.GetType().GetField("_factory",
|
||||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(repo);
|
||||
using var db = factory.CreateDbContext();
|
||||
|
||||
var connection = db.Database.GetDbConnection();
|
||||
connection.Open();
|
||||
using var command = connection.CreateCommand();
|
||||
command.CommandText = "PRAGMA table_info(SZUAbsolventenvereinEngineerApplications);";
|
||||
var columns = new List<string>();
|
||||
using var reader = command.ExecuteReader();
|
||||
while (reader.Read())
|
||||
{
|
||||
columns.Add($"{reader["name"]} ({reader["type"]})");
|
||||
}
|
||||
|
||||
if (columns.Count == 0)
|
||||
{
|
||||
// Maybe it's rewritten?
|
||||
command.CommandText =
|
||||
"SELECT name FROM sqlite_master WHERE type='table' AND name LIKE '%Engineer%';";
|
||||
using var reader2 = command.ExecuteReader();
|
||||
while (reader2.Read())
|
||||
{
|
||||
columns.Add($"Found table: {reader2["name"]}");
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(columns);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Create, ex,
|
||||
"Error getting columns: {Message}. Inner: {Inner}", ex.Message, ex.InnerException?.Message);
|
||||
return Ok($"Error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,63 +270,95 @@ namespace SZUAbsolventenverein.Module.PremiumArea.Controllers
|
||||
int ModuleId;
|
||||
if (int.TryParse(moduleid, out ModuleId) && IsAuthorizedEntityId(EntityNames.Module, ModuleId))
|
||||
{
|
||||
if (Request.Form.Files.Count == 0) return BadRequest("No file uploaded");
|
||||
|
||||
var file = Request.Form.Files[0];
|
||||
if (file.ContentType != "application/pdf") return BadRequest("Only PDF files are allowed");
|
||||
|
||||
var alias = _accessor.HttpContext.Items["Alias"] as Alias;
|
||||
var siteId = alias.SiteId;
|
||||
var folderPath = "EngineerApplications";
|
||||
var folder = _folders.GetFolder(siteId, folderPath);
|
||||
|
||||
if (folder == null)
|
||||
try
|
||||
{
|
||||
// Create folder
|
||||
folder = new Folder
|
||||
if (Request.Form.Files.Count == 0) return BadRequest("No file uploaded");
|
||||
|
||||
var file = Request.Form.Files[0];
|
||||
if (file.ContentType != "application/pdf") return BadRequest("Only PDF files are allowed");
|
||||
|
||||
var alias = _accessor.HttpContext.Items["Alias"] as Alias; // Retained original alias retrieval
|
||||
var siteId = alias.SiteId;
|
||||
var folderPath = "EngineerApplications";
|
||||
|
||||
var folder = _folders.GetFolder(siteId, folderPath);
|
||||
|
||||
if (folder == null)
|
||||
{
|
||||
SiteId = siteId,
|
||||
ParentId = null,
|
||||
Name = "EngineerApplications",
|
||||
Path = folderPath,
|
||||
PermissionList = new List<Permission>
|
||||
try
|
||||
{
|
||||
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
||||
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
||||
// Create folder
|
||||
folder = new Folder
|
||||
{
|
||||
SiteId = siteId,
|
||||
ParentId = null,
|
||||
Name = "EngineerApplications",
|
||||
Path = folderPath,
|
||||
PermissionList = new List<Permission>
|
||||
{
|
||||
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
||||
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
||||
}
|
||||
};
|
||||
folder = _folders.AddFolder(folder);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Create, ex,
|
||||
"Error creating folder: {Message}", ex.Message);
|
||||
return BadRequest($"Folder creation failed: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
var ext = Path.GetExtension(file.FileName).ToLower();
|
||||
if (ext != ".pdf") return BadRequest("Invalid file extension");
|
||||
|
||||
var tenantId = alias.TenantId;
|
||||
var uploadPath = Path.Combine(_environment.ContentRootPath, "Content", "Tenants",
|
||||
tenantId.ToString(), "Sites", siteId.ToString(), folderPath);
|
||||
if (!Directory.Exists(uploadPath)) Directory.CreateDirectory(uploadPath);
|
||||
|
||||
var uniqueName = $"{Guid.NewGuid()}{ext}";
|
||||
var filePath = Path.Combine(uploadPath, uniqueName);
|
||||
|
||||
using (var stream = new FileStream(filePath, FileMode.Create))
|
||||
{
|
||||
await file.CopyToAsync(stream);
|
||||
}
|
||||
|
||||
var fileObj = new Oqtane.Models.File
|
||||
{
|
||||
FolderId = folder.FolderId,
|
||||
Name = uniqueName,
|
||||
Extension = ext.Substring(1),
|
||||
Size = (int)file.Length,
|
||||
ImageHeight = 0,
|
||||
ImageWidth = 0
|
||||
};
|
||||
folder = _folders.AddFolder(folder);
|
||||
}
|
||||
|
||||
var ext = Path.GetExtension(file.FileName).ToLower();
|
||||
if (ext != ".pdf") return BadRequest("Invalid file extension");
|
||||
|
||||
var tenantId = alias.TenantId;
|
||||
var uploadPath = Path.Combine(_environment.ContentRootPath, "Content", "Tenants", tenantId.ToString(), "Sites", siteId.ToString(), folderPath);
|
||||
if (!Directory.Exists(uploadPath)) Directory.CreateDirectory(uploadPath);
|
||||
|
||||
var uniqueName = $"{Guid.NewGuid()}{ext}";
|
||||
var filePath = Path.Combine(uploadPath, uniqueName);
|
||||
|
||||
using (var stream = new FileStream(filePath, FileMode.Create))
|
||||
{
|
||||
await file.CopyToAsync(stream);
|
||||
try
|
||||
{
|
||||
var addedFile = _files.AddFile(fileObj);
|
||||
return Ok(new { FileId = addedFile.FileId, FileName = file.FileName });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Create, ex,
|
||||
"Error saving file record to DB: {Message}. Inner: {Inner}", ex.Message,
|
||||
ex.InnerException?.Message);
|
||||
// Critical: This is where we suspect the DbUpdateException
|
||||
Console.WriteLine($"UPLOAD DB ERROR: {ex.Message} | {ex.InnerException?.Message}");
|
||||
return BadRequest($"Database error during file registration: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
var fileObj = new Oqtane.Models.File
|
||||
catch (Exception ex)
|
||||
{
|
||||
FolderId = folder.FolderId,
|
||||
Name = uniqueName,
|
||||
Extension = ext.Substring(1),
|
||||
Size = (int)file.Length,
|
||||
ImageHeight = 0,
|
||||
ImageWidth = 0
|
||||
};
|
||||
|
||||
var addedFile = _files.AddFile(fileObj);
|
||||
|
||||
return Ok(new { FileId = addedFile.FileId, FileName = file.FileName });
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Create, ex, "General Upload Error: {Message}",
|
||||
ex.Message);
|
||||
return BadRequest(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
return StatusCode((int)HttpStatusCode.Forbidden);
|
||||
}
|
||||
|
||||
@@ -246,71 +371,25 @@ namespace SZUAbsolventenverein.Module.PremiumArea.Controllers
|
||||
{
|
||||
await _service.ReportApplicationAsync(id, ModuleId, reason);
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("download/{id}")]
|
||||
[Authorize]
|
||||
public async Task<IActionResult> Download(int id, string moduleid)
|
||||
{
|
||||
int ModuleId;
|
||||
if (int.TryParse(moduleid, out ModuleId) && IsAuthorizedEntityId(EntityNames.Module, ModuleId))
|
||||
{
|
||||
var app = await _service.GetApplicationAsync(id, ModuleId);
|
||||
if (app == null) return NotFound();
|
||||
|
||||
var alias = _accessor.HttpContext.Items["Alias"] as Alias;
|
||||
// Access Rules:
|
||||
// 1. Admin
|
||||
if (_accessor.HttpContext.User.IsInRole(RoleNames.Admin))
|
||||
{
|
||||
return await ServeFile(app.FileId.Value, app.PdfFileName);
|
||||
}
|
||||
|
||||
// 2. Owner
|
||||
var username = _accessor.HttpContext.User.Identity.Name;
|
||||
var currentUserId = -1;
|
||||
if (username != null)
|
||||
{
|
||||
var u = _users.GetUser(username, alias.SiteId);
|
||||
if (u != null) currentUserId = u.UserId;
|
||||
}
|
||||
|
||||
if (currentUserId == app.UserId)
|
||||
{
|
||||
return await ServeFile(app.FileId.Value, app.PdfFileName);
|
||||
}
|
||||
|
||||
// 3. Premium User AND Published/Approved
|
||||
if (app.Status == "Approved" || app.Status == "Published")
|
||||
{
|
||||
var premium = _premiums.GetUserPremium(currentUserId);
|
||||
if (premium != null && premium.PremiumUntil.HasValue && premium.PremiumUntil.Value > DateTime.UtcNow)
|
||||
{
|
||||
return await ServeFile(app.FileId.Value, app.PdfFileName);
|
||||
}
|
||||
}
|
||||
|
||||
return StatusCode((int)HttpStatusCode.Forbidden);
|
||||
}
|
||||
return StatusCode((int)HttpStatusCode.Forbidden);
|
||||
}
|
||||
|
||||
private async Task<IActionResult> ServeFile(int fileId, string downloadName)
|
||||
{
|
||||
var file = _files.GetFile(fileId);
|
||||
if (file != null)
|
||||
{
|
||||
var path = _files.GetFilePath(file);
|
||||
if (System.IO.File.Exists(path))
|
||||
{
|
||||
var bytes = await System.IO.File.ReadAllBytesAsync(path);
|
||||
return File(bytes, "application/pdf", downloadName ?? file.Name);
|
||||
}
|
||||
var path = _files.GetFilePath(file);
|
||||
if (System.IO.File.Exists(path))
|
||||
{
|
||||
var bytes = await System.IO.File.ReadAllBytesAsync(path);
|
||||
return File(bytes, "application/pdf", downloadName ?? file.Name);
|
||||
}
|
||||
}
|
||||
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
42
Server/Migrations/01000001_AddPremiumTables copy.cs
Normal file
42
Server/Migrations/01000001_AddPremiumTables copy.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Oqtane.Databases.Interfaces;
|
||||
using Oqtane.Migrations;
|
||||
using SZUAbsolventenverein.Module.PremiumArea.Migrations.EntityBuilders;
|
||||
using SZUAbsolventenverein.Module.PremiumArea.Repository;
|
||||
|
||||
namespace SZUAbsolventenverein.Module.PremiumArea.Migrations
|
||||
{
|
||||
[DbContext(typeof(PremiumAreaContext))]
|
||||
[Migration("SZUAbsolventenverein.Module.PremiumArea.01.00.00.01")]
|
||||
public class AddPremiumTables : MultiDatabaseMigration
|
||||
{
|
||||
public AddPremiumTables(IDatabase database) : base(database)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
var engAppBuilder = new EngineerApplicationEntityBuilder(migrationBuilder, ActiveDatabase);
|
||||
engAppBuilder.Create();
|
||||
|
||||
var userPremBuilder = new UserPremiumEntityBuilder(migrationBuilder, ActiveDatabase);
|
||||
userPremBuilder.Create();
|
||||
|
||||
var premEventBuilder = new PremiumEventEntityBuilder(migrationBuilder, ActiveDatabase);
|
||||
premEventBuilder.Create();
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
var engAppBuilder = new EngineerApplicationEntityBuilder(migrationBuilder, ActiveDatabase);
|
||||
engAppBuilder.Drop();
|
||||
|
||||
var userPremBuilder = new UserPremiumEntityBuilder(migrationBuilder, ActiveDatabase);
|
||||
userPremBuilder.Drop();
|
||||
|
||||
var premEventBuilder = new PremiumEventEntityBuilder(migrationBuilder, ActiveDatabase);
|
||||
premEventBuilder.Drop();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Oqtane.Databases.Interfaces;
|
||||
using Oqtane.Migrations;
|
||||
using SZUAbsolventenverein.Module.PremiumArea.Migrations.EntityBuilders;
|
||||
using SZUAbsolventenverein.Module.PremiumArea.Repository;
|
||||
|
||||
namespace SZUAbsolventenverein.Module.PremiumArea.Migrations
|
||||
{
|
||||
[DbContext(typeof(PremiumAreaContext))]
|
||||
[Migration("SZUAbsolventenverein.Module.PremiumArea.01.00.00.02")]
|
||||
public class RemoveReportingAndMoveToFileManager : MultiDatabaseMigration
|
||||
{
|
||||
public RemoveReportingAndMoveToFileManager(IDatabase database) : base(database)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
var engAppBuilder = new EngineerApplicationEntityBuilder(migrationBuilder, ActiveDatabase);
|
||||
engAppBuilder.DropColumn("PdfFileName");
|
||||
engAppBuilder.DropColumn("IsReported");
|
||||
engAppBuilder.DropColumn("ReportReason");
|
||||
engAppBuilder.DropColumn("ReportCount");
|
||||
engAppBuilder.DropColumn("AdminReviewedBy");
|
||||
engAppBuilder.DropColumn("AdminReviewedAt");
|
||||
engAppBuilder.DropColumn("AdminNote");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
@@ -52,23 +53,59 @@ namespace SZUAbsolventenverein.Module.PremiumArea.Repository
|
||||
}
|
||||
else
|
||||
{
|
||||
return db.EngineerApplication.AsNoTracking().FirstOrDefault(item => item.ApplicationId == ApplicationId);
|
||||
return db.EngineerApplication.AsNoTracking()
|
||||
.FirstOrDefault(item => item.ApplicationId == ApplicationId);
|
||||
}
|
||||
}
|
||||
|
||||
public EngineerApplication AddEngineerApplication(EngineerApplication EngineerApplication)
|
||||
{
|
||||
using var db = _factory.CreateDbContext();
|
||||
db.EngineerApplication.Add(EngineerApplication);
|
||||
db.SaveChanges();
|
||||
try
|
||||
{
|
||||
EngineerApplication.CreatedBy = EngineerApplication.CreatedBy ?? "system";
|
||||
EngineerApplication.CreatedOn = DateTime.UtcNow;
|
||||
EngineerApplication.ModifiedBy = EngineerApplication.ModifiedBy ?? "system";
|
||||
EngineerApplication.ModifiedOn = DateTime.UtcNow;
|
||||
db.EngineerApplication.Add(EngineerApplication);
|
||||
db.SaveChanges();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Throwing a new exception with more details so it's visible
|
||||
var msg = $"DB Error: {ex.Message} | Inner: {ex.InnerException?.Message}";
|
||||
Console.WriteLine(msg); // Log to console for dotnet run
|
||||
throw new Exception(msg, ex);
|
||||
}
|
||||
|
||||
return EngineerApplication;
|
||||
}
|
||||
|
||||
public EngineerApplication UpdateEngineerApplication(EngineerApplication EngineerApplication)
|
||||
{
|
||||
using var db = _factory.CreateDbContext();
|
||||
db.Entry(EngineerApplication).State = EntityState.Modified;
|
||||
db.SaveChanges();
|
||||
try
|
||||
{
|
||||
var existing = db.EngineerApplication.Find(EngineerApplication.ApplicationId);
|
||||
if (existing != null)
|
||||
{
|
||||
existing.FileId = EngineerApplication.FileId;
|
||||
existing.Status = EngineerApplication.Status;
|
||||
existing.SubmittedOn = EngineerApplication.SubmittedOn;
|
||||
existing.ApprovedOn = EngineerApplication.ApprovedOn;
|
||||
|
||||
existing.ModifiedBy = EngineerApplication.ModifiedBy ?? "system";
|
||||
existing.ModifiedOn = DateTime.UtcNow;
|
||||
db.SaveChanges();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var msg = $"DB Error (Update): {ex.Message} | Inner: {ex.InnerException?.Message}";
|
||||
Console.WriteLine(msg);
|
||||
throw new Exception(msg, ex);
|
||||
}
|
||||
|
||||
return EngineerApplication;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
@@ -51,6 +52,10 @@ namespace SZUAbsolventenverein.Module.PremiumArea.Repository
|
||||
public Models.PremiumArea AddPremiumArea(Models.PremiumArea PremiumArea)
|
||||
{
|
||||
using var db = _factory.CreateDbContext();
|
||||
PremiumArea.CreatedBy = PremiumArea.CreatedBy ?? "system";
|
||||
PremiumArea.CreatedOn = DateTime.UtcNow;
|
||||
PremiumArea.ModifiedBy = PremiumArea.ModifiedBy ?? "system";
|
||||
PremiumArea.ModifiedOn = DateTime.UtcNow;
|
||||
db.PremiumArea.Add(PremiumArea);
|
||||
db.SaveChanges();
|
||||
return PremiumArea;
|
||||
@@ -59,8 +64,14 @@ namespace SZUAbsolventenverein.Module.PremiumArea.Repository
|
||||
public Models.PremiumArea UpdatePremiumArea(Models.PremiumArea PremiumArea)
|
||||
{
|
||||
using var db = _factory.CreateDbContext();
|
||||
db.Entry(PremiumArea).State = EntityState.Modified;
|
||||
db.SaveChanges();
|
||||
var existing = db.PremiumArea.Find(PremiumArea.PremiumAreaId);
|
||||
if (existing != null)
|
||||
{
|
||||
existing.Name = PremiumArea.Name;
|
||||
existing.ModifiedBy = PremiumArea.ModifiedBy ?? "system";
|
||||
existing.ModifiedOn = DateTime.UtcNow;
|
||||
db.SaveChanges();
|
||||
}
|
||||
return PremiumArea;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
@@ -34,19 +35,35 @@ namespace SZUAbsolventenverein.Module.PremiumArea.Repository
|
||||
using var db = _factory.CreateDbContext();
|
||||
if (UserPremium.Id > 0)
|
||||
{
|
||||
db.Entry(UserPremium).State = EntityState.Modified;
|
||||
var existing = db.UserPremium.Find(UserPremium.Id);
|
||||
if (existing != null)
|
||||
{
|
||||
existing.UserId = UserPremium.UserId;
|
||||
existing.PremiumUntil = UserPremium.PremiumUntil;
|
||||
existing.ModifiedBy = UserPremium.ModifiedBy ?? "system";
|
||||
existing.ModifiedOn = DateTime.UtcNow;
|
||||
db.SaveChanges();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UserPremium.CreatedBy = UserPremium.CreatedBy ?? "system";
|
||||
UserPremium.CreatedOn = DateTime.UtcNow;
|
||||
UserPremium.ModifiedBy = UserPremium.ModifiedBy ?? "system";
|
||||
UserPremium.ModifiedOn = DateTime.UtcNow;
|
||||
db.UserPremium.Add(UserPremium);
|
||||
db.SaveChanges();
|
||||
}
|
||||
db.SaveChanges();
|
||||
return UserPremium;
|
||||
}
|
||||
|
||||
public void AddPremiumEvent(PremiumEvent premiumEvent)
|
||||
{
|
||||
using var db = _factory.CreateDbContext();
|
||||
premiumEvent.CreatedBy = premiumEvent.CreatedBy ?? "system";
|
||||
premiumEvent.CreatedOn = DateTime.UtcNow;
|
||||
premiumEvent.ModifiedBy = premiumEvent.ModifiedBy ?? "system";
|
||||
premiumEvent.ModifiedOn = DateTime.UtcNow;
|
||||
db.PremiumEvent.Add(premiumEvent);
|
||||
db.SaveChanges();
|
||||
}
|
||||
|
||||
98
Server/Services/PremiumService.cs
Normal file
98
Server/Services/PremiumService.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using SZUAbsolventenverein.Module.PremiumArea.Models;
|
||||
using SZUAbsolventenverein.Module.PremiumArea.Repository;
|
||||
using Oqtane.Infrastructure;
|
||||
using Oqtane.Shared;
|
||||
using Oqtane.Modules;
|
||||
using Oqtane.Enums;
|
||||
|
||||
namespace SZUAbsolventenverein.Module.PremiumArea.Services
|
||||
{
|
||||
public interface IPremiumService
|
||||
{
|
||||
bool IsPremium(int userId);
|
||||
DateTime? GetPremiumUntil(int userId);
|
||||
void GrantPremium(int userId, int durationMonths, string source, string referenceId = null);
|
||||
}
|
||||
|
||||
public class PremiumService : IPremiumService, ITransientService
|
||||
{
|
||||
private readonly IUserPremiumRepository _userPremiumRepository;
|
||||
private readonly ILogManager _logger;
|
||||
|
||||
public PremiumService(IUserPremiumRepository userPremiumRepository, ILogManager logger)
|
||||
{
|
||||
_userPremiumRepository = userPremiumRepository;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public bool IsPremium(int userId)
|
||||
{
|
||||
var premium = _userPremiumRepository.GetUserPremium(userId);
|
||||
return premium != null && premium.PremiumUntil.HasValue && premium.PremiumUntil.Value > DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public DateTime? GetPremiumUntil(int userId)
|
||||
{
|
||||
var premium = _userPremiumRepository.GetUserPremium(userId);
|
||||
return premium?.PremiumUntil;
|
||||
}
|
||||
|
||||
public void GrantPremium(int userId, int durationMonths, string source, string referenceId = null)
|
||||
{
|
||||
var current = _userPremiumRepository.GetUserPremium(userId);
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
DateTime startBase = now;
|
||||
if (current != null && current.PremiumUntil.HasValue && current.PremiumUntil.Value > now)
|
||||
{
|
||||
startBase = current.PremiumUntil.Value;
|
||||
}
|
||||
|
||||
var newUntil = startBase.AddMonths(durationMonths);
|
||||
|
||||
// delta days for audit
|
||||
int deltaDays = (newUntil - (current?.PremiumUntil ?? now)).Days;
|
||||
// correction: actually we want to know how many days we ADDED to the existing flow.
|
||||
// If they had 0 days separately, we added durationMonths * 30 approx.
|
||||
// If they had existing time, we added durationMonths.
|
||||
// Audit Log usually tracks "What did this action add?". It added 1 year.
|
||||
// But let's calculate days added relative to "previous state".
|
||||
// If expired: Added (NewUntil - Now) days.
|
||||
// If active: Added (NewUntil - OldUntil) = durationMonths roughly.
|
||||
|
||||
// Simpler: Just store the DurationMonths converted to days roughly, or the stored delta.
|
||||
int addedDays = (newUntil - startBase).Days;
|
||||
|
||||
if (current == null)
|
||||
{
|
||||
current = new UserPremium
|
||||
{
|
||||
UserId = userId,
|
||||
CreatedOn = now,
|
||||
ModifiedOn = now
|
||||
};
|
||||
}
|
||||
|
||||
current.PremiumUntil = newUntil;
|
||||
current.Source = source;
|
||||
current.ModifiedOn = now;
|
||||
|
||||
_userPremiumRepository.SaveUserPremium(current);
|
||||
|
||||
// Audit
|
||||
var audit = new PremiumEvent
|
||||
{
|
||||
UserId = userId,
|
||||
DeltaDays = addedDays,
|
||||
Source = source,
|
||||
ReferenceId = referenceId,
|
||||
CreatedOn = now,
|
||||
ModifiedOn = now
|
||||
};
|
||||
_userPremiumRepository.AddPremiumEvent(audit);
|
||||
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Granted Premium for User {UserId} until {Until}", userId, newUntil);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,9 @@ namespace SZUAbsolventenverein.Module.PremiumArea.Services
|
||||
private readonly IHttpContextAccessor _accessor;
|
||||
private readonly Alias _alias;
|
||||
|
||||
public ServerEngineerApplicationService(IEngineerApplicationRepository repository, IPremiumService premiumService, IUserPermissions userPermissions, ITenantManager tenantManager, ILogManager logger, IHttpContextAccessor accessor)
|
||||
public ServerEngineerApplicationService(IEngineerApplicationRepository repository,
|
||||
IPremiumService premiumService, IUserPermissions userPermissions, ITenantManager tenantManager,
|
||||
ILogManager logger, IHttpContextAccessor accessor)
|
||||
{
|
||||
_repository = repository;
|
||||
_premiumService = premiumService;
|
||||
@@ -34,42 +36,43 @@ namespace SZUAbsolventenverein.Module.PremiumArea.Services
|
||||
|
||||
public Task<List<EngineerApplication>> 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());
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
||||
return Task.FromResult(new List<EngineerApplication>());
|
||||
// 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<EngineerApplication>());
|
||||
}
|
||||
|
||||
public Task<List<EngineerApplication>> 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<EngineerApplication>());
|
||||
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<EngineerApplication>());
|
||||
}
|
||||
|
||||
public Task<EngineerApplication> GetApplicationAsync(int ApplicationId, int ModuleId)
|
||||
@@ -81,7 +84,8 @@ namespace SZUAbsolventenverein.Module.PremiumArea.Services
|
||||
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 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));
|
||||
|
||||
@@ -97,21 +101,22 @@ namespace SZUAbsolventenverein.Module.PremiumArea.Services
|
||||
{
|
||||
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<EngineerApplication>(null);
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Add Attempt (Anonymous)");
|
||||
return Task.FromResult<EngineerApplication>(null);
|
||||
}
|
||||
|
||||
// Check if allowed to view module (Registered Users usually can View)
|
||||
if (_userPermissions.IsAuthorized(user, _alias.SiteId, EntityNames.Module, Application.ModuleId, PermissionNames.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";
|
||||
|
||||
if (string.IsNullOrEmpty(Application.Status)) Application.Status = "Draft";
|
||||
|
||||
// Set ApprovedOn if Published? user asked for removal of admin approval.
|
||||
if (Application.Status == "Published")
|
||||
{
|
||||
@@ -120,9 +125,11 @@ namespace SZUAbsolventenverein.Module.PremiumArea.Services
|
||||
}
|
||||
|
||||
Application = _repository.AddEngineerApplication(Application);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Application Added {Application}", Application);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Application Added {Application}",
|
||||
Application);
|
||||
return Task.FromResult(Application);
|
||||
}
|
||||
|
||||
return Task.FromResult<EngineerApplication>(null);
|
||||
}
|
||||
|
||||
@@ -133,31 +140,27 @@ namespace SZUAbsolventenverein.Module.PremiumArea.Services
|
||||
|
||||
var user = _accessor.HttpContext.User;
|
||||
var userId = _accessor.HttpContext.GetUserId();
|
||||
bool isAdmin = _userPermissions.IsAuthorized(user, _alias.SiteId, EntityNames.Module, Application.ModuleId, PermissionNames.Edit);
|
||||
|
||||
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
|
||||
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.AdminNote = existing.AdminNote;
|
||||
Application.AdminReviewedBy = existing.AdminReviewedBy;
|
||||
Application.AdminReviewedAt = existing.AdminReviewedAt;
|
||||
// Preserve report status if user editing
|
||||
Application.IsReported = existing.IsReported;
|
||||
Application.ReportReason = existing.ReportReason;
|
||||
Application.ReportCount = existing.ReportCount;
|
||||
}
|
||||
|
||||
Application = _repository.UpdateEngineerApplication(Application);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Application Updated {Application}", Application);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Application Updated {Application}",
|
||||
Application);
|
||||
return Task.FromResult(Application);
|
||||
}
|
||||
|
||||
@@ -167,100 +170,97 @@ namespace SZUAbsolventenverein.Module.PremiumArea.Services
|
||||
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);
|
||||
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.AdminReviewedBy = _accessor.HttpContext.GetUserId();
|
||||
app.AdminReviewedAt = DateTime.UtcNow;
|
||||
|
||||
// Clear Reports
|
||||
app.IsReported = false;
|
||||
// app.ReportReason = null; // Optional: keep history?
|
||||
// app.ReportCount = 0;
|
||||
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);
|
||||
_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;
|
||||
// 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";
|
||||
app.AdminNote = Reason;
|
||||
app.AdminReviewedBy = _accessor.HttpContext.GetUserId();
|
||||
app.AdminReviewedAt = DateTime.UtcNow;
|
||||
_repository.UpdateEngineerApplication(app);
|
||||
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Application Rejected {Id}", ApplicationId);
|
||||
}
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
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;
|
||||
// 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.
|
||||
|
||||
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;
|
||||
// First, get the application to check existence
|
||||
var app = _repository.GetEngineerApplication(ApplicationId);
|
||||
if (app == null || app.ModuleId != ModuleId) return Task.CompletedTask;
|
||||
|
||||
if (canView)
|
||||
{
|
||||
app.IsReported = true;
|
||||
app.ReportReason = Reason;
|
||||
app.ReportCount++;
|
||||
_repository.UpdateEngineerApplication(app);
|
||||
// Send Notification?
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Application Reported {Id}", ApplicationId);
|
||||
}
|
||||
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)
|
||||
@@ -274,55 +274,58 @@ namespace SZUAbsolventenverein.Module.PremiumArea.Services
|
||||
// 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);
|
||||
}
|
||||
// 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;
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user