Merge pull request #1938 from sbwalker/dev

hide deleted pages in Admin Dashboard, impove Settings API by replacing IsPublic with IsPrivate, isolate Setting updates to not affect PageState, make Pager horizintally scrollable on narrow viewports, improve LocalizableComponent to support embedded controls
This commit is contained in:
Shaun Walker 2022-01-14 13:16:42 -05:00 committed by GitHub
commit 367c1c3568
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 260 additions and 219 deletions

View File

@ -27,6 +27,6 @@
protected override void OnInitialized()
{
var admin = PageState.Pages.FirstOrDefault(item => item.Path == "admin");
_pages = PageState.Pages.Where(item => item.ParentId == admin?.PageId).ToList();
_pages = PageState.Pages.Where(item => item.ParentId == admin?.PageId && !item.IsDeleted).ToList();
}
}

View File

@ -195,8 +195,8 @@ else
{
try
{
var settings = PageState.Site.Settings;
settings = SettingService.SetSetting(settings, "LogRetention", _retention);
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
settings = SettingService.SetSetting(settings, "LogRetention", _retention, true);
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success);

View File

@ -128,7 +128,7 @@ else
var moduleDefinition = new ModuleDefinition { Owner = _owner, Name = _module, Description = _description, Template = _template, Version = _reference };
moduleDefinition = await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition);
var settings = ModuleState.Settings;
var settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
SettingService.SetSetting(settings, "ModuleDefinitionName", moduleDefinition.ModuleDefinitionName);
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);

View File

