Merge pull request #1488 from sbwalker/dev

improvements to refresh logic, module template enhancements
This commit is contained in:
Shaun Walker 2021-06-16 16:26:53 -04:00 committed by GitHub
commit 31c412a886
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 540 additions and 64 deletions

View File

@ -142,7 +142,7 @@
await logger.LogInformation("Login Successful For Username {Username}", _username); await logger.LogInformation("Login Successful For Username {Username}", _username);
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider)); var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider));
authstateprovider.NotifyAuthenticationChanged(); authstateprovider.NotifyAuthenticationChanged();
NavigationManager.NavigateTo(NavigateUrl(_returnUrl, "reload")); NavigationManager.NavigateTo(NavigateUrl(_returnUrl, true));
} }
else else
{ {

View File

@ -103,7 +103,7 @@ else
{ {
await ModuleDefinitionService.DeleteModuleDefinitionAsync(moduleDefinition.ModuleDefinitionId, moduleDefinition.SiteId); await ModuleDefinitionService.DeleteModuleDefinitionAsync(moduleDefinition.ModuleDefinitionId, moduleDefinition.SiteId);
AddModuleMessage(Localizer["Module Deleted Successfully"], MessageType.Success); AddModuleMessage(Localizer["Module Deleted Successfully"], MessageType.Success);
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "reload")); NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, true));
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -51,11 +51,11 @@ else
private void Edit(string name) private void Edit(string name)
{ {
NavigationManager.NavigateTo(_scheme + name + "/admin/site/?redirect"); NavigationManager.NavigateTo(_scheme + name + "/admin/site/?reload");
} }
private void Browse(string name) private void Browse(string name)
{ {
NavigationManager.NavigateTo(_scheme + name + "/?redirect", true); NavigationManager.NavigateTo(_scheme + name + "/?reload");
} }
} }

View File

@ -104,7 +104,7 @@ else
{ {
await ThemeService.DeleteThemeAsync(Theme.ThemeName); await ThemeService.DeleteThemeAsync(Theme.ThemeName);
AddModuleMessage(Localizer["Theme Deleted Successfully"], MessageType.Success); AddModuleMessage(Localizer["Theme Deleted Successfully"], MessageType.Success);
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "reload")); NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, true));
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -84,11 +84,21 @@ namespace Oqtane.Modules
return NavigateUrl(path, ""); return NavigateUrl(path, "");
} }
public string NavigateUrl(bool refresh)
{
return NavigateUrl(PageState.Page.Path, refresh);
}
public string NavigateUrl(string path, string parameters) public string NavigateUrl(string path, string parameters)
{ {
return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters); return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters);
} }
public string NavigateUrl(string path, bool refresh)
{
return Utilities.NavigateUrl(PageState.Alias.Path, path, refresh ? "refresh" : "");
}
public string EditUrl(string action) public string EditUrl(string action)
{ {
return EditUrl(ModuleState.ModuleId, action); return EditUrl(ModuleState.ModuleId, action);

View File

@ -115,7 +115,7 @@ namespace Oqtane.Themes.Controls
await PageModuleService.UpdatePageModuleAsync(pagemodule); await PageModuleService.UpdatePageModuleAsync(pagemodule);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, oldPane); await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, oldPane);
return NavigateUrl(url, "reload"); return NavigateUrl(url, true);
} }
private async Task<string> DeleteModule(string url, PageModule pagemodule) private async Task<string> DeleteModule(string url, PageModule pagemodule)
@ -123,7 +123,7 @@ namespace Oqtane.Themes.Controls
pagemodule.IsDeleted = true; pagemodule.IsDeleted = true;
await PageModuleService.UpdatePageModuleAsync(pagemodule); await PageModuleService.UpdatePageModuleAsync(pagemodule);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
return NavigateUrl(url, "reload"); return NavigateUrl(url, true);
} }
private async Task<string> Settings(string url, PageModule pagemodule) private async Task<string> Settings(string url, PageModule pagemodule)
@ -148,7 +148,7 @@ namespace Oqtane.Themes.Controls
} }
pagemodule.Module.Permissions = UserSecurity.SetPermissionStrings(permissions); pagemodule.Module.Permissions = UserSecurity.SetPermissionStrings(permissions);
await ModuleService.UpdateModuleAsync(pagemodule.Module); await ModuleService.UpdateModuleAsync(pagemodule.Module);
return NavigateUrl(s, "reload"); return NavigateUrl(s, true);
} }
private async Task<string> Unpublish(string s, PageModule pagemodule) private async Task<string> Unpublish(string s, PageModule pagemodule)
@ -166,7 +166,7 @@ namespace Oqtane.Themes.Controls
} }
pagemodule.Module.Permissions = UserSecurity.SetPermissionStrings(permissions); pagemodule.Module.Permissions = UserSecurity.SetPermissionStrings(permissions);
await ModuleService.UpdateModuleAsync(pagemodule.Module); await ModuleService.UpdateModuleAsync(pagemodule.Module);
return NavigateUrl(s, "reload"); return NavigateUrl(s, true);
} }
private async Task<string> MoveTop(string s, PageModule pagemodule) private async Task<string> MoveTop(string s, PageModule pagemodule)
@ -174,7 +174,7 @@ namespace Oqtane.Themes.Controls
pagemodule.Order = 0; pagemodule.Order = 0;
await PageModuleService.UpdatePageModuleAsync(pagemodule); await PageModuleService.UpdatePageModuleAsync(pagemodule);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
return NavigateUrl(s, "reload"); return NavigateUrl(s, true);
} }
private async Task<string> MoveBottom(string s, PageModule pagemodule) private async Task<string> MoveBottom(string s, PageModule pagemodule)
@ -182,7 +182,7 @@ namespace Oqtane.Themes.Controls
pagemodule.Order = int.MaxValue; pagemodule.Order = int.MaxValue;
await PageModuleService.UpdatePageModuleAsync(pagemodule); await PageModuleService.UpdatePageModuleAsync(pagemodule);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
return NavigateUrl(s, "reload"); return NavigateUrl(s, true);
} }
private async Task<string> MoveUp(string s, PageModule pagemodule) private async Task<string> MoveUp(string s, PageModule pagemodule)
@ -190,7 +190,7 @@ namespace Oqtane.Themes.Controls
pagemodule.Order -= 3; pagemodule.Order -= 3;
await PageModuleService.UpdatePageModuleAsync(pagemodule); await PageModuleService.UpdatePageModuleAsync(pagemodule);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
return NavigateUrl(s, "reload"); return NavigateUrl(s, true);
} }
private async Task<string> MoveDown(string s, PageModule pagemodule) private async Task<string> MoveDown(string s, PageModule pagemodule)
@ -198,7 +198,7 @@ namespace Oqtane.Themes.Controls
pagemodule.Order += 3; pagemodule.Order += 3;
await PageModuleService.UpdatePageModuleAsync(pagemodule); await PageModuleService.UpdatePageModuleAsync(pagemodule);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
return NavigateUrl(s, "reload"); return NavigateUrl(s, true);
} }
public class ActionViewModel public class ActionViewModel

