Initial commit

This commit is contained in:
2026-01-15 15:22:05 +01:00
commit 0942f0b308
36 changed files with 1525 additions and 0 deletions

4
Client/AssemblyInfo.cs Normal file
View File

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

15
Client/Interop.cs Normal file
View File

@@ -0,0 +1,15 @@
using Microsoft.JSInterop;
using System.Threading.Tasks;
namespace SZUAbsolventenverein.Module.PremiumArea
{
public class Interop
{
private readonly IJSRuntime _jsRuntime;
public Interop(IJSRuntime jsRuntime)
{
_jsRuntime = jsRuntime;
}
}
}

View File

@@ -0,0 +1,112 @@
@using Oqtane.Modules.Controls
@using SZUAbsolventenverein.Module.PremiumArea.Services
@using SZUAbsolventenverein.Module.PremiumArea.Models
@namespace SZUAbsolventenverein.Module.PremiumArea
@inherits ModuleBase
@inject IPremiumAreaService PremiumAreaService
@inject NavigationManager NavigationManager
@inject IStringLocalizer<Edit> Localizer
<form @ref="form" class="@(validated ? " was-validated" : "needs-validation" )" novalidate>
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="name" HelpText="Enter a name" ResourceKey="Name">Name: </Label>
<div class="col-sm-9">
<input id="name" class="form-control" @bind="@_name" required />
</div>
</div>
</div>
<button type="button" class="btn btn-success" @onclick="Save">@Localizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
<br /><br />
@if (PageState.Action == "Edit")
{
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon"></AuditInfo>
}
</form>
@code {
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
public override string Actions => "Add,Edit";
public override string Title => "Manage PremiumArea";
public override List<Resource> Resources => new List<Resource>()
{
new Stylesheet("_content/SZUAbsolventenverein.Module.PremiumArea/Module.css")
};
private ElementReference form;
private bool validated = false;
private int _id;
private string _name;
private string _createdby;
private DateTime _createdon;
private string _modifiedby;
private DateTime _modifiedon;
protected override async Task OnInitializedAsync()
{
try
{
if (PageState.Action == "Edit")
{
_id = Int32.Parse(PageState.QueryString["id"]);
PremiumArea PremiumArea = await PremiumAreaService.GetPremiumAreaAsync(_id, ModuleState.ModuleId);
if (PremiumArea != null)
{
_name = PremiumArea.Name;
_createdby = PremiumArea.CreatedBy;
_createdon = PremiumArea.CreatedOn;
_modifiedby = PremiumArea.ModifiedBy;
_modifiedon = PremiumArea.ModifiedOn;
}
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading PremiumArea {PremiumAreaId} {Error}", _id, ex.Message);
AddModuleMessage(Localizer["Message.LoadError"], MessageType.Error);
}
}
private async Task Save()
{
try
{
validated = true;
var interop = new Oqtane.UI.Interop(JSRuntime);
if (await interop.FormValid(form))
{
if (PageState.Action == "Add")
{
PremiumArea PremiumArea = new PremiumArea();
PremiumArea.ModuleId = ModuleState.ModuleId;
PremiumArea.Name = _name;
PremiumArea = await PremiumAreaService.AddPremiumAreaAsync(PremiumArea);
await logger.LogInformation("PremiumArea Added {PremiumArea}", PremiumArea);
}
else
{
PremiumArea PremiumArea = await PremiumAreaService.GetPremiumAreaAsync(_id, ModuleState.ModuleId);
PremiumArea.Name = _name;
await PremiumAreaService.UpdatePremiumAreaAsync(PremiumArea);
await logger.LogInformation("PremiumArea Updated {PremiumArea}", PremiumArea);
}
NavigationManager.NavigateTo(NavigateUrl());
}
else
{
AddModuleMessage(Localizer["Message.SaveValidation"], MessageType.Warning);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Saving PremiumArea {Error}", ex.Message);
AddModuleMessage(Localizer["Message.SaveError"], MessageType.Error);
}
}
}

View File

@@ -0,0 +1,79 @@
@using SZUAbsolventenverein.Module.PremiumArea.Services
@using SZUAbsolventenverein.Module.PremiumArea.Models
@namespace SZUAbsolventenverein.Module.PremiumArea
@inherits ModuleBase
@inject IPremiumAreaService PremiumAreaService
@inject NavigationManager NavigationManager
@inject IStringLocalizer<Index> Localizer
@if (_PremiumAreas == null)
{
<p><em>Loading...</em></p>
}
else
{
<ActionLink Action="Add" Security="SecurityAccessLevel.Edit" Text="Add PremiumArea" ResourceKey="Add" />
<br />
<br />
@if (@_PremiumAreas.Count != 0)
{
<Pager Items="@_PremiumAreas">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>@Localizer["Name"]</th>
</Header>
<Row>
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.PremiumAreaId.ToString())" ResourceKey="Edit" /></td>
<td><ActionDialog Header="Delete PremiumArea" Message="Are You Sure You Wish To Delete This PremiumArea?" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await Delete(context))" ResourceKey="Delete" Id="@context.PremiumAreaId.ToString()" /></td>
<td>@context.Name</td>
</Row>
</Pager>
}
else
{
<p>@Localizer["Message.DisplayNone"]</p>
}
}
@code {
public override string RenderMode => RenderModes.Static;
public override List<Resource> Resources => new List<Resource>()
{
new Stylesheet("_content/SZUAbsolventenverein.Module.PremiumArea/Module.css"),
new Script("_content/SZUAbsolventenverein.Module.PremiumArea/Module.js")
};
List<PremiumArea> _PremiumAreas;
protected override async Task OnInitializedAsync()
{
try
{
_PremiumAreas = await PremiumAreaService.GetPremiumAreasAsync(ModuleState.ModuleId);
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading PremiumArea {Error}", ex.Message);
AddModuleMessage(Localizer["Message.LoadError"], MessageType.Error);
}
}
private async Task Delete(PremiumArea PremiumArea)
{
try
{
await PremiumAreaService.DeletePremiumAreaAsync(PremiumArea.PremiumAreaId, ModuleState.ModuleId);
await logger.LogInformation("PremiumArea Deleted {PremiumArea}", PremiumArea);
_PremiumAreas = await PremiumAreaService.GetPremiumAreasAsync(ModuleState.ModuleId);
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Deleting PremiumArea {PremiumArea} {Error}", PremiumArea, ex.Message);
AddModuleMessage(Localizer["Message.DeleteError"], MessageType.Error);
}
}
}