@ -479,12 +479,12 @@
site = await SiteService.UpdateSiteAsync(site);
var settings = await SettingService.GetSiteSettingsAsync(site.SiteId);
SettingService.SetSetting(settings, "SMTPHost", _smtphost, false);
SettingService.SetSetting(settings, "SMTPPort", _smtpport, false);
SettingService.SetSetting(settings, "SMTPSSL", _smtpssl, false);
SettingService.SetSetting(settings, "SMTPUsername", _smtpusername, false);
SettingService.SetSetting(settings, "SMTPPassword", _smtppassword, false);
SettingService.SetSetting(settings, "SMTPSender", _smtpsender, false);
SettingService.SetSetting(settings, "SMTPHost", _smtphost, true);
SettingService.SetSetting(settings, "SMTPPort", _smtpport, true);
SettingService.SetSetting(settings, "SMTPSSL", _smtpssl, true);
SettingService.SetSetting(settings, "SMTPUsername", _smtpusername, true);
SettingService.SetSetting(settings, "SMTPPassword", _smtppassword, true);
SettingService.SetSetting(settings, "SMTPSender", _smtpsender, true);
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
@ -596,12 +596,12 @@
try
{
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
SettingService.SetSetting(settings, "SMTPHost", _smtphost);
SettingService.SetSetting(settings, "SMTPPort", _smtpport);
SettingService.SetSetting(settings, "SMTPSSL", _smtpssl);
SettingService.SetSetting(settings, "SMTPUsername", _smtpusername);
SettingService.SetSetting(settings, "SMTPPassword", _smtppassword);
SettingService.SetSetting(settings, "SMTPSender", _smtpsender);
SettingService.SetSetting(settings, "SMTPHost", _smtphost, true);
SettingService.SetSetting(settings, "SMTPPort", _smtpport, true);
SettingService.SetSetting(settings, "SMTPSSL", _smtpssl, true);
SettingService.SetSetting(settings, "SMTPUsername", _smtpusername, true);
SettingService.SetSetting(settings, "SMTPPassword", _smtppassword, true);
SettingService.SetSetting(settings, "SMTPSender", _smtpsender, true);
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
await logger.LogInformation("Site SMTP Settings Saved");

View File

@ -57,7 +57,6 @@ else
{
@if (_results.Count > 0)
{
<div class="table-responsive">
<Pager Class="table table-bordered" Items="@_results">
<Header>
@foreach (KeyValuePair<string, string> kvp in _results.First())
@ -72,7 +71,6 @@ else
}
</Row>
</Pager>
</div>
}
else
{

View File

@ -151,9 +151,9 @@ else
site.VisitorTracking = bool.Parse(_tracking);
await SiteService.UpdateSiteAsync(site);
var settings = PageState.Site.Settings;
settings = SettingService.SetSetting(settings, "VisitorFilter", _filter);
settings = SettingService.SetSetting(settings, "VisitorRetention", _retention);
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
settings = SettingService.SetSetting(settings, "VisitorFilter", _filter, true);
settings = SettingService.SetSetting(settings, "VisitorRetention", _retention, true);
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success);

View File

@ -13,6 +13,9 @@ namespace Oqtane.Modules.Controls
[Parameter]
public string ResourceKey { get; set; }
[Parameter]
public string ResourceType { get; set; }
protected bool IsLocalizable { get; private set; }
protected string Localize(string name) => _localizer?[name] ?? name;
@ -50,9 +53,14 @@ namespace Oqtane.Modules.Controls
{
IsLocalizable = false;
if (!String.IsNullOrEmpty(ResourceKey) && ModuleState?.ModuleType != null)
if (string.IsNullOrEmpty(ResourceType))
{
var moduleType = Type.GetType(ModuleState.ModuleType);
ResourceType = ModuleState?.ModuleType;
}
if (!String.IsNullOrEmpty(ResourceKey) && !string.IsNullOrEmpty(ResourceType))
{
var moduleType = Type.GetType(ResourceType);
if (moduleType != null)
{
using (var scope = ServiceActivator.GetScope())

View File

@ -54,6 +54,7 @@
}
@if (Format == "Table" && Row != null)
{
<div class="table-responsive">
<table class="@Class">
<thead>
<tr>@Header</tr>
@ -69,6 +70,7 @@
}
</tbody>
</table>
</div>
}
@if (Format == "Grid" && Row != null)
{

View File

@ -7,7 +7,7 @@
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="files" ResourceKey="Allow File Management" HelpText="Specify If Editors Can Upload and Select Files">Allow File Management: </Label>
<Label Class="col-sm-3" For="files" ResourceKey="AllowFileManagement" ResourceType="@resourceType" HelpText="Specify If Editors Can Upload and Select Files">Allow File Management: </Label>
<div class="col-sm-9">
<select id="files" class="form-select" @bind="@_allowfilemanagement">
<option value="true">@SharedLocalizer["Yes"]</option>
@ -18,6 +18,7 @@
</div>
@code {
private string resourceType = "Oqtane.Modules.HtmlText.Settings, Oqtane.Client"; // for localization
private string _allowfilemanagement;
protected override void OnInitialized()
@ -36,7 +37,7 @@
{
try
{
var settings = ModuleState.Settings;
var settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
settings = SettingService.SetSetting(settings, "AllowFileManagement", _allowfilemanagement);
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
}

View File

@ -59,7 +59,7 @@
: 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:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>

View File

@ -121,7 +121,7 @@
<value>Specify if a Footer pane should always be displayed in a fixed location at the bottom of the page.</value>
</data>
<data name="Footer.Text" xml:space="preserve">
<value>Display Footer?</value>
<value>Display Fixed Footer?</value>
</data>
<data name="Login.HelpText" xml:space="preserve">
<value>Specify if a Login option should be displayed, Note that this option does not prevent the login page from being accessible via a direct url.</value>

View File

@ -206,7 +206,7 @@ namespace Oqtane.Services
/// <returns></returns>
Dictionary<string, string> SetSetting(Dictionary<string, string> settings, string settingName, string settingValue);
Dictionary<string, string> SetSetting(Dictionary<string, string> settings, string settingName, string settingValue, bool isPublic);
Dictionary<string, string> SetSetting(Dictionary<string, string> settings, string settingName, string settingValue, bool isPrivate);
Dictionary<string, string> MergeSettings(Dictionary<string, string> settings1, Dictionary<string, string> settings2);

View File

@ -131,12 +131,12 @@ namespace Oqtane.Services
foreach (KeyValuePair<string, string> kvp in settings)
{
string value = kvp.Value;
bool ispublic = true;
bool isprivate = false;
if (value.StartsWith("[Private]"))
{
value = value.Substring(9); // remove [Private]
ispublic = false;
isprivate = true;
}
Setting setting = settingsList.FirstOrDefault(item => item.SettingName.Equals(kvp.Key, StringComparison.OrdinalIgnoreCase));
@ -147,22 +147,21 @@ namespace Oqtane.Services
setting.EntityId = entityId;
setting.SettingName = kvp.Key;
setting.SettingValue = value;
setting.IsPublic = ispublic;
setting.IsPrivate = isprivate;
setting = await AddSettingAsync(setting);
}
else
{
if (setting.SettingValue != value || setting.IsPublic != ispublic)
if (setting.SettingValue != value)
{
setting.SettingValue = value;
setting.IsPublic = ispublic;
setting.IsPrivate = isprivate;
setting = await UpdateSettingAsync(setting);
}
}
}
}
public async Task<Setting> GetSettingAsync(string entityName, int settingId)
{
return await GetJsonAsync<Setting>($"{Apiurl}/{settingId}/{entityName}");
@ -196,16 +195,16 @@ namespace Oqtane.Services
public Dictionary<string, string> SetSetting(Dictionary<string, string> settings, string settingName, string settingValue)
{
return SetSetting(settings, settingName, settingValue, true);
return SetSetting(settings, settingName, settingValue, false);
}
public Dictionary<string, string> SetSetting(Dictionary<string, string> settings, string settingName, string settingValue, bool isPublic)
public Dictionary<string, string> SetSetting(Dictionary<string, string> settings, string settingName, string settingValue, bool isPrivate)
{
if (settings == null)
{
settings = new Dictionary<string, string>();
}
settingValue = (isPublic) ? settingValue : "[Private]" + settingValue;
settingValue = (isPrivate) ? "[Private]" + settingValue : settingValue;
if (settings.ContainsKey(settingName))
{
settings[settingName] = settingValue;

View File

@ -6,7 +6,7 @@
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="title" ResourceKey="Title" HelpText="Specify If The Module Title Should Be Displayed">Display Title?</Label>
<Label Class="col-sm-3" For="title" ResourceKey="Title" ResourceType="@resourceType" HelpText="Specify If The Module Title Should Be Displayed">Display Title?</Label>
<div class="col-sm-9">
<select id="title" class="form-select" @bind="@_title">
<option value="true">Yes</option>
@ -15,7 +15,7 @@
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="background\" ResourceKey="Background" HelpText="Optionally Specify A Background Color For The Container">Background Color:</Label>
<Label Class="col-sm-3" For="background\" ResourceKey="Background" ResourceType="@resourceType" HelpText="Optionally Specify A Background Color For The Container">Background Color:</Label>
<div class="col-sm-9">
<select id="background" class="form-select" @bind="@_background">
<option value="">None</option>
@ -31,7 +31,7 @@
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="text" ResourceKey="Text" HelpText="Optionally Specify A Text Color For The Container">Text Color:</Label>
<Label Class="col-sm-3" For="text" ResourceKey="Text" ResourceType="@resourceType" HelpText="Optionally Specify A Text Color For The Container">Text Color:</Label>
<div class="col-sm-9">
<select id="text" class="form-select" @bind="@_text">
<option value="">None</option>
@ -47,7 +47,7 @@
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="border" ResourceKey="Border" HelpText="Optionally Specify A Border For The Container">Border Color:</Label>
<Label Class="col-sm-3" For="border" ResourceKey="Border" ResourceType="@resourceType" HelpText="Optionally Specify A Border For The Container">Border Color:</Label>
<div class="col-sm-9">
<select id="border" class="form-select" @bind="@_border">
<option value="">None</option>
@ -66,6 +66,7 @@
</div>
@code {
private string resourceType = "Oqtane.Themes.OqtaneTheme.ContainerSettings, Oqtane.Client"; // for localization
private string _title = "true";
private string _background = "";
private string _text = "";
@ -90,7 +91,7 @@
{
try
{
var settings = ModuleState.Settings;
var settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
settings = SettingService.SetSetting(settings, GetType().Namespace + ":Title", _title);
settings = SettingService.SetSetting(settings, GetType().Namespace + ":Background", _background);
settings = SettingService.SetSetting(settings, GetType().Namespace + ":Text", _text);

View File

@ -8,7 +8,7 @@
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="scope" ResourceKey="Scope" HelpText="Specify if the settings are applicable to this page or the entire site.">Setting Scope:</Label>
<Label Class="col-sm-3" For="scope" ResourceKey="Scope" ResourceType="@resourceType" HelpText="Specify if the settings are applicable to this page or the entire site.">Setting Scope:</Label>
<div class="col-sm-9">
<select id="scope" class="form-select" value="@_scope" @onchange="(e => ScopeChanged(e))">
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
@ -20,7 +20,7 @@
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="login" ResourceKey="Login" HelpText="Specify if a Login option should be displayed. Note that this option does not prevent the login page from being accessible via a direct url.">Show Login?</Label>
<Label Class="col-sm-3" For="login" ResourceKey="Login" ResourceType="@resourceType" HelpText="Specify if a Login option should be displayed. Note that this option does not prevent the login page from being accessible via a direct url.">Show Login?</Label>
<div class="col-sm-9">
<select id="login" class="form-select" @bind="@_login">
<option value="-">&lt;@SharedLocalizer["Not Specified"]&gt;</option>
@ -30,7 +30,7 @@
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="register" ResourceKey="Register" HelpText="Specify if a Register option should be displayed. Note that this option is also dependent on the Allow Registration option in Site Settings.">Show Register?</Label>
<Label Class="col-sm-3" For="register" ResourceKey="Register" ResourceType="@resourceType" HelpText="Specify if a Register option should be displayed. Note that this option is also dependent on the Allow Registration option in Site Settings.">Show Register?</Label>
<div class="col-sm-9">
<select id="register" class="form-select" @bind="@_register">
<option value="-">&lt;@SharedLocalizer["Not Specified"]&gt;</option>
@ -40,7 +40,7 @@
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="footer" ResourceKey="Footer" HelpText="Specify if a Footer pane should always be displayed in a fixed location at the bottom of the page">Display Fixed Footer?</Label>
<Label Class="col-sm-3" For="footer" ResourceKey="Footer" ResourceType="@resourceType" HelpText="Specify if a Footer pane should always be displayed in a fixed location at the bottom of the page">Display Fixed Footer?</Label>
<div class="col-sm-9">
<select id="footer" class="form-select" @bind="@_footer">
<option value="-">&lt;@SharedLocalizer["Not Specified"]&gt;</option>
@ -52,6 +52,7 @@
</div>
@code {
private string resourceType = "Oqtane.Themes.OqtaneTheme.ThemeSettings, Oqtane.Client"; // for localization
private string _scope = "page";
private string _login = "-";
private string _register = "-";
@ -110,7 +111,7 @@
{
if (_scope == "site")
{
var settings = PageState.Site.Settings;
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
if (_login != "-")
{
settings = SettingService.SetSetting(settings, GetType().Namespace + ":Login", _login, true);
@ -127,7 +128,7 @@
}
else
{
var settings = PageState.Page.Settings;
var settings = await SettingService.GetPageSettingsAsync(PageState.Page.PageId);
if (_login != "-")
{
settings = SettingService.SetSetting(settings, GetType().Namespace + ":Login", _login);

View File

@ -75,7 +75,7 @@ namespace Oqtane.Controllers
module.ModuleDefinition = moduledefinitions.Find(item => item.ModuleDefinitionName == module.ModuleDefinitionName);
module.Settings = settings.Where(item => item.EntityId == pagemodule.ModuleId)
.Where(item => item.IsPublic || _userPermissions.IsAuthorized(User, PermissionNames.Edit, pagemodule.Module.Permissions))
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, pagemodule.Module.Permissions))
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
modules.Add(module);
@ -102,7 +102,7 @@ namespace Oqtane.Controllers
List<ModuleDefinition> moduledefinitions = _moduleDefinitions.GetModuleDefinitions(module.SiteId).ToList();
module.ModuleDefinition = moduledefinitions.Find(item => item.ModuleDefinitionName == module.ModuleDefinitionName);
module.Settings = _settings.GetSettings(EntityNames.Module, id)
.Where(item => item.IsPublic || _userPermissions.IsAuthorized(User, PermissionNames.Edit, module.Permissions))
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, module.Permissions))
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
return module;
}

View File

@ -57,7 +57,7 @@ namespace Oqtane.Controllers
if (_userPermissions.IsAuthorized(User, PermissionNames.View, page.Permissions))
{
page.Settings = settings.Where(item => item.EntityId == page.PageId)
.Where(item => item.IsPublic || _userPermissions.IsAuthorized(User, PermissionNames.Edit, page.Permissions))
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, page.Permissions))
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
pages.Add(page);
}
@ -89,7 +89,7 @@ namespace Oqtane.Controllers
if (page != null && page.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User,PermissionNames.View, page.Permissions))
{
page.Settings = _settings.GetSettings(EntityNames.Page, page.PageId)
.Where(item => item.IsPublic || _userPermissions.IsAuthorized(User, PermissionNames.Edit, page.Permissions))
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, page.Permissions))
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
return page;
}
@ -109,7 +109,7 @@ namespace Oqtane.Controllers
if (page != null && page.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, page.Permissions))
{
page.Settings = _settings.GetSettings(EntityNames.Page, page.PageId)
.Where(item => item.IsPublic || _userPermissions.IsAuthorized(User, PermissionNames.Edit, page.Permissions))
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, page.Permissions))
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
return page;
}

View File

@ -41,9 +41,9 @@ namespace Oqtane.Controllers
if (IsAuthorized(entityName, entityId, PermissionNames.View))
{
settings = _settings.GetSettings(entityName, entityId).ToList();
if (FilterPublic(entityName, entityId))
if (FilterPrivate(entityName, entityId))
{
settings = settings.Where(item => item.IsPublic).ToList();
settings = settings.Where(item => !item.IsPrivate).ToList();
}
}
else
@ -61,7 +61,7 @@ namespace Oqtane.Controllers
Setting setting = _settings.GetSetting(entityName, id);
if (IsAuthorized(setting.EntityName, setting.EntityId, PermissionNames.View))
{
if (FilterPublic(entityName, id) && !setting.IsPublic)
if (FilterPrivate(entityName, id) && setting.IsPrivate)
{
setting = null;
}
@ -199,7 +199,7 @@ namespace Oqtane.Controllers
return authorized;
}
private bool FilterPublic(string entityName, int entityId)
private bool FilterPrivate(string entityName, int entityId)
{
bool filter = false;
switch (entityName)

View File

@ -45,7 +45,7 @@ namespace Oqtane.Controllers
if (site.SiteId == _alias.SiteId)
{
site.Settings = _settings.GetSettings(EntityNames.Site, site.SiteId)
.Where(item => item.IsPublic || User.IsInRole(RoleNames.Admin))
.Where(item => !item.IsPrivate || User.IsInRole(RoleNames.Admin))
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
return site;
}

View File

@ -0,0 +1,32 @@
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Oqtane.Databases.Interfaces;
using Oqtane.Migrations.EntityBuilders;
using Oqtane.Repository;
using Oqtane.Shared;
namespace Oqtane.Migrations.Tenant
{
[DbContext(typeof(TenantDBContext))]
[Migration("Tenant.03.00.02.02")]
public class UpdateSettingIsPrivate : MultiDatabaseMigration
{
public UpdateSettingIsPrivate(IDatabase database) : base(database)
{
}
protected override void Up(MigrationBuilder migrationBuilder)
{
var settingEntityBuilder = new SettingEntityBuilder(migrationBuilder, ActiveDatabase);
settingEntityBuilder.AddBooleanColumn("IsPrivate", true);
settingEntityBuilder.UpdateColumn("IsPrivate", "0", "bool", "");
settingEntityBuilder.UpdateColumn("IsPrivate", "1", "bool", "EntityName = 'Site' AND SettingName LIKE 'SMTP%'");
settingEntityBuilder.DropColumn("IsPublic");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
// not implemented
}
}
}

View File

@ -33,9 +33,9 @@ namespace Oqtane.Models
public string SettingValue { get; set; }
/// <summary>
/// Indicates if this setting is publicly available - only applicable to Site Settings as other entities have more granular permissions
/// Indicates if this setting is private - indicating it should be maintained on the server and not sent to the client
/// </summary>
public bool IsPublic { get; set; }
public bool IsPrivate { get; set; }
#region IAuditable Properties

View File

@ -1,7 +1,6 @@
using Oqtane.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.IO;
using System.Linq;