View File

@ -537,7 +537,7 @@
} }
page.Permissions = UserSecurity.SetPermissionStrings(permissions); page.Permissions = UserSecurity.SetPermissionStrings(permissions);
await PageService.UpdatePageAsync(page); await PageService.UpdatePageAsync(page);
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "reload")); NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, true));
} }
} }

View File

@ -47,7 +47,7 @@ namespace Oqtane.Themes.Controls
// client-side Blazor // client-side Blazor
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider)); var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider));
authstateprovider.NotifyAuthenticationChanged(); authstateprovider.NotifyAuthenticationChanged();
NavigationManager.NavigateTo(NavigateUrl(!authorizedtoviewpage ? PageState.Alias.Path : PageState.Page.Path, "reload")); NavigationManager.NavigateTo(NavigateUrl(!authorizedtoviewpage ? PageState.Alias.Path : PageState.Page.Path, true));
} }
} }
} }

View File

@ -64,6 +64,16 @@ namespace Oqtane.Themes
return NavigateUrl(path, ""); return NavigateUrl(path, "");
} }
public string NavigateUrl(bool refresh)
{
return NavigateUrl(PageState.Page.Path, refresh);
}
public string NavigateUrl(string path, bool refresh)
{
return Utilities.NavigateUrl(PageState.Alias.Path, path, refresh ? "refresh" : "");
}
public string NavigateUrl(string path, string parameters) public string NavigateUrl(string path, string parameters)
{ {
return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters); return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters);

View File

