Feature: Hall of Fame Module Implementation (1.0.1)

- Added Hall of Fame module logic (Models, Controller, Service).
- Implemented 'One Entry Per User' and 'Publish/Draft' workflow.
- Updated UI to Grid Layout (Index.razor) and Unified Form (Edit.razor).
- Added Database Migration 01000001 for new columns.
- Bumped version to 1.0.1.
This commit is contained in:
Adam Gaiswinkler
2026-01-15 00:01:55 +01:00
parent 5dfa690432
commit 7114904412
12 changed files with 378 additions and 55 deletions

View File

@@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Http;
using Oqtane.Shared;
using Oqtane.Enums;
@@ -22,6 +23,7 @@ namespace SZUAbsolventenverein.Module.HallOfFame.Controllers
_HallOfFameService = HallOfFameService;
}
// GET: api/<controller>?moduleid=x
// GET: api/<controller>?moduleid=x
[HttpGet]
[Authorize(Policy = PolicyNames.ViewModule)]
@@ -30,7 +32,11 @@ namespace SZUAbsolventenverein.Module.HallOfFame.Controllers
int ModuleId;
if (int.TryParse(moduleid, out ModuleId) && IsAuthorizedEntityId(EntityNames.Module, ModuleId))
{
return await _HallOfFameService.GetHallOfFamesAsync(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.
return list.Where(item => item.Status == "Published");
}
else
{
@@ -58,6 +64,25 @@ namespace SZUAbsolventenverein.Module.HallOfFame.Controllers
}
}
// GET api/<controller>/user/5?moduleid=x
[HttpGet("user/{userid}")]
[Authorize(Policy = PolicyNames.ViewModule)]
public async Task<Models.HallOfFame> GetByUserId(int userid, string moduleid)
{
int ModuleId;
if (int.TryParse(moduleid, out ModuleId) && IsAuthorizedEntityId(EntityNames.Module, ModuleId))
{
var list = await _HallOfFameService.GetHallOfFamesAsync(ModuleId);
return list.FirstOrDefault(item => item.UserId == userid);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized HallOfFame GetByUserId Attempt {UserId} {ModuleId}", userid, moduleid);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
}
// POST api/<controller>
[HttpPost]
[Authorize(Policy = PolicyNames.EditModule)]
@@ -65,6 +90,15 @@ namespace SZUAbsolventenverein.Module.HallOfFame.Controllers
{
if (ModelState.IsValid && IsAuthorizedEntityId(EntityNames.Module, HallOfFame.ModuleId))
{
// Enforce one entry per user
var allEntries = await _HallOfFameService.GetHallOfFamesAsync(HallOfFame.ModuleId);
if (allEntries.Any(e => e.UserId == HallOfFame.UserId))
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "User {UserId} already has a Hall of Fame entry.", HallOfFame.UserId);
HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
return null;
}
HallOfFame = await _HallOfFameService.AddHallOfFameAsync(HallOfFame);
}
else
@@ -83,7 +117,17 @@ namespace SZUAbsolventenverein.Module.HallOfFame.Controllers
{
if (ModelState.IsValid && HallOfFame.HallOfFameId == id && IsAuthorizedEntityId(EntityNames.Module, HallOfFame.ModuleId))
{
HallOfFame = await _HallOfFameService.UpdateHallOfFameAsync(HallOfFame);
var existing = await _HallOfFameService.GetHallOfFameAsync(id, HallOfFame.ModuleId);
if (existing != null && existing.UserId == HallOfFame.UserId)
{
HallOfFame = await _HallOfFameService.UpdateHallOfFameAsync(HallOfFame);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized HallOfFame Put Attempt by User {UserId} for Entry {HallOfFameId}", HallOfFame.UserId, id);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
HallOfFame = null;
}
}
else
{