Html encode job log messages, add new IModule property to allow modules to specify Runtime support, provide feedback during module content import, remove default EditMode option at the Page level (should be implemented at Module level) - resolves issue where Admin modules could not be deleted, include link to Event Log in AddModuleMessage for Error message type, fixed fallback support for themes in siterouter, integrated auth policy into site templates for Module Creator

This commit is contained in:
Shaun Walker
2020-07-08 19:56:02 -04:00
parent 49f4e64cb4
commit f515def414
37 changed files with 220 additions and 165 deletions

View File

@ -50,7 +50,7 @@
if (PageState.Action == "Edit")
{
_id = Int32.Parse(PageState.QueryString["id"]);
[Module] [Module] = await [Module]Service.Get[Module]Async(_id);
[Module] [Module] = await [Module]Service.Get[Module]Async(_id, ModuleState.ModuleId);
if ([Module] != null)
{
_name = [Module].Name;
@ -82,7 +82,7 @@
}
else
{
[Module] [Module] = await [Module]Service.Get[Module]Async(_id);
[Module] [Module] = await [Module]Service.Get[Module]Async(_id, ModuleState.ModuleId);
[Module].Name = _name;
await [Module]Service.Update[Module]Async([Module]);
await logger.LogInformation("[Module] Updated {[Module]}", [Module]);

View File

@ -95,7 +95,7 @@ else
{
try
{
await [Module]Service.Delete[Module]Async([Module].[Module]Id);
await [Module]Service.Delete[Module]Async([Module].[Module]Id, ModuleState.ModuleId);
await logger.LogInformation("[Module] Deleted {[Module]}", [Module]);
_[Module]s = await [Module]Service.Get[Module]sAsync(ModuleState.ModuleId);
StateHasChanged();

View File

@ -8,12 +8,12 @@ namespace [Owner].[Module]s.Services
{
Task<List<[Module]>> Get[Module]sAsync(int ModuleId);
Task<[Module]> Get[Module]Async(int [Module]Id);
Task<[Module]> Get[Module]Async(int [Module]Id, int ModuleId);
Task<[Module]> Add[Module]Async([Module] [Module]);
Task<[Module]> Update[Module]Async([Module] [Module]);
Task Delete[Module]Async(int [Module]Id);
Task Delete[Module]Async(int [Module]Id, int ModuleId);
}
}

View File

@ -18,32 +18,44 @@ namespace [Owner].[Module]s.Services
_siteState = siteState;
}
private string Apiurl=> CreateApiUrl(_siteState.Alias, "[Module]");
private string Apiurl => CreateApiUrl(_siteState.Alias, "[Module]");
public async Task<List<[Module]>> Get[Module]sAsync(int ModuleId)
{
List<[Module]> [Module]s = await GetJsonAsync<List<[Module]>>($"{Apiurl}?moduleid={ModuleId}");
List<[Module]> [Module]s = await GetJsonAsync<List<[Module]>>(CreateAuthPolicyUrl($"{Apiurl}?moduleid={ModuleId}", ModuleId));
return [Module]s.OrderBy(item => item.Name).ToList();
}
public async Task<[Module]> Get[Module]Async(int [Module]Id)
public async Task<[Module]> Get[Module]Async(int [Module]Id, int ModuleId)
{
return await GetJsonAsync<[Module]>($"{Apiurl}/{[Module]Id}");
return await GetJsonAsync<[Module]>(CreateAuthPolicyUrl($"{Apiurl}/{[Module]Id}", ModuleId));
}
public async Task<[Module]> Add[Module]Async([Module] [Module])
{
return await PostJsonAsync<[Module]>($"{Apiurl}?entityid={[Module].ModuleId}", [Module]);
return await PostJsonAsync<[Module]>(CreateAuthPolicyUrl($"{Apiurl}?entityid={[Module].ModuleId}", [Module].ModuleId), [Module]);
}
public async Task<[Module]> Update[Module]Async([Module] [Module])
{
return await PutJsonAsync<[Module]>($"{Apiurl}/{[Module].[Module]Id}?entityid={[Module].ModuleId}", [Module]);
return await PutJsonAsync<[Module]>(CreateAuthPolicyUrl($"{Apiurl}/{[Module].[Module]Id}", [Module].ModuleId), [Module]);
}
public async Task Delete[Module]Async(int [Module]Id)
public async Task Delete[Module]Async(int [Module]Id, int ModuleId)
{
await DeleteAsync($"{Apiurl}/{[Module]Id}");
await DeleteAsync(CreateAuthPolicyUrl($"{Apiurl}/{[Module]Id}", ModuleId));
}
private string CreateAuthPolicyUrl(string Url, int ModuleId)
{
if (Url.Contains("?"))
{
return Url + "&entityid=" + ModuleId.ToString();
}
else
{
return Url + "?entityid=" + ModuleId.ToString();
}
}
}
}