@ -1,6 +1,6 @@
namespace Oqtane.UI namespace Oqtane.UI
{ {
public enum Reload public enum Refresh
{ {
None, None,
Page, Page,

View File

@ -77,7 +77,7 @@
var action = Constants.DefaultAction; var action = Constants.DefaultAction;
var urlparameters = string.Empty; var urlparameters = string.Empty;
var editmode = false; var editmode = false;
var reload = Reload.None; var refresh = UI.Refresh.None;
var lastsyncdate = DateTime.UtcNow.AddHours(-1); var lastsyncdate = DateTime.UtcNow.AddHours(-1);
var runtime = GetRuntime(); var runtime = GetRuntime();
@ -89,17 +89,17 @@
// parse querystring // parse querystring
var querystring = ParseQueryString(uri.Query); var querystring = ParseQueryString(uri.Query);
// the reload parameter is used to reload the PageState // the refresh parameter is used to refresh the PageState
if (querystring.ContainsKey("reload")) if (querystring.ContainsKey("refresh"))
{ {
reload = Reload.Site; refresh = UI.Refresh.Site;
} }
else else
{ {
// reload the client application if the user navigated to a site with a different alias or there is a forced redirect // reload the client application if the user navigated to a site with a different alias or there is a forced reload
if ((!path.StartsWith(SiteState.Alias.Path) && SiteState.Alias.Path != "") || querystring.ContainsKey("redirect")) if ((!path.StartsWith(SiteState.Alias.Path) && SiteState.Alias.Path != "") || querystring.ContainsKey("reload"))
{ {
NavigationManager.NavigateTo(_absoluteUri.Replace("?redirect", ""), true); NavigationManager.NavigateTo(_absoluteUri.Replace("?reload", ""), true);
return; return;
} }
} }
@ -113,7 +113,7 @@
// process any sync events // process any sync events
var sync = await SyncService.GetSyncAsync(lastsyncdate); var sync = await SyncService.GetSyncAsync(lastsyncdate);
lastsyncdate = sync.SyncDate; lastsyncdate = sync.SyncDate;
if (reload != Reload.Site && sync.SyncEvents.Any()) if (refresh != UI.Refresh.Site && sync.SyncEvents.Any())
{ {
// if running on WebAssembly reload the client application if the server application was restarted // if running on WebAssembly reload the client application if the server application was restarted
if (runtime == Shared.Runtime.WebAssembly && PageState != null && sync.SyncEvents.Exists(item => item.TenantId == -1)) if (runtime == Shared.Runtime.WebAssembly && PageState != null && sync.SyncEvents.Exists(item => item.TenantId == -1))
@ -123,14 +123,14 @@
} }
if (sync.SyncEvents.Exists(item => item.EntityName == EntityNames.Site && item.EntityId == SiteState.Alias.SiteId)) if (sync.SyncEvents.Exists(item => item.EntityName == EntityNames.Site && item.EntityId == SiteState.Alias.SiteId))
{ {
reload = Reload.Site; refresh = UI.Refresh.Site;
} }
} }
if (reload == Reload.Site || PageState == null || PageState.Alias.SiteId != SiteState.Alias.SiteId) if (refresh == UI.Refresh.Site || PageState == null || PageState.Alias.SiteId != SiteState.Alias.SiteId)
{ {
site = await SiteService.GetSiteAsync(SiteState.Alias.SiteId); site = await SiteService.GetSiteAsync(SiteState.Alias.SiteId);
reload = Reload.Site; refresh = UI.Refresh.Site;
} }
else else
{ {
@ -139,7 +139,7 @@
if (site != null) if (site != null)
{ {
if (PageState == null || reload == Reload.Site) if (PageState == null || refresh == UI.Refresh.Site)
{ {
// get user // get user
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
@ -154,15 +154,15 @@
} }
// process any sync events for user // process any sync events for user
if (reload != Reload.Site && user != null && sync.SyncEvents.Any()) if (refresh != UI.Refresh.Site && user != null && sync.SyncEvents.Any())
{ {
if (sync.SyncEvents.Exists(item => item.EntityName == EntityNames.User && item.EntityId == user.UserId)) if (sync.SyncEvents.Exists(item => item.EntityName == EntityNames.User && item.EntityId == user.UserId))
{ {
reload = Reload.Site; refresh = UI.Refresh.Site;
} }
} }
if (PageState == null || reload == Reload.Site) if (PageState == null || refresh == UI.Refresh.Site)
{ {
pages = await PageService.GetPagesAsync(site.SiteId); pages = await PageService.GetPagesAsync(site.SiteId);
} }
@ -239,7 +239,7 @@
// remove trailing slash so it can be used as a key for Pages // remove trailing slash so it can be used as a key for Pages
if (path.EndsWith("/")) path = path.Substring(0, path.Length - 1); if (path.EndsWith("/")) path = path.Substring(0, path.Length - 1);
if (PageState == null || reload == Reload.Site) if (PageState == null || refresh == UI.Refresh.Site)
{ {
page = pages.FirstOrDefault(item => item.Path.Equals(path, StringComparison.OrdinalIgnoreCase)); page = pages.FirstOrDefault(item => item.Path.Equals(path, StringComparison.OrdinalIgnoreCase));
} }
@ -274,7 +274,7 @@
{ {
page = await ProcessPage(page, site, user); page = await ProcessPage(page, site, user);
if (PageState == null || reload == Reload.Site) if (PageState == null || refresh == UI.Refresh.Site)
{ {
modules = await ModuleService.GetModulesAsync(site.SiteId); modules = await ModuleService.GetModulesAsync(site.SiteId);
} }

View File

@ -6,25 +6,27 @@
@inherits ModuleBase @inherits ModuleBase
@inject I[Module]Service [Module]Service @inject I[Module]Service [Module]Service
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@inject IStringLocalizer<Edit> Localizer
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<table class="table table-borderless"> <table class="table table-borderless">
<tr> <tr>
<td> <td>
<Label For="name" HelpText="Enter a name">Name: </Label> <Label For="name" HelpText="Enter a name" ResourceKey="Name">Name: </Label>
</td> </td>
<td> <td>
<input id="name" class="form-control" @bind="@_name" /> <input id="name" class="form-control" @bind="@_name" required />
</td> </td>
</tr> </tr>
</table> </table>
<button type="button" class="btn btn-success" @onclick="Save">Save</button> <button type="button" class="btn btn-success" @onclick="Save">@Localizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
<br /> <br /><br />
<br />
@if (PageState.Action == "Edit") @if (PageState.Action == "Edit")
{ {
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon"></AuditInfo> <AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon"></AuditInfo>
} }
</form>
@code { @code {
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
@ -38,12 +40,15 @@
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" } new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" }
}; };
int _id; private ElementReference form;
string _name; private bool validated = false;
string _createdby;
DateTime _createdon; private int _id;
string _modifiedby; private string _name;
DateTime _modifiedon; private string _createdby;
private DateTime _createdon;
private string _modifiedby;
private DateTime _modifiedon;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
@ -66,7 +71,7 @@
catch (Exception ex) catch (Exception ex)
{ {
await logger.LogError(ex, "Error Loading [Module] {[Module]Id} {Error}", _id, ex.Message); await logger.LogError(ex, "Error Loading [Module] {[Module]Id} {Error}", _id, ex.Message);
AddModuleMessage("Error Loading [Module]", MessageType.Error); AddModuleMessage(Localizer["Message.LoadError"], MessageType.Error);
} }
} }
@ -74,7 +79,9 @@
{ {
try try
{ {
if (!string.IsNullOrEmpty(_name)) validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{ {
if (PageState.Action == "Add") if (PageState.Action == "Add")
{ {
@ -95,13 +102,13 @@
} }
else else
{ {
AddModuleMessage("The Name Is Required", MessageType.Warning); AddModuleMessage(Localizer["Message.SaveValidation"], MessageType.Warning);
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
await logger.LogError(ex, "Error Saving [Module] {Error}", ex.Message); await logger.LogError(ex, "Error Saving [Module] {Error}", ex.Message);
AddModuleMessage("Error Saving [Module]", MessageType.Error); AddModuleMessage(Localizer["Message.SaveError"], MessageType.Error);
} }
} }
} }

View File

@ -5,6 +5,7 @@
@inherits ModuleBase @inherits ModuleBase
@inject I[Module]Service [Module]Service @inject I[Module]Service [Module]Service
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@inject IStringLocalizer<Index> Localizer
@if (_[Module]s == null) @if (_[Module]s == null)
{ {
@ -12,7 +13,7 @@
} }
else else
{ {
<ActionLink Action="Add" Security="SecurityAccessLevel.Edit" Text="Add [Module]" /> <ActionLink Action="Add" Security="SecurityAccessLevel.Edit" Text="Add [Module]" ResourceKey="Add" />
<br /> <br />
<br /> <br />
@if (@_[Module]s.Count != 0) @if (@_[Module]s.Count != 0)
@ -21,18 +22,18 @@ else
<Header> <Header>
<th style="width: 1px;">&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
<th>Name</th> <th>@Localizer["Name"]</th>
</Header> </Header>
<Row> <Row>
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.[Module]Id.ToString())" /></td> <td><ActionLink Action="Edit" Parameters="@($"id=" + context.[Module]Id.ToString())" ResourceKey="Edit" /></td>
<td><ActionDialog Header="Delete [Module]" Message="@("Are You Sure You Wish To Delete The " + context.Name + " [Module]?")" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await Delete(context))" /></td> <td><ActionDialog Header="Delete [Module]" Message="@("Are You Sure You Wish To Delete The " + context.Name + " [Module]?")" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await Delete(context))" ResourceKey="Delete" /></td>
<td>@context.Name</td> <td>@context.Name</td>
</Row> </Row>
</Pager> </Pager>
} }
else else
{ {
<p>No [Module]s To Display</p> <p>@Localizer["Message.DisplayNone"]</p>
} }
} }
@ -54,7 +55,7 @@ else
catch (Exception ex) catch (Exception ex)
{ {
await logger.LogError(ex, "Error Loading [Module] {Error}", ex.Message); await logger.LogError(ex, "Error Loading [Module] {Error}", ex.Message);
AddModuleMessage("Error Loading [Module]", MessageType.Error); AddModuleMessage(Localizer["Message.LoadError"], MessageType.Error);
} }
} }
@ -70,7 +71,7 @@ else
catch (Exception ex) catch (Exception ex)
{ {
await logger.LogError(ex, "Error Deleting [Module] {[Module]} {Error}", [Module], ex.Message); await logger.LogError(ex, "Error Deleting [Module] {[Module]} {Error}", [Module], ex.Message);
AddModuleMessage("Error Deleting [Module]", MessageType.Error); AddModuleMessage(Localizer["Message.DeleteError"], MessageType.Error);
} }
} }
} }

