feat(halloffame): implement image upload and enhance module functionality
- Added image upload system (JPG/PNG, max 5MB) with live preview and removal option - Fixed Concurrency Exception during deletion (split transactions for reports and entries) - Optimized card layout: consistent height and height-based truncation for descriptions - Added sort direction toggle (Ascending/Descending) with arrow icons for Date, Name, and Year - Refactored HallOfFameService to use streams for Server/Wasm compatibility - Improved error handling and UI feedback for upload and delete operations
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using System.Collections.Generic;
|
||||
@@ -10,6 +11,8 @@ using SZUAbsolventenverein.Module.HallOfFame.Services;
|
||||
using Oqtane.Controllers;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using System.IO;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
|
||||
namespace SZUAbsolventenverein.Module.HallOfFame.Controllers
|
||||
{
|
||||
@@ -17,10 +20,12 @@ namespace SZUAbsolventenverein.Module.HallOfFame.Controllers
|
||||
public class HallOfFameController : ModuleControllerBase
|
||||
{
|
||||
private readonly IHallOfFameService _HallOfFameService;
|
||||
private readonly IWebHostEnvironment _environment;
|
||||
|
||||
public HallOfFameController(IHallOfFameService HallOfFameService, ILogManager logger, IHttpContextAccessor accessor) : base(logger, accessor)
|
||||
public HallOfFameController(IHallOfFameService HallOfFameService, ILogManager logger, IHttpContextAccessor accessor, IWebHostEnvironment environment) : base(logger, accessor)
|
||||
{
|
||||
_HallOfFameService = HallOfFameService;
|
||||
_environment = environment;
|
||||
}
|
||||
|
||||
// GET: api/<controller>?moduleid=x
|
||||
@@ -33,9 +38,10 @@ namespace SZUAbsolventenverein.Module.HallOfFame.Controllers
|
||||
if (int.TryParse(moduleid, out ModuleId) && IsAuthorizedEntityId(EntityNames.Module, ModuleId))
|
||||
{
|
||||
var list = await _HallOfFameService.GetHallOfFamesAsync(ModuleId);
|
||||
// Filter: Show only Published unless user has Edit permissions (simplified check for now, can be expanded)
|
||||
// For now, let's filter in memory or service. The requirement says: "Hauptseite zeigt nur Published".
|
||||
// We will filter here.
|
||||
if (User.IsInRole(RoleNames.Admin) || User.IsInRole(RoleNames.Host))
|
||||
{
|
||||
return list;
|
||||
}
|
||||
return list.Where(item => item.Status == "Published");
|
||||
}
|
||||
else
|
||||
@@ -138,7 +144,47 @@ namespace SZUAbsolventenverein.Module.HallOfFame.Controllers
|
||||
return HallOfFame;
|
||||
}
|
||||
|
||||
// DELETE api/<controller>/5
|
||||
// PUT api/<controller>/report/5
|
||||
[HttpPut("report/{id}")]
|
||||
[Authorize(Policy = PolicyNames.ViewModule)]
|
||||
public async Task Report(int id, [FromQuery] string reason)
|
||||
{
|
||||
Models.HallOfFame HallOfFame = await _HallOfFameService.GetHallOfFameAsync(id, -1);
|
||||
if (HallOfFame != null && IsAuthorizedEntityId(EntityNames.Module, HallOfFame.ModuleId))
|
||||
{
|
||||
await _HallOfFameService.ReportAsync(id, HallOfFame.ModuleId, reason);
|
||||
}
|
||||
}
|
||||
|
||||
// GET api/<controller>/reports/5?moduleid=x
|
||||
[HttpGet("reports/{id}")]
|
||||
[Authorize(Policy = PolicyNames.EditModule)]
|
||||
public async Task<IEnumerable<Models.HallOfFameReport>> GetReports(int id, string moduleid)
|
||||
{
|
||||
int ModuleId;
|
||||
if (int.TryParse(moduleid, out ModuleId) && IsAuthorizedEntityId(EntityNames.Module, ModuleId))
|
||||
{
|
||||
return await _HallOfFameService.GetHallOfFameReportsAsync(id, ModuleId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized HallOfFame GetReports Attempt {HallOfFameId} {ModuleId}", id, moduleid);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE api/<controller>/report/5/x
|
||||
[HttpDelete("report/{id}/{moduleid}")]
|
||||
[Authorize(Policy = PolicyNames.EditModule)]
|
||||
public async Task DeleteReport(int id, int moduleid)
|
||||
{
|
||||
if (IsAuthorizedEntityId(EntityNames.Module, moduleid))
|
||||
{
|
||||
await _HallOfFameService.DeleteHallOfFameReportAsync(id, moduleid);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpDelete("{id}/{moduleid}")]
|
||||
[Authorize(Policy = PolicyNames.EditModule)]
|
||||
public async Task Delete(int id, int moduleid)
|
||||
@@ -154,5 +200,33 @@ namespace SZUAbsolventenverein.Module.HallOfFame.Controllers
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
}
|
||||
}
|
||||
[HttpPost("upload")]
|
||||
[Authorize(Policy = PolicyNames.EditModule)]
|
||||
public async Task<IActionResult> Upload(IFormFile file)
|
||||
{
|
||||
if (file == null || file.Length == 0) return BadRequest("Keine Datei ausgewählt.");
|
||||
|
||||
var extension = Path.GetExtension(file.FileName).ToLower();
|
||||
if (extension != ".jpg" && extension != ".jpeg" && extension != ".png")
|
||||
{
|
||||
return BadRequest("Nur JPG und PNG Dateien sind erlaubt.");
|
||||
}
|
||||
|
||||
var folder = Path.Combine(_environment.WebRootPath, "Content", "HallOfFame");
|
||||
if (!Directory.Exists(folder))
|
||||
{
|
||||
Directory.CreateDirectory(folder);
|
||||
}
|
||||
|
||||
var fileName = Guid.NewGuid().ToString() + extension;
|
||||
var path = Path.Combine(folder, fileName);
|
||||
|
||||
using (var stream = new FileStream(path, FileMode.Create))
|
||||
{
|
||||
await file.CopyToAsync(stream);
|
||||
}
|
||||
|
||||
return Ok(new { url = "/Content/HallOfFame/" + fileName });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user