View File

@@ -0,0 +1,19 @@
using Oqtane.Models;
using Oqtane.Modules;
namespace SZUAbsolventenverein.Module.PremiumArea
{
public class ModuleInfo : IModule
{
public ModuleDefinition ModuleDefinition => new ModuleDefinition
{
Name = "PremiumArea",
Description = "This module adds a premium member system to Octane. Users receive premium status after completing a payment. Premium members get access to exclusive features and content.",
Version = "1.0.0",
ServerManagerType = "SZUAbsolventenverein.Module.PremiumArea.Manager.PremiumAreaManager, SZUAbsolventenverein.Module.PremiumArea.Server.Oqtane",
ReleaseVersions = "1.0.0",
Dependencies = "SZUAbsolventenverein.Module.PremiumArea.Shared.Oqtane",
PackageName = "SZUAbsolventenverein.Module.PremiumArea"
};
}
}

View File

@@ -0,0 +1,47 @@
@namespace SZUAbsolventenverein.Module.PremiumArea
@inherits ModuleBase
@inject ISettingService SettingService
@inject IStringLocalizer<Settings> Localizer
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="value" HelpText="Enter a value" ResourceKey="SettingName" ResourceType="@resourceType">Name: </Label>
<div class="col-sm-9">
<input id="value" type="text" class="form-control" @bind="@_value" />
</div>
</div>
</div>
@code {
private string resourceType = "SZUAbsolventenverein.Module.PremiumArea.Settings, SZUAbsolventenverein.Module.PremiumArea.Client.Oqtane"; // for localization
public override string Title => "PremiumArea Settings";
string _value;
protected override async Task OnInitializedAsync()
{
try
{
Dictionary<string, string> settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
_value = SettingService.GetSetting(settings, "SettingName", "");
}
catch (Exception ex)
{
AddModuleMessage(ex.Message, MessageType.Error);
}
}
public async Task UpdateSettings()
{
try
{
var settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
settings = SettingService.SetSetting(settings, "SettingName", _value);
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
}
catch (Exception ex)
{
AddModuleMessage(ex.Message, MessageType.Error);
}
}
}

View File

@@ -0,0 +1,141 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Name.Text" xml:space="preserve">
<value>Name: </value>
</data>
<data name="Name.HelpText" xml:space="preserve">
<value>Enter the name</value>
</data>
<data name="Save" xml:space="preserve">
<value>Save</value>
</data>
<data name="Cancel" xml:space="preserve">
<value>Cancel</value>
</data>
<data name="Message.LoadError" xml:space="preserve">
<value>Error Loading PremiumArea</value>
</data>
<data name="Message.SaveValidation" xml:space="preserve">
<value>Please Provide All Required Information</value>
</data>
<data name="Message.SaveError" xml:space="preserve">
<value>Error Saving PremiumArea</value>
</data>
</root>

View File

@@ -0,0 +1,147 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Name" xml:space="preserve">
<value>Name</value>
</data>
<data name="Add.Text" xml:space="preserve">
<value>Add PremiumArea</value>
</data>
<data name="Edit.Text" xml:space="preserve">
<value>Edit</value>
</data>
<data name="Delete.Text" xml:space="preserve">
<value>Delete</value>
</data>
<data name="Delete.Header" xml:space="preserve">
<value>Delete PremiumArea</value>
</data>
<data name="Delete.Message" xml:space="preserve">
<value>Are You Sure You Wish To Delete This PremiumArea?</value>
</data>
<data name="Message.DisplayNone" xml:space="preserve">
<value>No PremiumAreas To Display</value>
</data>
<data name="Message.LoadError" xml:space="preserve">
<value>Error Loading PremiumArea</value>
</data>
<data name="Message.DeleteError" xml:space="preserve">
<value>Error Deleting PremiumArea</value>
</data>
</root>

View File

@@ -0,0 +1,126 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="SettingName.Text" xml:space="preserve">
<value>Name: </value>
</data>
<data name="SettingName.HelpText" xml:space="preserve">
<value>Enter a value</value>
</data>
</root>

View File