View File

@ -1,11 +1,12 @@
@namespace [Owner].[Module] @namespace [Owner].[Module]
@inherits ModuleBase @inherits ModuleBase
@inject ISettingService SettingService @inject ISettingService SettingService
@inject IStringLocalizer<Settings> Localizer
<table class="table table-borderless"> <table class="table table-borderless">
<tr> <tr>
<td> <td>
<Label For="value" HelpText="Enter a value">Name: </Label> <Label For="value" HelpText="Enter a value" ResourceKey="SettingName">Name: </Label>
</td> </td>
<td> <td>
<input id="value" type="text" class="form-control" @bind="@_value" /> <input id="value" type="text" class="form-control" @bind="@_value" />

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 [Module]</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 [Module]</value>
</data>
</root>

View File

@ -0,0 +1,138 @@
<?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="Edit" xml:space="preserve">
<value>Edit</value>
</data>
<data name="Delete" xml:space="preserve">
<value>Delete</value>
</data>
<data name="Message.DisplayNone" xml:space="preserve">
<value>No [Module]s To Display</value>
</data>
<data name="Message.LoadError" xml:space="preserve">
<value>Error Loading [Module]</value>
</data>
<data name="Message.DeleteError" xml:space="preserve">
<value>Error Deleting [Module]</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