View File

@ -1,6 +1,7 @@
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;
@ -14,16 +15,22 @@ namespace [Owner].[Module]s.Controllers
{
private readonly I[Module]Repository _[Module]s;
private readonly ILogManager _logger;
protected int _entityId = -1;
public [Module]Controller(I[Module]Repository [Module]s, ILogManager logger)
public [Module]Controller(I[Module]Repository [Module]s, ILogManager logger, IHttpContextAccessor accessor)
{
_[Module]s = [Module]s;
_logger = logger;
if (accessor.HttpContext.Request.Query.ContainsKey("entityid"))
{
_entityId = int.Parse(accessor.HttpContext.Request.Query["entityid"]);
}
}
// GET: api/<controller>?moduleid=x
[HttpGet]
[Authorize(Roles = Constants.RegisteredRole)]
[Authorize(Policy = "ViewModule")]
public IEnumerable<[Module]> Get(string moduleid)
{
return _[Module]s.Get[Module]s(int.Parse(moduleid));
@ -31,18 +38,23 @@ namespace [Owner].[Module]s.Controllers
// GET api/<controller>/5
[HttpGet("{id}")]
[Authorize(Roles = Constants.RegisteredRole)]
[Authorize(Policy = "ViewModule")]
public [Module] Get(int id)
{
return _[Module]s.Get[Module](id);
[Module] [Module] = _[Module]s.Get[Module](id);
if ([Module] != null && [Module].ModuleId != _entityId)
{
[Module] = null;
}
return [Module];
}
// POST api/<controller>
[HttpPost]
[Authorize(Roles = Constants.AdminRole)]
[Authorize(Policy = "EditModule")]
public [Module] Post([FromBody] [Module] [Module])
{
if (ModelState.IsValid)
if (ModelState.IsValid && [Module].ModuleId == _entityId)
{
[Module] = _[Module]s.Add[Module]([Module]);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "[Module] Added {[Module]}", [Module]);
@ -52,10 +64,10 @@ namespace [Owner].[Module]s.Controllers
// PUT api/<controller>/5
[HttpPut("{id}")]
[Authorize(Roles = Constants.AdminRole)]
[Authorize(Policy = "EditModule")]
public [Module] Put(int id, [FromBody] [Module] [Module])
{
if (ModelState.IsValid)
if (ModelState.IsValid && [Module].ModuleId == _entityId)
{
[Module] = _[Module]s.Update[Module]([Module]);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "[Module] Updated {[Module]}", [Module]);
@ -65,11 +77,15 @@ namespace [Owner].[Module]s.Controllers
// DELETE api/<controller>/5
[HttpDelete("{id}")]
[Authorize(Roles = Constants.AdminRole)]
[Authorize(Policy = "EditModule")]
public void Delete(int id)
{
_[Module]s.Delete[Module](id);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "[Module] Deleted {[Module]Id}", id);
[Module] [Module] = _[Module]s.Get[Module](id);
if ([Module] != null && [Module].ModuleId == _entityId)
{
_[Module]s.Delete[Module](id);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "[Module] Deleted {[Module]Id}", id);
}
}
}
}

View File

@ -1 +0,0 @@
This is the location where static resources such as images, style sheets, or scripts for this module will be located. Static assets can be organized in subfolders. When the module package is deployed the assets will be extracted under the web root in a folder that matches the module namespace.