@@ -0,0 +1,38 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Version>1.0.0</Version>
<Authors>SZUAbsolventenverein</Authors>
<Company>SZUAbsolventenverein</Company>
<Description>This module adds a premium member system to Octane. Users receive premium status after completing a payment. Premium members get access to exclusive features and content.</Description>
<Product>SZUAbsolventenverein.Module.PremiumArea</Product>
<Copyright>SZUAbsolventenverein</Copyright>
<AssemblyName>SZUAbsolventenverein.Module.PremiumArea.Client.Oqtane</AssemblyName>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="10.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="10.0.1" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="10.0.1" />
<PackageReference Include="Microsoft.Extensions.Http" Version="10.0.1" />
<PackageReference Include="System.Net.Http.Json" Version="10.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Shared\SZUAbsolventenverein.Module.PremiumArea.Shared.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="Oqtane.Client"><HintPath>..\..\oqtane.framework\Oqtane.Server\bin\Debug\net10.0\Oqtane.Client.dll</HintPath></Reference>
<Reference Include="Oqtane.Shared"><HintPath>..\..\oqtane.framework\Oqtane.Server\bin\Debug\net10.0\Oqtane.Shared.dll</HintPath></Reference>
</ItemGroup>
<PropertyGroup>
<!-- there may be other elements here -->
<BlazorWebAssemblyEnableLinking>false</BlazorWebAssemblyEnableLinking>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,55 @@
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Oqtane.Services;
using Oqtane.Shared;
namespace SZUAbsolventenverein.Module.PremiumArea.Services
{
public interface IPremiumAreaService
{
Task<List<Models.PremiumArea>> GetPremiumAreasAsync(int ModuleId);
Task<Models.PremiumArea> GetPremiumAreaAsync(int PremiumAreaId, int ModuleId);
Task<Models.PremiumArea> AddPremiumAreaAsync(Models.PremiumArea PremiumArea);
Task<Models.PremiumArea> UpdatePremiumAreaAsync(Models.PremiumArea PremiumArea);
Task DeletePremiumAreaAsync(int PremiumAreaId, int ModuleId);
}
public class PremiumAreaService : ServiceBase, IPremiumAreaService
{
public PremiumAreaService(HttpClient http, SiteState siteState) : base(http, siteState) { }
private string Apiurl => CreateApiUrl("PremiumArea");
public async Task<List<Models.PremiumArea>> GetPremiumAreasAsync(int ModuleId)
{
List<Models.PremiumArea> PremiumAreas = await GetJsonAsync<List<Models.PremiumArea>>(CreateAuthorizationPolicyUrl($"{Apiurl}?moduleid={ModuleId}", EntityNames.Module, ModuleId), Enumerable.Empty<Models.PremiumArea>().ToList());
return PremiumAreas.OrderBy(item => item.Name).ToList();
}
public async Task<Models.PremiumArea> GetPremiumAreaAsync(int PremiumAreaId, int ModuleId)
{
return await GetJsonAsync<Models.PremiumArea>(CreateAuthorizationPolicyUrl($"{Apiurl}/{PremiumAreaId}/{ModuleId}", EntityNames.Module, ModuleId));
}
public async Task<Models.PremiumArea> AddPremiumAreaAsync(Models.PremiumArea PremiumArea)
{
return await PostJsonAsync<Models.PremiumArea>(CreateAuthorizationPolicyUrl($"{Apiurl}", EntityNames.Module, PremiumArea.ModuleId), PremiumArea);
}
public async Task<Models.PremiumArea> UpdatePremiumAreaAsync(Models.PremiumArea PremiumArea)
{
return await PutJsonAsync<Models.PremiumArea>(CreateAuthorizationPolicyUrl($"{Apiurl}/{PremiumArea.PremiumAreaId}", EntityNames.Module, PremiumArea.ModuleId), PremiumArea);
}
public async Task DeletePremiumAreaAsync(int PremiumAreaId, int ModuleId)
{
await DeleteAsync(CreateAuthorizationPolicyUrl($"{Apiurl}/{PremiumAreaId}/{ModuleId}", EntityNames.Module, ModuleId));
}
}
}

View File

@@ -0,0 +1,18 @@
using Microsoft.Extensions.DependencyInjection;
using System.Linq;
using Oqtane.Services;
using SZUAbsolventenverein.Module.PremiumArea.Services;
namespace SZUAbsolventenverein.Module.PremiumArea.Startup
{
public class ClientStartup : IClientStartup
{
public void ConfigureServices(IServiceCollection services)
{
if (!services.Any(s => s.ServiceType == typeof(IPremiumAreaService)))
{
services.AddScoped<IPremiumAreaService, PremiumAreaService>();
}
}
}
}

24
Client/_Imports.razor Normal file
View File

@@ -0,0 +1,24 @@
@using System
@using System.Linq
@using System.Collections.Generic
@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.Extensions.Localization
@using Microsoft.JSInterop
@using Oqtane.Models
@using Oqtane.Modules
@using Oqtane.Modules.Controls
@using Oqtane.Providers
@using Oqtane.Security
@using Oqtane.Services
@using Oqtane.Shared
@using Oqtane.Themes
@using Oqtane.Themes.Controls
@using Oqtane.UI
@using Oqtane.Enums
@using Oqtane.Interfaces

View File