@ -7,6 +7,7 @@ using Oqtane.Enums;
using Oqtane.Infrastructure; using Oqtane.Infrastructure;
using [Owner].[Module].Repository; using [Owner].[Module].Repository;
using Oqtane.Controllers; using Oqtane.Controllers;
using System.Net;
namespace [Owner].[Module].Controllers namespace [Owner].[Module].Controllers
{ {
@ -25,12 +26,15 @@ namespace [Owner].[Module].Controllers
[Authorize(Policy = PolicyNames.ViewModule)] [Authorize(Policy = PolicyNames.ViewModule)]
public IEnumerable<Models.[Module]> Get(string moduleid) public IEnumerable<Models.[Module]> Get(string moduleid)
{ {
if (int.Parse(moduleid) == _authEntityId[EntityNames.Module]) int ModuleId;
if (int.TryParse(moduleid, out ModuleId) == _authEntityId[EntityNames.Module])
{ {
return _[Module]Repository.Get[Module]s(int.Parse(moduleid)); return _[Module]Repository.Get[Module]s(int.Parse(ModuleId));
} }
else else
{ {
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized [Module] Get Attempt {ModuleId}", moduleid);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null; return null;
} }
} }
@ -43,12 +47,18 @@ namespace [Owner].[Module].Controllers
Models.[Module] [Module] = _[Module]Repository.Get[Module](id); Models.[Module] [Module] = _[Module]Repository.Get[Module](id);
if ([Module] != null && [Module].ModuleId != _authEntityId[EntityNames.Module]) if ([Module] != null && [Module].ModuleId != _authEntityId[EntityNames.Module])
{ {
[Module] = null;
}
return [Module]; return [Module];
} }
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized [Module] Get Attempt {[Module]Id}", id);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
}
// POST api/<controller> // POST api/<controller>
[ValidateAntiForgeryToken]
[HttpPost] [HttpPost]
[Authorize(Policy = PolicyNames.EditModule)] [Authorize(Policy = PolicyNames.EditModule)]
public Models.[Module] Post([FromBody] Models.[Module] [Module]) public Models.[Module] Post([FromBody] Models.[Module] [Module])
@ -58,23 +68,37 @@ namespace [Owner].[Module].Controllers
[Module] = _[Module]Repository.Add[Module]([Module]); [Module] = _[Module]Repository.Add[Module]([Module]);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "[Module] Added {[Module]}", [Module]); _logger.Log(LogLevel.Information, this, LogFunction.Create, "[Module] Added {[Module]}", [Module]);
} }
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized [Module] Post Attempt {[Module]}", [Module]);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
[Module] = null;
}
return [Module]; return [Module];
} }
// PUT api/<controller>/5 // PUT api/<controller>/5
[ValidateAntiForgeryToken]
[HttpPut("{id}")] [HttpPut("{id}")]
[Authorize(Policy = PolicyNames.EditModule)] [Authorize(Policy = PolicyNames.EditModule)]
public Models.[Module] Put(int id, [FromBody] Models.[Module] [Module]) public Models.[Module] Put(int id, [FromBody] Models.[Module] [Module])
{ {
if (ModelState.IsValid && [Module].ModuleId == _authEntityId[EntityNames.Module]) if (ModelState.IsValid && [Module].ModuleId == _authEntityId[EntityNames.Module] && _[Module]Repository.Get[Module]([Module].[Module]Id, false) != null)
{ {
[Module] = _[Module]Repository.Update[Module]([Module]); [Module] = _[Module]Repository.Update[Module]([Module]);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "[Module] Updated {[Module]}", [Module]); _logger.Log(LogLevel.Information, this, LogFunction.Update, "[Module] Updated {[Module]}", [Module]);
} }
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized [Module] Put Attempt {[Module]}", [Module]);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
[Module] = null;
}
return [Module]; return [Module];
} }
// DELETE api/<controller>/5 // DELETE api/<controller>/5
[ValidateAntiForgeryToken]
[HttpDelete("{id}")] [HttpDelete("{id}")]
[Authorize(Policy = PolicyNames.EditModule)] [Authorize(Policy = PolicyNames.EditModule)]
public void Delete(int id) public void Delete(int id)
@ -85,6 +109,11 @@ namespace [Owner].[Module].Controllers
_[Module]Repository.Delete[Module](id); _[Module]Repository.Delete[Module](id);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "[Module] Deleted {[Module]Id}", id); _logger.Log(LogLevel.Information, this, LogFunction.Delete, "[Module] Deleted {[Module]Id}", id);
} }
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized [Module] Delete Attempt {[Module]Id}", id);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
} }
} }
} }

