Initial commit

This commit is contained in:
Adam Gaiswinkler
2026-01-02 22:52:11 +01:00
commit 5dfa690432
37 changed files with 1611 additions and 0 deletions

4
Server/AssemblyInfo.cs Normal file
View File

@ -0,0 +1,4 @@
using System.Resources;
using Microsoft.Extensions.Localization;
[assembly: RootNamespace("SZUAbsolventenverein.Module.HallOfFame.Server")]

View File

@ -0,0 +1,114 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using System.Collections.Generic;
using Microsoft.AspNetCore.Http;
using Oqtane.Shared;
using Oqtane.Enums;
using Oqtane.Infrastructure;
using SZUAbsolventenverein.Module.HallOfFame.Services;
using Oqtane.Controllers;
using System.Net;
using System.Threading.Tasks;
namespace SZUAbsolventenverein.Module.HallOfFame.Controllers
{
[Route(ControllerRoutes.ApiRoute)]
public class HallOfFameController : ModuleControllerBase
{
private readonly IHallOfFameService _HallOfFameService;
public HallOfFameController(IHallOfFameService HallOfFameService, ILogManager logger, IHttpContextAccessor accessor) : base(logger, accessor)
{
_HallOfFameService = HallOfFameService;
}
// GET: api/<controller>?moduleid=x
[HttpGet]
[Authorize(Policy = PolicyNames.ViewModule)]
public async Task<IEnumerable<Models.HallOfFame>> Get(string moduleid)
{
int ModuleId;
if (int.TryParse(moduleid, out ModuleId) && IsAuthorizedEntityId(EntityNames.Module, ModuleId))
{
return await _HallOfFameService.GetHallOfFamesAsync(ModuleId);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized HallOfFame Get Attempt {ModuleId}", moduleid);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
}
// GET api/<controller>/5
[HttpGet("{id}/{moduleid}")]
[Authorize(Policy = PolicyNames.ViewModule)]
public async Task<Models.HallOfFame> Get(int id, int moduleid)
{
Models.HallOfFame HallOfFame = await _HallOfFameService.GetHallOfFameAsync(id, moduleid);
if (HallOfFame != null && IsAuthorizedEntityId(EntityNames.Module, HallOfFame.ModuleId))
{
return HallOfFame;
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized HallOfFame Get Attempt {HallOfFameId} {ModuleId}", id, moduleid);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
}
// POST api/<controller>
[HttpPost]
[Authorize(Policy = PolicyNames.EditModule)]
public async Task<Models.HallOfFame> Post([FromBody] Models.HallOfFame HallOfFame)
{
if (ModelState.IsValid && IsAuthorizedEntityId(EntityNames.Module, HallOfFame.ModuleId))
{
HallOfFame = await _HallOfFameService.AddHallOfFameAsync(HallOfFame);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized HallOfFame Post Attempt {HallOfFame}", HallOfFame);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
HallOfFame = null;
}
return HallOfFame;
}
// PUT api/<controller>/5
[HttpPut("{id}")]
[Authorize(Policy = PolicyNames.EditModule)]
public async Task<Models.HallOfFame> Put(int id, [FromBody] Models.HallOfFame HallOfFame)
{
if (ModelState.IsValid && HallOfFame.HallOfFameId == id && IsAuthorizedEntityId(EntityNames.Module, HallOfFame.ModuleId))
{
HallOfFame = await _HallOfFameService.UpdateHallOfFameAsync(HallOfFame);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized HallOfFame Put Attempt {HallOfFame}", HallOfFame);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
HallOfFame = null;
}
return HallOfFame;
}
// DELETE api/<controller>/5
[HttpDelete("{id}/{moduleid}")]
[Authorize(Policy = PolicyNames.EditModule)]
public async Task Delete(int id, int moduleid)
{
Models.HallOfFame HallOfFame = await _HallOfFameService.GetHallOfFameAsync(id, moduleid);
if (HallOfFame != null && IsAuthorizedEntityId(EntityNames.Module, HallOfFame.ModuleId))
{
await _HallOfFameService.DeleteHallOfFameAsync(id, HallOfFame.ModuleId);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized HallOfFame Delete Attempt {HallOfFameId} {ModuleId}", id, moduleid);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
}
}
}

View File

@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Oqtane.Modules;
using Oqtane.Models;
using Oqtane.Infrastructure;
using Oqtane.Interfaces;
using Oqtane.Enums;
using Oqtane.Repository;
using SZUAbsolventenverein.Module.HallOfFame.Repository;
using System.Threading.Tasks;
namespace SZUAbsolventenverein.Module.HallOfFame.Manager
{
public class HallOfFameManager : MigratableModuleBase, IInstallable, IPortable, ISearchable
{
private readonly IHallOfFameRepository _HallOfFameRepository;
private readonly IDBContextDependencies _DBContextDependencies;
public HallOfFameManager(IHallOfFameRepository HallOfFameRepository, IDBContextDependencies DBContextDependencies)
{
_HallOfFameRepository = HallOfFameRepository;
_DBContextDependencies = DBContextDependencies;
}
public bool Install(Tenant tenant, string version)
{
return Migrate(new HallOfFameContext(_DBContextDependencies), tenant, MigrationType.Up);
}
public bool Uninstall(Tenant tenant)
{
return Migrate(new HallOfFameContext(_DBContextDependencies), tenant, MigrationType.Down);
}
public string ExportModule(Oqtane.Models.Module module)
{
string content = "";
List<Models.HallOfFame> HallOfFames = _HallOfFameRepository.GetHallOfFames(module.ModuleId).ToList();
if (HallOfFames != null)
{
content = JsonSerializer.Serialize(HallOfFames);
}
return content;
}
public void ImportModule(Oqtane.Models.Module module, string content, string version)
{
List<Models.HallOfFame> HallOfFames = null;
if (!string.IsNullOrEmpty(content))
{
HallOfFames = JsonSerializer.Deserialize<List<Models.HallOfFame>>(content);
}
if (HallOfFames != null)
{
foreach(var HallOfFame in HallOfFames)
{
_HallOfFameRepository.AddHallOfFame(new Models.HallOfFame { ModuleId = module.ModuleId, Name = HallOfFame.Name });
}
}
}
public Task<List<SearchContent>> GetSearchContentsAsync(PageModule pageModule, DateTime lastIndexedOn)
{
var searchContentList = new List<SearchContent>();
foreach (var HallOfFame in _HallOfFameRepository.GetHallOfFames(pageModule.ModuleId))
{
if (HallOfFame.ModifiedOn >= lastIndexedOn)
{
searchContentList.Add(new SearchContent
{
EntityName = "SZUAbsolventenvereinHallOfFame",
EntityId = HallOfFame.HallOfFameId.ToString(),
Title = HallOfFame.Name,
Body = HallOfFame.Name,
ContentModifiedBy = HallOfFame.ModifiedBy,
ContentModifiedOn = HallOfFame.ModifiedOn
});
}
}
return Task.FromResult(searchContentList);
}
}
}

View File

@ -0,0 +1,30 @@
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Oqtane.Databases.Interfaces;
using Oqtane.Migrations;
using SZUAbsolventenverein.Module.HallOfFame.Migrations.EntityBuilders;
using SZUAbsolventenverein.Module.HallOfFame.Repository;
namespace SZUAbsolventenverein.Module.HallOfFame.Migrations
{
[DbContext(typeof(HallOfFameContext))]
[Migration("SZUAbsolventenverein.Module.HallOfFame.01.00.00.00")]
public class InitializeModule : MultiDatabaseMigration
{
public InitializeModule(IDatabase database) : base(database)
{
}
protected override void Up(MigrationBuilder migrationBuilder)
{
var entityBuilder = new HallOfFameEntityBuilder(migrationBuilder, ActiveDatabase);
entityBuilder.Create();
}
protected override void Down(MigrationBuilder migrationBuilder)
{
var entityBuilder = new HallOfFameEntityBuilder(migrationBuilder, ActiveDatabase);
entityBuilder.Drop();
}
}
}

View File

@ -0,0 +1,36 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
using Oqtane.Databases.Interfaces;
using Oqtane.Migrations;
using Oqtane.Migrations.EntityBuilders;
namespace SZUAbsolventenverein.Module.HallOfFame.Migrations.EntityBuilders
{
public class HallOfFameEntityBuilder : AuditableBaseEntityBuilder<HallOfFameEntityBuilder>
{
private const string _entityTableName = "SZUAbsolventenvereinHallOfFame";
private readonly PrimaryKey<HallOfFameEntityBuilder> _primaryKey = new("PK_SZUAbsolventenvereinHallOfFame", x => x.HallOfFameId);
private readonly ForeignKey<HallOfFameEntityBuilder> _moduleForeignKey = new("FK_SZUAbsolventenvereinHallOfFame_Module", x => x.ModuleId, "Module", "ModuleId", ReferentialAction.Cascade);
public HallOfFameEntityBuilder(MigrationBuilder migrationBuilder, IDatabase database) : base(migrationBuilder, database)
{
EntityTableName = _entityTableName;
PrimaryKey = _primaryKey;
ForeignKeys.Add(_moduleForeignKey);
}
protected override HallOfFameEntityBuilder BuildTable(ColumnsBuilder table)
{
HallOfFameId = AddAutoIncrementColumn(table,"HallOfFameId");
ModuleId = AddIntegerColumn(table,"ModuleId");
Name = AddMaxStringColumn(table,"Name");
AddAuditableColumns(table);
return this;
}
public OperationBuilder<AddColumnOperation> HallOfFameId { get; set; }
public OperationBuilder<AddColumnOperation> ModuleId { get; set; }
public OperationBuilder<AddColumnOperation> Name { get; set; }
}
}

View File

@ -0,0 +1,26 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Http;
using Oqtane.Modules;
using Oqtane.Repository;
using Oqtane.Infrastructure;
using Oqtane.Repository.Databases.Interfaces;
namespace SZUAbsolventenverein.Module.HallOfFame.Repository
{
public class HallOfFameContext : DBContextBase, ITransientService, IMultiDatabase
{
public virtual DbSet<Models.HallOfFame> HallOfFame { get; set; }
public HallOfFameContext(IDBContextDependencies DBContextDependencies) : base(DBContextDependencies)
{
// ContextBase handles multi-tenant database connections
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Models.HallOfFame>().ToTable(ActiveDatabase.RewriteName("SZUAbsolventenvereinHallOfFame"));
}
}
}

View File

@ -0,0 +1,75 @@
using Microsoft.EntityFrameworkCore;
using System.Linq;
using System.Collections.Generic;
using Oqtane.Modules;
namespace SZUAbsolventenverein.Module.HallOfFame.Repository
{
public interface IHallOfFameRepository
{
IEnumerable<Models.HallOfFame> GetHallOfFames(int ModuleId);
Models.HallOfFame GetHallOfFame(int HallOfFameId);
Models.HallOfFame GetHallOfFame(int HallOfFameId, bool tracking);
Models.HallOfFame AddHallOfFame(Models.HallOfFame HallOfFame);
Models.HallOfFame UpdateHallOfFame(Models.HallOfFame HallOfFame);
void DeleteHallOfFame(int HallOfFameId);
}
public class HallOfFameRepository : IHallOfFameRepository, ITransientService
{
private readonly IDbContextFactory<HallOfFameContext> _factory;
public HallOfFameRepository(IDbContextFactory<HallOfFameContext> factory)
{
_factory = factory;
}
public IEnumerable<Models.HallOfFame> GetHallOfFames(int ModuleId)
{
using var db = _factory.CreateDbContext();
return db.HallOfFame.Where(item => item.ModuleId == ModuleId).ToList();
}
public Models.HallOfFame GetHallOfFame(int HallOfFameId)
{
return GetHallOfFame(HallOfFameId, true);
}
public Models.HallOfFame GetHallOfFame(int HallOfFameId, bool tracking)
{
using var db = _factory.CreateDbContext();
if (tracking)
{
return db.HallOfFame.Find(HallOfFameId);
}
else
{
return db.HallOfFame.AsNoTracking().FirstOrDefault(item => item.HallOfFameId == HallOfFameId);
}
}
public Models.HallOfFame AddHallOfFame(Models.HallOfFame HallOfFame)
{
using var db = _factory.CreateDbContext();
db.HallOfFame.Add(HallOfFame);
db.SaveChanges();
return HallOfFame;
}
public Models.HallOfFame UpdateHallOfFame(Models.HallOfFame HallOfFame)
{
using var db = _factory.CreateDbContext();
db.Entry(HallOfFame).State = EntityState.Modified;
db.SaveChanges();
return HallOfFame;
}
public void DeleteHallOfFame(int HallOfFameId)
{
using var db = _factory.CreateDbContext();
Models.HallOfFame HallOfFame = db.HallOfFame.Find(HallOfFameId);
db.HallOfFame.Remove(HallOfFame);
db.SaveChanges();
}
}
}

View File

@ -0,0 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
<Version>1.0.0</Version>
<Product>SZUAbsolventenverein.Module.HallOfFame</Product>
<Authors>SZUAbsolventenverein</Authors>
<Company>SZUAbsolventenverein</Company>
<Description>The Hall of Fame module displays selected individuals or achievements within the CMS. Entries are shown online, can be exported as a PDF, and are only published with user consen.</Description>
<Copyright>SZUAbsolventenverein</Copyright>
<AssemblyName>SZUAbsolventenverein.Module.HallOfFame.Server.Oqtane</AssemblyName>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<ItemGroup>
<Content Remove="wwwroot\_content\**\*.*" />
<None Include="wwwroot\_content\**\*.*" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.8" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.8" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.8" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Client\SZUAbsolventenverein.Module.HallOfFame.Client.csproj" />
<ProjectReference Include="..\Shared\SZUAbsolventenverein.Module.HallOfFame.Shared.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="Oqtane.Server"><HintPath>..\..\oqtane.framework\Oqtane.Server\bin\Debug\net9.0\Oqtane.Server.dll</HintPath></Reference>
<Reference Include="Oqtane.Shared"><HintPath>..\..\oqtane.framework\Oqtane.Server\bin\Debug\net9.0\Oqtane.Shared.dll</HintPath></Reference>
</ItemGroup>
</Project>

View File

@ -0,0 +1,101 @@
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.HallOfFame.Repository;
namespace SZUAbsolventenverein.Module.HallOfFame.Services
{
public class ServerHallOfFameService : IHallOfFameService
{
private readonly IHallOfFameRepository _HallOfFameRepository;
private readonly IUserPermissions _userPermissions;
private readonly ILogManager _logger;
private readonly IHttpContextAccessor _accessor;
private readonly Alias _alias;
public ServerHallOfFameService(IHallOfFameRepository HallOfFameRepository, IUserPermissions userPermissions, ITenantManager tenantManager, ILogManager logger, IHttpContextAccessor accessor)
{
_HallOfFameRepository = HallOfFameRepository;
_userPermissions = userPermissions;
_logger = logger;
_accessor = accessor;
_alias = tenantManager.GetAlias();
}
public Task<List<Models.HallOfFame>> GetHallOfFamesAsync(int ModuleId)
{
if (_userPermissions.IsAuthorized(_accessor.HttpContext.User, _alias.SiteId, EntityNames.Module, ModuleId, PermissionNames.View))
{
return Task.FromResult(_HallOfFameRepository.GetHallOfFames(ModuleId).ToList());
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized HallOfFame Get Attempt {ModuleId}", ModuleId);
return null;
}
}
public Task<Models.HallOfFame> GetHallOfFameAsync(int HallOfFameId, int ModuleId)
{
if (_userPermissions.IsAuthorized(_accessor.HttpContext.User, _alias.SiteId, EntityNames.Module, ModuleId, PermissionNames.View))
{
return Task.FromResult(_HallOfFameRepository.GetHallOfFame(HallOfFameId));
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized HallOfFame Get Attempt {HallOfFameId} {ModuleId}", HallOfFameId, ModuleId);
return null;
}
}
public Task<Models.HallOfFame> AddHallOfFameAsync(Models.HallOfFame HallOfFame)
{
if (_userPermissions.IsAuthorized(_accessor.HttpContext.User, _alias.SiteId, EntityNames.Module, HallOfFame.ModuleId, PermissionNames.Edit))
{
HallOfFame = _HallOfFameRepository.AddHallOfFame(HallOfFame);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "HallOfFame Added {HallOfFame}", HallOfFame);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized HallOfFame Add Attempt {HallOfFame}", HallOfFame);
HallOfFame = null;
}
return Task.FromResult(HallOfFame);
}
public Task<Models.HallOfFame> UpdateHallOfFameAsync(Models.HallOfFame HallOfFame)
{
if (_userPermissions.IsAuthorized(_accessor.HttpContext.User, _alias.SiteId, EntityNames.Module, HallOfFame.ModuleId, PermissionNames.Edit))
{
HallOfFame = _HallOfFameRepository.UpdateHallOfFame(HallOfFame);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "HallOfFame Updated {HallOfFame}", HallOfFame);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized HallOfFame Update Attempt {HallOfFame}", HallOfFame);
HallOfFame = null;
}
return Task.FromResult(HallOfFame);
}
public Task DeleteHallOfFameAsync(int HallOfFameId, int ModuleId)
{
if (_userPermissions.IsAuthorized(_accessor.HttpContext.User, _alias.SiteId, EntityNames.Module, ModuleId, PermissionNames.Edit))
{
_HallOfFameRepository.DeleteHallOfFame(HallOfFameId);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "HallOfFame Deleted {HallOfFameId}", HallOfFameId);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized HallOfFame Delete Attempt {HallOfFameId} {ModuleId}", HallOfFameId, ModuleId);
}
return Task.CompletedTask;
}
}
}

View File

@ -0,0 +1,28 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Oqtane.Infrastructure;
using SZUAbsolventenverein.Module.HallOfFame.Repository;
using SZUAbsolventenverein.Module.HallOfFame.Services;
namespace SZUAbsolventenverein.Module.HallOfFame.Startup
{
public class ServerStartup : IServerStartup
{
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// not implemented
}
public void ConfigureMvc(IMvcBuilder mvcBuilder)
{
// not implemented
}
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IHallOfFameService, ServerHallOfFameService>();
services.AddDbContextFactory<HallOfFameContext>(opt => { }, ServiceLifetime.Transient);
}
}
}

View File

@ -0,0 +1 @@
/* Module Custom Styles */

5
Server/wwwroot/Module.js Normal file
View File

@ -0,0 +1,5 @@
/* Module Script */
var SZUAbsolventenverein = SZUAbsolventenverein || {};
SZUAbsolventenverein.HallOfFame = {
};