@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<AccelerateBuildsInVisualStudio>false</AccelerateBuildsInVisualStudio>
</PropertyGroup>
<ItemGroup>
<None Include="icon.png">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Client\SZUAbsolventenverein.Module.PremiumArea.Client.csproj" />
<ProjectReference Include="..\Server\SZUAbsolventenverein.Module.PremiumArea.Server.csproj" />
<ProjectReference Include="..\Shared\SZUAbsolventenverein.Module.PremiumArea.Shared.csproj" />
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Condition="'$(OS)' == 'Windows_NT' And '$(Configuration)' == 'Debug'" Command="debug.cmd $(TargetFramework) $([System.String]::Copy('$(MSBuildProjectName)').Replace('.Package',''))" />
<Exec Condition="'$(OS)' != 'Windows_NT' And '$(Configuration)' == 'Debug'" Command="bash $(ProjectDir)debug.sh $(TargetFramework) $([System.String]::Copy('$(MSBuildProjectName)').Replace('.Package',''))" />
<Exec Condition="'$(OS)' == 'Windows_NT' And '$(Configuration)' == 'Release'" Command="release.cmd $(TargetFramework) $([System.String]::Copy('$(MSBuildProjectName)').Replace('.Package',''))" />
<Exec Condition="'$(OS)' != 'Windows_NT' And '$(Configuration)' == 'Release'" Command="bash $(ProjectDir)release.sh $(TargetFramework) $([System.String]::Copy('$(MSBuildProjectName)').Replace('.Package',''))" />
</Target>
</Project>

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>$projectname$</id>
<version>1.0.0</version>
<authors>SZUAbsolventenverein</authors>
<owners>SZUAbsolventenverein</owners>
<title>PremiumArea</title>
<description>This module adds a premium member system to Octane. Users receive premium status after completing a payment. Premium members get access to exclusive features and content.</description>
<copyright>SZUAbsolventenverein</copyright>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license>
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<icon>icon.png</icon>
<tags>oqtane module</tags>
<releaseNotes></releaseNotes>
<summary></summary>
<packageTypes>
<packageType name="Dependency" />
<packageType name="Oqtane.Framework" version="10.0.3" />
</packageTypes>
</metadata>
<files>
<file src="..\Client\bin\Release\$targetframework$\$ProjectName$.Client.Oqtane.dll" target="lib\$targetframework$" />
<file src="..\Client\bin\Release\$targetframework$\$ProjectName$.Client.Oqtane.pdb" target="lib\$targetframework$" />
<file src="..\Server\bin\Release\$targetframework$\$ProjectName$.Server.Oqtane.dll" target="lib\$targetframework$" />
<file src="..\Server\bin\Release\$targetframework$\$ProjectName$.Server.Oqtane.pdb" target="lib\$targetframework$" />
<file src="..\Shared\bin\Release\$targetframework$\$ProjectName$.Shared.Oqtane.dll" target="lib\$targetframework$" />
<file src="..\Shared\bin\Release\$targetframework$\$ProjectName$.Shared.Oqtane.pdb" target="lib\$targetframework$" />
<file src="..\Server\obj\Release\$targetframework$\staticwebassets\msbuild.$ProjectName$.Microsoft.AspNetCore.StaticWebAssetEndpoints.props" target="build\Microsoft.AspNetCore.StaticWebAssetEndpoints.props" />
<file src="..\Server\obj\Release\$targetframework$\staticwebassets\msbuild.$ProjectName$.Microsoft.AspNetCore.StaticWebAssets.props" target="build\Microsoft.AspNetCore.StaticWebAssets.props" />
<file src="..\Server\obj\Release\$targetframework$\staticwebassets\msbuild.build.$ProjectName$.props" target="build\$ProjectName$.props" />
<file src="..\Server\obj\Release\$targetframework$\staticwebassets\msbuild.buildMultiTargeting.$ProjectName$.props" target="buildMultiTargeting\$ProjectName$.props" />
<file src="..\Server\obj\Release\$targetframework$\staticwebassets\msbuild.buildTransitive.$ProjectName$.props" target="buildTransitive\$ProjectName$.props" />
<file src="..\Server\wwwroot\**\*.*" target="staticwebassets" />
<file src="icon.png" target="" />
</files>
</package>

11
Package/debug.cmd Normal file
View File

@@ -0,0 +1,11 @@
@echo off
set TargetFramework=%1
set ProjectName=%2
XCOPY "..\Client\bin\Debug\%TargetFramework%\%ProjectName%.Client.Oqtane.dll" "..\..\oqtane.framework\Oqtane.Server\bin\Debug\%TargetFramework%\" /Y
XCOPY "..\Client\bin\Debug\%TargetFramework%\%ProjectName%.Client.Oqtane.pdb" "..\..\oqtane.framework\Oqtane.Server\bin\Debug\%TargetFramework%\" /Y
XCOPY "..\Server\bin\Debug\%TargetFramework%\%ProjectName%.Server.Oqtane.dll" "..\..\oqtane.framework\Oqtane.Server\bin\Debug\%TargetFramework%\" /Y
XCOPY "..\Server\bin\Debug\%TargetFramework%\%ProjectName%.Server.Oqtane.pdb" "..\..\oqtane.framework\Oqtane.Server\bin\Debug\%TargetFramework%\" /Y
XCOPY "..\Shared\bin\Debug\%TargetFramework%\%ProjectName%.Shared.Oqtane.dll" "..\..\oqtane.framework\Oqtane.Server\bin\Debug\%TargetFramework%\" /Y
XCOPY "..\Shared\bin\Debug\%TargetFramework%\%ProjectName%.Shared.Oqtane.pdb" "..\..\oqtane.framework\Oqtane.Server\bin\Debug\%TargetFramework%\" /Y
XCOPY "..\Server\wwwroot\*" "..\..\oqtane.framework\Oqtane.Server\wwwroot\_content\%ProjectName%\" /Y /S /I