View File

@ -28,8 +28,14 @@
@code {
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
public override string Actions => "Add,Edit";
public override List<Resource> Resources => new List<Resource>()
{
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" }
};
int _id;
string _name;
string _createdby;
@ -44,7 +50,7 @@
if (PageState.Action == "Edit")
{
_id = Int32.Parse(PageState.QueryString["id"]);
[Module] [Module] = await [Module]Service.Get[Module]Async(_id);
[Module] [Module] = await [Module]Service.Get[Module]Async(_id, ModuleState.ModuleId);
if ([Module] != null)
{
_name = [Module].Name;
@ -76,7 +82,7 @@
}
else
{
[Module] [Module] = await [Module]Service.Get[Module]Async(_id);
[Module] [Module] = await [Module]Service.Get[Module]Async(_id, ModuleState.ModuleId);
[Module].Name = _name;
await [Module]Service.Update[Module]Async([Module]);
await logger.LogInformation("[Module] Updated {[Module]}", [Module]);

View File

@ -61,6 +61,11 @@ else
<!-- The content above is for informational purposes only and can be safely removed -->
@code {
public override List<Resource> Resources => new List<Resource>()
{
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" }
};
List<[Module]> _[Module]s;
protected override async Task OnInitializedAsync()
@ -80,7 +85,7 @@ else
{
try
{
await [Module]Service.Delete[Module]Async([Module].[Module]Id);
await [Module]Service.Delete[Module]Async([Module].[Module]Id, ModuleState.ModuleId);
await logger.LogInformation("[Module] Deleted {[Module]}", [Module]);
_[Module]s = await [Module]Service.Get[Module]sAsync(ModuleState.ModuleId);
StateHasChanged();

View File

@ -8,12 +8,12 @@ namespace [Owner].[Module]s.Services
{
Task<List<[Module]>> Get[Module]sAsync(int ModuleId);
Task<[Module]> Get[Module]Async(int [Module]Id);
Task<[Module]> Get[Module]Async(int [Module]Id, int ModuleId);
Task<[Module]> Add[Module]Async([Module] [Module]);
Task<[Module]> Update[Module]Async([Module] [Module]);
Task Delete[Module]Async(int [Module]Id);
Task Delete[Module]Async(int [Module]Id, int ModuleId);
}
}

View File

@ -18,32 +18,44 @@ namespace [Owner].[Module]s.Services
_siteState = siteState;
}
private string Apiurl=> CreateApiUrl(_siteState.Alias, "[Module]");
private string Apiurl => CreateApiUrl(_siteState.Alias, "[Module]");
public async Task<List<[Module]>> Get[Module]sAsync(int ModuleId)
{
List<[Module]> [Module]s = await GetJsonAsync<List<[Module]>>($"{Apiurl}?moduleid={ModuleId}");
List<[Module]> [Module]s = await GetJsonAsync<List<[Module]>>(CreateAuthPolicyUrl($"{Apiurl}?moduleid={ModuleId}", ModuleId));
return [Module]s.OrderBy(item => item.Name).ToList();
}
public async Task<[Module]> Get[Module]Async(int [Module]Id)
public async Task<[Module]> Get[Module]Async(int [Module]Id, int ModuleId)
{
return await GetJsonAsync<[Module]>($"{Apiurl}/{[Module]Id}");
return await GetJsonAsync<[Module]>(CreateAuthPolicyUrl($"{Apiurl}/{[Module]Id}", ModuleId));
}
public async Task<[Module]> Add[Module]Async([Module] [Module])
{
return await PostJsonAsync<[Module]>($"{Apiurl}?entityid={[Module].ModuleId}", [Module]);
return await PostJsonAsync<[Module]>(CreateAuthPolicyUrl($"{Apiurl}?entityid={[Module].ModuleId}", [Module].ModuleId), [Module]);
}
public async Task<[Module]> Update[Module]Async([Module] [Module])
{
return await PutJsonAsync<[Module]>($"{Apiurl}/{[Module].[Module]Id}?entityid={[Module].ModuleId}", [Module]);
return await PutJsonAsync<[Module]>(CreateAuthPolicyUrl($"{Apiurl}/{[Module].[Module]Id}", [Module].ModuleId), [Module]);
}
public async Task Delete[Module]Async(int [Module]Id)
public async Task Delete[Module]Async(int [Module]Id, int ModuleId)
{
await DeleteAsync($"{Apiurl}/{[Module]Id}");
await DeleteAsync(CreateAuthPolicyUrl($"{Apiurl}/{[Module]Id}", ModuleId));
}
private string CreateAuthPolicyUrl(string Url, int ModuleId)
{
if (Url.Contains("?"))
{
return Url + "&entityid=" + ModuleId.ToString();
}
else
{
return Url + "?entityid=" + ModuleId.ToString();
}
}
}
}

View File

@ -1,6 +1,7 @@
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;
@ -14,16 +15,22 @@ namespace [Owner].[Module]s.Controllers
{
private readonly I[Module]Repository _[Module]s;
private readonly ILogManager _logger;
protected int _entityId = -1;
public [Module]Controller(I[Module]Repository [Module]s, ILogManager logger)
public [Module]Controller(I[Module]Repository [Module]s, ILogManager logger, IHttpContextAccessor accessor)
{
_[Module]s = [Module]s;
_logger = logger;
if (accessor.HttpContext.Request.Query.ContainsKey("entityid"))
{
_entityId = int.Parse(accessor.HttpContext.Request.Query["entityid"]);
}
}
// GET: api/<controller>?moduleid=x
[HttpGet]
[Authorize(Roles = Constants.RegisteredRole)]
[Authorize(Policy = "ViewModule")]
public IEnumerable<[Module]> Get(string moduleid)
{
return _[Module]s.Get[Module]s(int.Parse(moduleid));
@ -31,18 +38,23 @@ namespace [Owner].[Module]s.Controllers
// GET api/<controller>/5
[HttpGet("{id}")]
[Authorize(Roles = Constants.RegisteredRole)]
[Authorize(Policy = "ViewModule")]
public [Module] Get(int id)
{
return _[Module]s.Get[Module](id);
[Module] [Module] = _[Module]s.Get[Module](id);
if ([Module] != null && [Module].ModuleId != _entityId)
{
[Module] = null;
}
return [Module];
}
// POST api/<controller>
[HttpPost]
[Authorize(Roles = Constants.AdminRole)]
[Authorize(Policy = "EditModule")]
public [Module] Post([FromBody] [Module] [Module])
{
if (ModelState.IsValid)
if (ModelState.IsValid && [Module].ModuleId == _entityId)
{
[Module] = _[Module]s.Add[Module]([Module]);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "[Module] Added {[Module]}", [Module]);
@ -52,10 +64,10 @@ namespace [Owner].[Module]s.Controllers
// PUT api/<controller>/5
[HttpPut("{id}")]
[Authorize(Roles = Constants.AdminRole)]
[Authorize(Policy = "EditModule")]
public [Module] Put(int id, [FromBody] [Module] [Module])
{
if (ModelState.IsValid)
if (ModelState.IsValid && [Module].ModuleId == _entityId)
{
[Module] = _[Module]s.Update[Module]([Module]);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "[Module] Updated {[Module]}", [Module]);
@ -65,11 +77,15 @@ namespace [Owner].[Module]s.Controllers
// DELETE api/<controller>/5
[HttpDelete("{id}")]
[Authorize(Roles = Constants.AdminRole)]
[Authorize(Policy = "EditModule")]
public void Delete(int id)
{
_[Module]s.Delete[Module](id);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "[Module] Deleted {[Module]Id}", id);
[Module] [Module] = _[Module]s.Get[Module](id);
if ([Module] != null && [Module].ModuleId == _entityId)
{
_[Module]s.Delete[Module](id);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "[Module] Deleted {[Module]Id}", id);
}
}
}
}

View File

@ -1 +0,0 @@
This is the location where static resources such as images, style sheets, or scripts for this module will be located. Static assets can be organized in subfolders. When the module package is deployed the assets will be extracted under the web root in a folder that matches the module namespace.