View File

@ -7,6 +7,7 @@ namespace [Owner].[Module].Repository
{ {
IEnumerable<Models.[Module]> Get[Module]s(int ModuleId); IEnumerable<Models.[Module]> Get[Module]s(int ModuleId);
Models.[Module] Get[Module](int [Module]Id); Models.[Module] Get[Module](int [Module]Id);
Models.[Module] Get[Module](int [Module]Id, bool tracking);
Models.[Module] Add[Module](Models.[Module] [Module]); Models.[Module] Add[Module](Models.[Module] [Module]);
Models.[Module] Update[Module](Models.[Module] [Module]); Models.[Module] Update[Module](Models.[Module] [Module]);
void Delete[Module](int [Module]Id); void Delete[Module](int [Module]Id);

View File

@ -21,9 +21,21 @@ namespace [Owner].[Module].Repository
} }
public Models.[Module] Get[Module](int [Module]Id) public Models.[Module] Get[Module](int [Module]Id)
{
return Get[Module]([Module]Id, true);
}
public Models.[Module] Get[Module](int [Module]Id, bool tracking)
{
if (tracking)
{ {
return _db.[Module].Find([Module]Id); return _db.[Module].Find([Module]Id);
} }
else
{
return _db.[Module].AsNoTracking().FirstOrDefault(item => item.[Module]Id == [Module]Id);
}
}
public Models.[Module] Add[Module](Models.[Module] [Module]) public Models.[Module] Add[Module](Models.[Module] [Module])
{ {