12
Package/debug.sh Normal file
View File

@@ -0,0 +1,12 @@
#!/bin/bash
TargetFramework=$1
ProjectName=$2
cp -f "../Client/bin/Debug/$TargetFramework/$ProjectName$.Client.Oqtane.dll" "../../oqtane.framework/Oqtane.Server/bin/Debug/$TargetFramework/"
cp -f "../Client/bin/Debug/$TargetFramework/$ProjectName$.Client.Oqtane.pdb" "../../oqtane.framework/Oqtane.Server/bin/Debug/$TargetFramework/"
cp -f "../Server/bin/Debug/$TargetFramework/$ProjectName$.Server.Oqtane.dll" "../../oqtane.framework/Oqtane.Server/bin/Debug/$TargetFramework/"
cp -f "../Server/bin/Debug/$TargetFramework/$ProjectName$.Server.Oqtane.pdb" "../../oqtane.framework/Oqtane.Server/bin/Debug/$TargetFramework/"
cp -f "../Shared/bin/Debug/$TargetFramework/$ProjectName$.Shared.Oqtane.dll" "../../oqtane.framework/Oqtane.Server/bin/Debug/$TargetFramework/"
cp -f "../Shared/bin/Debug/$TargetFramework/$ProjectName$.Shared.Oqtane.pdb" "../../oqtane.framework/Oqtane.Server/bin/Debug/$TargetFramework/"
cp -rf "../Server/wwwroot/"* "../../oqtane.framework/Oqtane.Server/wwwroot/_content/%ProjectName%/"

BIN
Package/icon.png Normal file

Binary file not shown.

8
Package/release.cmd Normal file
View File

@@ -0,0 +1,8 @@
@echo off
set TargetFramework=%1
set ProjectName=%2
del "*.nupkg"
"..\..\oqtane.framework\oqtane.package\FixProps.exe"
"..\..\oqtane.framework\oqtane.package\nuget.exe" pack %ProjectName%.nuspec -Properties targetframework=%TargetFramework%;projectname=%ProjectName%
XCOPY "*.nupkg" "..\..\oqtane.framework\Oqtane.Server\Packages\" /Y

7
Package/release.sh Normal file
View File

@@ -0,0 +1,7 @@
TargetFramework=$1
ProjectName=$2
find . -name "*.nupkg" -delete
"..\..\oqtane.framework\oqtane.package\FixProps.exe"
"..\..\oqtane.framework\oqtane.package\nuget.exe" pack %ProjectName%.nuspec -Properties targetframework=%TargetFramework%;projectname=%ProjectName%
cp -f "*.nupkg" "..\..\oqtane.framework\Oqtane.Server\Packages\"

View File

@@ -0,0 +1,7 @@
<Solution>
<Project Path="..\oqtane.framework\Oqtane.Server\Oqtane.Server.csproj" DefaultStartup="true" />
<Project Path="Client\SZUAbsolventenverein.Module.PremiumArea.Client.csproj" />
<Project Path="Server\SZUAbsolventenverein.Module.PremiumArea.Server.csproj" />
<Project Path="Shared\SZUAbsolventenverein.Module.PremiumArea.Shared.csproj" />
<Project Path="Package\SZUAbsolventenverein.Module.PremiumArea.Package.csproj" />
</Solution>

4
Server/AssemblyInfo.cs Normal file
View File

@@ -0,0 +1,4 @@
using System.Resources;
using Microsoft.Extensions.Localization;
[assembly: RootNamespace("SZUAbsolventenverein.Module.PremiumArea.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.PremiumArea.Services;
using Oqtane.Controllers;
using System.Net;
using System.Threading.Tasks;
namespace SZUAbsolventenverein.Module.PremiumArea.Controllers
{
[Route(ControllerRoutes.ApiRoute)]
public class PremiumAreaController : ModuleControllerBase
{
private readonly IPremiumAreaService _PremiumAreaService;
public PremiumAreaController(IPremiumAreaService PremiumAreaService, ILogManager logger, IHttpContextAccessor accessor) : base(logger, accessor)
{
_PremiumAreaService = PremiumAreaService;
}
// GET: api/<controller>?moduleid=x
[HttpGet]
[Authorize(Policy = PolicyNames.ViewModule)]
public async Task<IEnumerable<Models.PremiumArea>> Get(string moduleid)
{
int ModuleId;
if (int.TryParse(moduleid, out ModuleId) && IsAuthorizedEntityId(EntityNames.Module, ModuleId))
{
return await _PremiumAreaService.GetPremiumAreasAsync(ModuleId);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized PremiumArea 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.PremiumArea> Get(int id, int moduleid)
{
Models.PremiumArea PremiumArea = await _PremiumAreaService.GetPremiumAreaAsync(id, moduleid);
if (PremiumArea != null && IsAuthorizedEntityId(EntityNames.Module, PremiumArea.ModuleId))
{
return PremiumArea;
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized PremiumArea Get Attempt {PremiumAreaId} {ModuleId}", id, moduleid);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
}
// POST api/<controller>
[HttpPost]
[Authorize(Policy = PolicyNames.EditModule)]
public async Task<Models.PremiumArea> Post([FromBody] Models.PremiumArea PremiumArea)
{
if (ModelState.IsValid && IsAuthorizedEntityId(EntityNames.Module, PremiumArea.ModuleId))
{
PremiumArea = await _PremiumAreaService.AddPremiumAreaAsync(PremiumArea);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized PremiumArea Post Attempt {PremiumArea}", PremiumArea);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
PremiumArea = null;
}
return PremiumArea;
}
// PUT api/<controller>/5
[HttpPut("{id}")]
[Authorize(Policy = PolicyNames.EditModule)]
public async Task<Models.PremiumArea> Put(int id, [FromBody] Models.PremiumArea PremiumArea)
{
if (ModelState.IsValid && PremiumArea.PremiumAreaId == id && IsAuthorizedEntityId(EntityNames.Module, PremiumArea.ModuleId))
{
PremiumArea = await _PremiumAreaService.UpdatePremiumAreaAsync(PremiumArea);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized PremiumArea Put Attempt {PremiumArea}", PremiumArea);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
PremiumArea = null;
}
return PremiumArea;
}
// DELETE api/<controller>/5
[HttpDelete("{id}/{moduleid}")]
[Authorize(Policy = PolicyNames.EditModule)]
public async Task Delete(int id, int moduleid)
{
Models.PremiumArea PremiumArea = await _PremiumAreaService.GetPremiumAreaAsync(id, moduleid);
if (PremiumArea != null && IsAuthorizedEntityId(EntityNames.Module, PremiumArea.ModuleId))
{
await _PremiumAreaService.DeletePremiumAreaAsync(id, PremiumArea.ModuleId);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized PremiumArea Delete Attempt {PremiumAreaId} {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.PremiumArea.Repository;
using System.Threading.Tasks;
namespace SZUAbsolventenverein.Module.PremiumArea.Manager
{
public class PremiumAreaManager : MigratableModuleBase, IInstallable, IPortable, ISearchable
{
private readonly IPremiumAreaRepository _PremiumAreaRepository;
private readonly IDBContextDependencies _DBContextDependencies;
public PremiumAreaManager(IPremiumAreaRepository PremiumAreaRepository, IDBContextDependencies DBContextDependencies)
{
_PremiumAreaRepository = PremiumAreaRepository;
_DBContextDependencies = DBContextDependencies;
}
public bool Install(Tenant tenant, string version)
{
return Migrate(new PremiumAreaContext(_DBContextDependencies), tenant, MigrationType.Up);
}
public bool Uninstall(Tenant tenant)
{
return Migrate(new PremiumAreaContext(_DBContextDependencies), tenant, MigrationType.Down);
}
public string ExportModule(Oqtane.Models.Module module)
{
string content = "";
List<Models.PremiumArea> PremiumAreas = _PremiumAreaRepository.GetPremiumAreas(module.ModuleId).ToList();
if (PremiumAreas != null)
{
content = JsonSerializer.Serialize(PremiumAreas);
}
return content;
}
public void ImportModule(Oqtane.Models.Module module, string content, string version)
{
List<Models.PremiumArea> PremiumAreas = null;
if (!string.IsNullOrEmpty(content))
{
PremiumAreas = JsonSerializer.Deserialize<List<Models.PremiumArea>>(content);
}
if (PremiumAreas != null)
{
foreach(var PremiumArea in PremiumAreas)
{
_PremiumAreaRepository.AddPremiumArea(new Models.PremiumArea { ModuleId = module.ModuleId, Name = PremiumArea.Name });
}
}
}
public Task<List<SearchContent>> GetSearchContentsAsync(PageModule pageModule, DateTime lastIndexedOn)
{
var searchContentList = new List<SearchContent>();
foreach (var PremiumArea in _PremiumAreaRepository.GetPremiumAreas(pageModule.ModuleId))
{
if (PremiumArea.ModifiedOn >= lastIndexedOn)
{
searchContentList.Add(new SearchContent
{
EntityName = "SZUAbsolventenvereinPremiumArea",
EntityId = PremiumArea.PremiumAreaId.ToString(),
Title = PremiumArea.Name,
Body = PremiumArea.Name,
ContentModifiedBy = PremiumArea.ModifiedBy,
ContentModifiedOn = PremiumArea.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.PremiumArea.Migrations.EntityBuilders;
using SZUAbsolventenverein.Module.PremiumArea.Repository;
namespace SZUAbsolventenverein.Module.PremiumArea.Migrations
{
[DbContext(typeof(PremiumAreaContext))]
[Migration("SZUAbsolventenverein.Module.PremiumArea.01.00.00.00")]
public class InitializeModule : MultiDatabaseMigration
{
public InitializeModule(IDatabase database) : base(database)
{
}
protected override void Up(MigrationBuilder migrationBuilder)
{
var entityBuilder = new PremiumAreaEntityBuilder(migrationBuilder, ActiveDatabase);
entityBuilder.Create();
}
protected override void Down(MigrationBuilder migrationBuilder)
{
var entityBuilder = new PremiumAreaEntityBuilder(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.PremiumArea.Migrations.EntityBuilders
{
public class PremiumAreaEntityBuilder : AuditableBaseEntityBuilder<PremiumAreaEntityBuilder>
{
private const string _entityTableName = "SZUAbsolventenvereinPremiumArea";
private readonly PrimaryKey<PremiumAreaEntityBuilder> _primaryKey = new("PK_SZUAbsolventenvereinPremiumArea", x => x.PremiumAreaId);
private readonly ForeignKey<PremiumAreaEntityBuilder> _moduleForeignKey = new("FK_SZUAbsolventenvereinPremiumArea_Module", x => x.ModuleId, "Module", "ModuleId", ReferentialAction.Cascade);
public PremiumAreaEntityBuilder(MigrationBuilder migrationBuilder, IDatabase database) : base(migrationBuilder, database)
{
EntityTableName = _entityTableName;
PrimaryKey = _primaryKey;
ForeignKeys.Add(_moduleForeignKey);
}
protected override PremiumAreaEntityBuilder BuildTable(ColumnsBuilder table)
{
PremiumAreaId = AddAutoIncrementColumn(table,"PremiumAreaId");
ModuleId = AddIntegerColumn(table,"ModuleId");
Name = AddMaxStringColumn(table,"Name");
AddAuditableColumns(table);
return this;
}
public OperationBuilder<AddColumnOperation> PremiumAreaId { 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.PremiumArea.Repository
{
public class PremiumAreaContext : DBContextBase, ITransientService, IMultiDatabase
{
public virtual DbSet<Models.PremiumArea> PremiumArea { get; set; }
public PremiumAreaContext(IDBContextDependencies DBContextDependencies) : base(DBContextDependencies)
{
// ContextBase handles multi-tenant database connections
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Models.PremiumArea>().ToTable(ActiveDatabase.RewriteName("SZUAbsolventenvereinPremiumArea"));
}
}
}

View File

@@ -0,0 +1,75 @@
using Microsoft.EntityFrameworkCore;
using System.Linq;
using System.Collections.Generic;
using Oqtane.Modules;
namespace SZUAbsolventenverein.Module.PremiumArea.Repository
{
public interface IPremiumAreaRepository
{
IEnumerable<Models.PremiumArea> GetPremiumAreas(int ModuleId);
Models.PremiumArea GetPremiumArea(int PremiumAreaId);
Models.PremiumArea GetPremiumArea(int PremiumAreaId, bool tracking);
Models.PremiumArea AddPremiumArea(Models.PremiumArea PremiumArea);
Models.PremiumArea UpdatePremiumArea(Models.PremiumArea PremiumArea);
void DeletePremiumArea(int PremiumAreaId);
}
public class PremiumAreaRepository : IPremiumAreaRepository, ITransientService
{
private readonly IDbContextFactory<PremiumAreaContext> _factory;
public PremiumAreaRepository(IDbContextFactory<PremiumAreaContext> factory)
{
_factory = factory;
}
public IEnumerable<Models.PremiumArea> GetPremiumAreas(int ModuleId)
{
using var db = _factory.CreateDbContext();
return db.PremiumArea.Where(item => item.ModuleId == ModuleId).ToList();
}
public Models.PremiumArea GetPremiumArea(int PremiumAreaId)
{
return GetPremiumArea(PremiumAreaId, true);
}
public Models.PremiumArea GetPremiumArea(int PremiumAreaId, bool tracking)
{
using var db = _factory.CreateDbContext();
if (tracking)
{
return db.PremiumArea.Find(PremiumAreaId);
}
else
{
return db.PremiumArea.AsNoTracking().FirstOrDefault(item => item.PremiumAreaId == PremiumAreaId);
}
}
public Models.PremiumArea AddPremiumArea(Models.PremiumArea PremiumArea)
{
using var db = _factory.CreateDbContext();
db.PremiumArea.Add(PremiumArea);
db.SaveChanges();
return PremiumArea;
}
public Models.PremiumArea UpdatePremiumArea(Models.PremiumArea PremiumArea)
{
using var db = _factory.CreateDbContext();
db.Entry(PremiumArea).State = EntityState.Modified;
db.SaveChanges();
return PremiumArea;
}
public void DeletePremiumArea(int PremiumAreaId)
{
using var db = _factory.CreateDbContext();
Models.PremiumArea PremiumArea = db.PremiumArea.Find(PremiumAreaId);
db.PremiumArea.Remove(PremiumArea);
db.SaveChanges();
}
}
}

View File

@@ -0,0 +1,38 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
<Version>1.0.0</Version>
<Product>SZUAbsolventenverein.Module.PremiumArea</Product>
<Authors>SZUAbsolventenverein</Authors>
<Company>SZUAbsolventenverein</Company>
<Description>This module adds a premium member system to Octane. Users receive premium status after completing a payment. Premium members get access to exclusive features and content.</Description>
<Copyright>SZUAbsolventenverein</Copyright>
<AssemblyName>SZUAbsolventenverein.Module.PremiumArea.Server.Oqtane</AssemblyName>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<StaticWebAssetsFingerprintContent>false</StaticWebAssetsFingerprintContent>
</PropertyGroup>
<ItemGroup>
<Content Remove="wwwroot\_content\**\*.*" />
<None Include="wwwroot\_content\**\*.*" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="10.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="10.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.1" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="10.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Client\SZUAbsolventenverein.Module.PremiumArea.Client.csproj" />
<ProjectReference Include="..\Shared\SZUAbsolventenverein.Module.PremiumArea.Shared.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="Oqtane.Server"><HintPath>..\..\oqtane.framework\Oqtane.Server\bin\Debug\net10.0\Oqtane.Server.dll</HintPath></Reference>
<Reference Include="Oqtane.Shared"><HintPath>..\..\oqtane.framework\Oqtane.Server\bin\Debug\net10.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.PremiumArea.Repository;
namespace SZUAbsolventenverein.Module.PremiumArea.Services
{
public class ServerPremiumAreaService : IPremiumAreaService
{
private readonly IPremiumAreaRepository _PremiumAreaRepository;
private readonly IUserPermissions _userPermissions;
private readonly ILogManager _logger;
private readonly IHttpContextAccessor _accessor;
private readonly Alias _alias;
public ServerPremiumAreaService(IPremiumAreaRepository PremiumAreaRepository, IUserPermissions userPermissions, ITenantManager tenantManager, ILogManager logger, IHttpContextAccessor accessor)
{
_PremiumAreaRepository = PremiumAreaRepository;
_userPermissions = userPermissions;
_logger = logger;
_accessor = accessor;
_alias = tenantManager.GetAlias();
}
public Task<List<Models.PremiumArea>> GetPremiumAreasAsync(int ModuleId)
{
if (_userPermissions.IsAuthorized(_accessor.HttpContext.User, _alias.SiteId, EntityNames.Module, ModuleId, PermissionNames.View))
{
return Task.FromResult(_PremiumAreaRepository.GetPremiumAreas(ModuleId).ToList());
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized PremiumArea Get Attempt {ModuleId}", ModuleId);
return null;
}
}
public Task<Models.PremiumArea> GetPremiumAreaAsync(int PremiumAreaId, int ModuleId)
{
if (_userPermissions.IsAuthorized(_accessor.HttpContext.User, _alias.SiteId, EntityNames.Module, ModuleId, PermissionNames.View))
{
return Task.FromResult(_PremiumAreaRepository.GetPremiumArea(PremiumAreaId));
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized PremiumArea Get Attempt {PremiumAreaId} {ModuleId}", PremiumAreaId, ModuleId);
return null;
}
}
public Task<Models.PremiumArea> AddPremiumAreaAsync(Models.PremiumArea PremiumArea)
{
if (_userPermissions.IsAuthorized(_accessor.HttpContext.User, _alias.SiteId, EntityNames.Module, PremiumArea.ModuleId, PermissionNames.Edit))
{
PremiumArea = _PremiumAreaRepository.AddPremiumArea(PremiumArea);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "PremiumArea Added {PremiumArea}", PremiumArea);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized PremiumArea Add Attempt {PremiumArea}", PremiumArea);
PremiumArea = null;
}
return Task.FromResult(PremiumArea);
}
public Task<Models.PremiumArea> UpdatePremiumAreaAsync(Models.PremiumArea PremiumArea)
{
if (_userPermissions.IsAuthorized(_accessor.HttpContext.User, _alias.SiteId, EntityNames.Module, PremiumArea.ModuleId, PermissionNames.Edit))
{
PremiumArea = _PremiumAreaRepository.UpdatePremiumArea(PremiumArea);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "PremiumArea Updated {PremiumArea}", PremiumArea);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized PremiumArea Update Attempt {PremiumArea}", PremiumArea);
PremiumArea = null;
}
return Task.FromResult(PremiumArea);
}
public Task DeletePremiumAreaAsync(int PremiumAreaId, int ModuleId)
{
if (_userPermissions.IsAuthorized(_accessor.HttpContext.User, _alias.SiteId, EntityNames.Module, ModuleId, PermissionNames.Edit))
{
_PremiumAreaRepository.DeletePremiumArea(PremiumAreaId);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "PremiumArea Deleted {PremiumAreaId}", PremiumAreaId);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized PremiumArea Delete Attempt {PremiumAreaId} {ModuleId}", PremiumAreaId, 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.PremiumArea.Repository;
using SZUAbsolventenverein.Module.PremiumArea.Services;
namespace SZUAbsolventenverein.Module.PremiumArea.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<IPremiumAreaService, ServerPremiumAreaService>();
services.AddDbContextFactory<PremiumAreaContext>(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.PremiumArea = {
};

View File

@@ -0,0 +1,15 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Oqtane.Models;
namespace SZUAbsolventenverein.Module.PremiumArea.Models
{
[Table("SZUAbsolventenvereinPremiumArea")]
public class PremiumArea : ModelBase
{
[Key]
public int PremiumAreaId { get; set; }
public int ModuleId { get; set; }
public string Name { get; set; }
}
}

View File

@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Version>1.0.0</Version>
<Product>SZUAbsolventenverein.Module.PremiumArea</Product>
<Authors>SZUAbsolventenverein</Authors>
<Company>SZUAbsolventenverein</Company>
<Description>This module adds a premium member system to Octane. Users receive premium status after completing a payment. Premium members get access to exclusive features and content.</Description>
<Copyright>SZUAbsolventenverein</Copyright>
<AssemblyName>SZUAbsolventenverein.Module.PremiumArea.Shared.Oqtane</AssemblyName>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<Reference Include="Oqtane.Shared"><HintPath>..\..\oqtane.framework\Oqtane.Server\bin\Debug\net10.0\Oqtane.Shared.dll</HintPath></Reference>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,6 @@
{
"Title": "Default Module Template",
"Type": "External",
"Version": "10.0.0",
"Namespace": "SZUAbsolventenverein.Module.PremiumArea"
}