This commit is contained in:
Ikuo Ohba
2025-05-02 23:45:18 +09:00
11 changed files with 128 additions and 112 deletions

View File

@ -59,29 +59,23 @@ else
</select>
</div>
</div>
@if (_allowregistration == "true")
{
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="registerurl" HelpText="Optionally provide a custom registration url" ResourceKey="RegisterUrl">Register Url:</Label>
<div class="col-sm-9">
<input id="registerurl" class="form-control" @bind="@_registerurl" />
</div>
</div>
}
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="profileurl" HelpText="Optionally provide a custom profile url" ResourceKey="ProfileUrl">Profile Url:</Label>
<div class="col-sm-9">
<input id="profileurl" class="form-control" @bind="@_profileurl" />
</div>
</div>
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{
@if (_providertype != "")
{
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="allowsitelogin" HelpText="Do you want to allow users to sign in using a username and password that is managed locally on this site? Note that you should only disable this option if you have already sucessfully configured an external login provider, or else you may lock yourself out of the site." ResourceKey="AllowSiteLogin">Allow Login?</Label>
<div class="col-sm-9">
<select id="allowsitelogin" class="form-select" @bind="@_allowsitelogin">
<option value="true">@SharedLocalizer["Yes"]</option>
<option value="false">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
}
else
{
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="allowsitelogin" HelpText="Do you want to allow users to sign in using a username and password that is managed locally on this site? Note that you should only disable this option if you have already sucessfully configured an external login provider, or else you may lock yourself out of the site." ResourceKey="AllowSiteLogin">Allow Login?</Label>
<div class="col-sm-9">
<input id="allowsitelogin" class="form-control" value="@SharedLocalizer["Yes"]" readonly />
</div>
</div>
}
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="twofactor" HelpText="Do you want users to use two factor authentication? Note that you should use the Disabled option until you have successfully verified that the Notification Job in Scheduled Jobs is enabled and your SMTP options in Site Settings are configured or else you will lock yourself out." ResourceKey="TwoFactor">Two Factor?</Label>
<div class="col-sm-9">
@ -432,6 +426,15 @@ else
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="allowsitelogin" HelpText="Do you want to allow users to sign in using a username and password that is managed locally on this site? Note that you should only disable this option if you have already sucessfully configured an external login provider, or else you may lock yourself out of the site." ResourceKey="AllowSiteLogin">Allow Local Login?</Label>
<div class="col-sm-9">
<select id="allowsitelogin" class="form-select" @bind="@_allowsitelogin">
<option value="true">@SharedLocalizer["Yes"]</option>
<option value="false">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
}
}
</Section>
@ -485,7 +488,8 @@ else
private List<UserRole> users;
private string _allowregistration;
private string _allowsitelogin;
private string _registerurl;
private string _profileurl;
private string _twofactor;
private string _cookiename;
private string _cookieexpiration;
@ -533,6 +537,7 @@ else
private string _createusers;
private string _verifyusers;
private string _allowhostrole;
private string _allowsitelogin;
private string _secret;
private string _secrettype = "password";
@ -553,7 +558,8 @@ else
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
_allowregistration = PageState.Site.AllowRegistration.ToString().ToLower();
_allowsitelogin = SettingService.GetSetting(settings, "LoginOptions:AllowSiteLogin", "true");
_registerurl = SettingService.GetSetting(settings, "LoginOptions:RegisterUrl", "");
_profileurl = SettingService.GetSetting(settings, "LoginOptions:ProfileUrl", "");
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{
@ -616,6 +622,7 @@ else
_createusers = SettingService.GetSetting(settings, "ExternalLogin:CreateUsers", "true");
_verifyusers = SettingService.GetSetting(settings, "ExternalLogin:VerifyUsers", "true");
_allowhostrole = SettingService.GetSetting(settings, "ExternalLogin:AllowHostRole", "false");
_allowsitelogin = SettingService.GetSetting(settings, "LoginOptions:AllowSiteLogin", "true");
}
private async Task LoadUsersAsync(bool load)
@ -673,10 +680,11 @@ else
await SiteService.UpdateSiteAsync(site);
var settings = await SettingService.GetSiteSettingsAsync(site.SiteId);
settings = SettingService.SetSetting(settings, "LoginOptions:AllowSiteLogin", _allowsitelogin, false);
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{
settings = SettingService.SetSetting(settings, "LoginOptions:RegisterUrl", _registerurl, false);
settings = SettingService.SetSetting(settings, "LoginOptions:ProfileUrl", _profileurl, false);
settings = SettingService.SetSetting(settings, "LoginOptions:TwoFactor", _twofactor, false);
settings = SettingService.SetSetting(settings, "LoginOptions:CookieName", _cookiename, true);
settings = SettingService.SetSetting(settings, "LoginOptions:CookieExpiration", _cookieexpiration, true);
@ -720,6 +728,7 @@ else
settings = SettingService.SetSetting(settings, "ExternalLogin:CreateUsers", _createusers, true);
settings = SettingService.SetSetting(settings, "ExternalLogin:VerifyUsers", _verifyusers, true);
settings = SettingService.SetSetting(settings, "ExternalLogin:AllowHostRole", _allowhostrole, true);
settings = SettingService.SetSetting(settings, "LoginOptions:AllowSiteLogin", _allowsitelogin, false);
settings = SettingService.SetSetting(settings, "JwtOptions:Secret", _secret, true);
settings = SettingService.SetSetting(settings, "JwtOptions:Issuer", _issuer, true);

View File

@ -17,7 +17,7 @@ namespace Oqtane.Modules
public abstract class ModuleBase : ComponentBase, IModuleControl
{
private Logger _logger;
private string _urlparametersstate;
private string _urlparametersstate = string.Empty;
private Dictionary<string, string> _urlparameters;
private bool _scriptsloaded = false;
@ -62,7 +62,7 @@ namespace Oqtane.Modules
public Dictionary<string, string> UrlParameters {
get
{
if (_urlparametersstate == null || _urlparametersstate != PageState.UrlParameters)
if (string.IsNullOrEmpty(_urlparametersstate) || _urlparametersstate != PageState.UrlParameters)
{
_urlparametersstate = PageState.UrlParameters;
_urlparameters = GetUrlParameters(UrlParametersTemplate);

View File

@ -132,6 +132,18 @@
<data name="AllowRegistration.Text" xml:space="preserve">
<value>Allow Registration? </value>
</data>
<data name="RegisterUrl.HelpText" xml:space="preserve">
<value>Optionally provide a custom registration url</value>
</data>
<data name="RegisterUrl.Text" xml:space="preserve">
<value>Register Url: </value>
</data>
<data name="ProfileUrl.HelpText" xml:space="preserve">
<value>Optionally provide a custom user profile url</value>
</data>
<data name="ProfileUrl.Text" xml:space="preserve">
<value>Profile Url: </value>
</data>
<data name="Error.SaveSiteSettings" xml:space="preserve">
<value>Error Saving Settings</value>
</data>
@ -208,7 +220,7 @@
<value>Do you want to allow users to sign in using a username and password that is managed locally on this site? Note that you should only disable this option if you have already sucessfully configured an external login provider, or else you may lock yourself out of the site.</value>
</data>
<data name="AllowSiteLogin.Text" xml:space="preserve">
<value>Allow Login?</value>
<value>Allow Local Login?</value>
</data>
<data name="Authority.HelpText" xml:space="preserve">
<value>The authority url or issuer url associated with the identity provider</value>

View File

@ -1,6 +1,7 @@
@namespace Oqtane.Themes.Controls
@using System.Net
@inherits ThemeControlBase
@inject ISettingService SettingService
@inject IStringLocalizer<UserProfile> Localizer
@inject NavigationManager NavigationManager
@ -51,20 +52,38 @@
if (!string.IsNullOrEmpty(RegisterUrl))
{
_registerurl = RegisterUrl + "?returnurl=" + (RegisterUrl.Contains("://") ? WebUtility.UrlEncode(PageState.Route.RootUrl) + _returnurl : _returnurl);
_registerurl = RegisterUrl;
_registerurl += (!_registerurl.Contains("?") ? "?" : "&") + "returnurl=" + (_registerurl.Contains("://") ? WebUtility.UrlEncode(PageState.Route.RootUrl) + _returnurl : _returnurl);
}
else
{
_registerurl = NavigateUrl("register", "returnurl=" + _returnurl);
if (!string.IsNullOrEmpty(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:RegisterUrl", "")))
{
_registerurl = SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:RegisterUrl", "");
_registerurl += (!_registerurl.Contains("?") ? "?" : "&") + "returnurl=" + (_registerurl.Contains("://") ? WebUtility.UrlEncode(PageState.Route.RootUrl) + _returnurl : _returnurl);
}
else
{
_registerurl = NavigateUrl("register", "returnurl=" + _returnurl);
}
}
if (!string.IsNullOrEmpty(ProfileUrl))
{
_profileurl = ProfileUrl + "?returnurl=" + (ProfileUrl.Contains("://") ? WebUtility.UrlEncode(PageState.Route.RootUrl) + _returnurl : _returnurl);
_profileurl = ProfileUrl;
_profileurl += (!_profileurl.Contains("?") ? "?" : "&") + "returnurl=" + (_profileurl.Contains("://") ? WebUtility.UrlEncode(PageState.Route.RootUrl) + _returnurl : _returnurl);
}
else
{
_profileurl = NavigateUrl("profile", "returnurl=" + _returnurl);
if (!string.IsNullOrEmpty(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:ProfileUrl", "")))
{
_profileurl = SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:ProfileUrl", "");
_profileurl += (!_profileurl.Contains("?") ? "?" : "&") + "returnurl=" + (_profileurl.Contains("://") ? WebUtility.UrlEncode(PageState.Route.RootUrl) + _returnurl : _returnurl);
}
else
{
_profileurl = NavigateUrl("profile", "returnurl=" + _returnurl);
}
}
}
}

View File

@ -40,27 +40,26 @@ namespace Oqtane.Infrastructure
log += "Processing Notifications For Site: " + site.Name + "<br />";
// get site settings
List<Setting> sitesettings = settingRepository.GetSettings(EntityNames.Site, site.SiteId).ToList();
Dictionary<string, string> settings = GetSettings(sitesettings);
if (!site.IsDeleted && (!settings.ContainsKey("SMTPEnabled") || settings["SMTPEnabled"] == "True"))
var settings = settingRepository.GetSettings(EntityNames.Site, site.SiteId, EntityNames.Host, -1);
if (!site.IsDeleted && settingRepository.GetSettingValue(settings, "SMTPEnabled", "True") == "True")
{
if (settings.ContainsKey("SMTPHost") && settings["SMTPHost"] != "" &&
settings.ContainsKey("SMTPPort") && settings["SMTPPort"] != "" &&
settings.ContainsKey("SMTPSSL") && settings["SMTPSSL"] != "" &&
settings.ContainsKey("SMTPSender") && settings["SMTPSender"] != "")
if (settingRepository.GetSettingValue(settings, "SMTPHost", "") != "" &&
settingRepository.GetSettingValue(settings, "SMTPPort", "") != "" &&
settingRepository.GetSettingValue(settings, "SMTPSender", "") != "")
{
// construct SMTP Client
var client = new SmtpClient()
{
DeliveryMethod = SmtpDeliveryMethod.Network,
UseDefaultCredentials = false,
Host = settings["SMTPHost"],
Port = int.Parse(settings["SMTPPort"]),
EnableSsl = bool.Parse(settings["SMTPSSL"])
Host = settingRepository.GetSettingValue(settings, "SMTPHost", ""),
Port = int.Parse(settingRepository.GetSettingValue(settings, "SMTPPort", "")),
EnableSsl = bool.Parse(settingRepository.GetSettingValue(settings, "SMTPSSL", "False"))
};
if (settings["SMTPUsername"] != "" && settings["SMTPPassword"] != "")
if (settingRepository.GetSettingValue(settings, "SMTPUsername", "") != "" && settingRepository.GetSettingValue(settings, "SMTPPassword", "") != "")
{
client.Credentials = new NetworkCredential(settings["SMTPUsername"], settings["SMTPPassword"]);
client.Credentials = new NetworkCredential(settingRepository.GetSettingValue(settings, "SMTPUsername", ""), settingRepository.GetSettingValue(settings, "SMTPPassword", ""));
}
// iterate through undelivered notifications
@ -100,7 +99,7 @@ namespace Oqtane.Infrastructure
MailMessage mailMessage = new MailMessage();
// sender
if (settings.ContainsKey("SMTPRelay") && settings["SMTPRelay"] == "True" && !string.IsNullOrEmpty(notification.FromEmail))
if (settingRepository.GetSettingValue(settings, "SMTPRelay", "False") == "True" && !string.IsNullOrEmpty(notification.FromEmail))
{
if (!string.IsNullOrEmpty(notification.FromDisplayName))
{
@ -113,7 +112,7 @@ namespace Oqtane.Infrastructure
}
else
{
mailMessage.From = new MailAddress(settings["SMTPSender"], (!string.IsNullOrEmpty(notification.FromDisplayName)) ? notification.FromDisplayName : site.Name);
mailMessage.From = new MailAddress(settingRepository.GetSettingValue(settings, "SMTPSender", ""), (!string.IsNullOrEmpty(notification.FromDisplayName)) ? notification.FromDisplayName : site.Name);
}
// recipient
@ -162,7 +161,7 @@ namespace Oqtane.Infrastructure
}
else
{
log += "SMTP Not Configured Properly In Site Settings - Host, Port, SSL, And Sender Are All Required" + "<br />";
log += "SMTP Not Configured Properly In Site Settings - Host, Port, And Sender Are All Required" + "<br />";
}
}
else
@ -173,15 +172,5 @@ namespace Oqtane.Infrastructure
return log;
}
private Dictionary<string, string> GetSettings(List<Setting> settings)
{
Dictionary<string, string> dictionary = new Dictionary<string, string>();
foreach (Setting setting in settings.OrderBy(item => item.SettingName).ToList())
{
dictionary.Add(setting.SettingName, setting.SettingValue);
}
return dictionary;
}
}
}

View File

@ -40,18 +40,13 @@ namespace Oqtane.Infrastructure
foreach (Site site in sites)
{
log += "<br />Processing Site: " + site.Name + "<br />";
int retention;
int count;
// get site settings
Dictionary<string, string> settings = GetSettings(settingRepository.GetSettings(EntityNames.Site, site.SiteId).ToList());
var settings = settingRepository.GetSettings(EntityNames.Site, site.SiteId, EntityNames.Host, -1);
// purge event log
retention = 30; // 30 days
if (settings.ContainsKey("LogRetention") && !string.IsNullOrEmpty(settings["LogRetention"]))
{
retention = int.Parse(settings["LogRetention"]);
}
var retention = int.Parse(settingRepository.GetSettingValue(settings, "LogRetention", "30")); // 30 day default
try
{
count = logRepository.DeleteLogs(site.SiteId, retention);
@ -65,11 +60,7 @@ namespace Oqtane.Infrastructure
// purge visitors
if (site.VisitorTracking)
{
retention = 30; // 30 days
if (settings.ContainsKey("VisitorRetention") && !string.IsNullOrEmpty(settings["VisitorRetention"]))
{
retention = int.Parse(settings["VisitorRetention"]);
}
retention = int.Parse(settingRepository.GetSettingValue(settings, "VisitorRetention", "30")); // 30 day default
try
{
count = visitorRepository.DeleteVisitors(site.SiteId, retention);
@ -82,11 +73,7 @@ namespace Oqtane.Infrastructure
}
// purge notifications
retention = 30; // 30 days
if (settings.ContainsKey("NotificationRetention") && !string.IsNullOrEmpty(settings["NotificationRetention"]))
{
retention = int.Parse(settings["NotificationRetention"]);
}
retention = int.Parse(settingRepository.GetSettingValue(settings, "NotificationRetention", "30")); // 30 day default
try
{
count = notificationRepository.DeleteNotifications(site.SiteId, retention);
@ -98,11 +85,7 @@ namespace Oqtane.Infrastructure
}
// purge broken urls
retention = 30; // 30 days
if (settings.ContainsKey("UrlMappingRetention") && !string.IsNullOrEmpty(settings["UrlMappingRetention"]))
{
retention = int.Parse(settings["UrlMappingRetention"]);
}
retention = int.Parse(settingRepository.GetSettingValue(settings, "UrlMappingRetention", "30")); // 30 day default
try
{
count = urlMappingRepository.DeleteUrlMappings(site.SiteId, retention);
@ -127,15 +110,5 @@ namespace Oqtane.Infrastructure
return log;
}
private Dictionary<string, string> GetSettings(List<Setting> settings)
{
Dictionary<string, string> dictionary = new Dictionary<string, string>();
foreach (Setting setting in settings.OrderBy(item => item.SettingName).ToList())
{
dictionary.Add(setting.SettingName, setting.SettingValue);
}
return dictionary;
}
}
}

View File

@ -23,10 +23,6 @@ namespace Oqtane.Infrastructure
var config = context.RequestServices.GetService(typeof(IConfigManager)) as IConfigManager;
string path = context.Request.Path.ToString();
// note that in order to support Alias subfolders we used to ignore Blazor framework requests...
// but this does not work in static rendering as the web UI request originates from /_blazor
//if (config.IsInstalled() && !path.StartsWith("/_"))
if (config.IsInstalled())
{
// get alias (note that this also sets SiteState.Alias)
@ -43,7 +39,7 @@ namespace Oqtane.Infrastructure
var sitesettings = cache.GetOrCreate(Constants.HttpContextSiteSettingsKey + alias.SiteKey, entry =>
{
var settingRepository = context.RequestServices.GetService(typeof(ISettingRepository)) as ISettingRepository;
return settingRepository.GetSettings(EntityNames.Site, alias.SiteId)
return settingRepository.GetSettings(EntityNames.Site, alias.SiteId, EntityNames.Host, -1)
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
});
context.Items.Add(Constants.HttpContextSiteSettingsKey, sitesettings);

View File

@ -1,5 +1,6 @@
using System.Collections.Generic;
using Oqtane.Models;
using Oqtane.Shared;
namespace Oqtane.Repository
{
@ -7,11 +8,13 @@ namespace Oqtane.Repository
{
IEnumerable<Setting> GetSettings(string entityName);
IEnumerable<Setting> GetSettings(string entityName, int entityId);
IEnumerable<Setting> GetSettings(string entityName1, int entityId1, string entityName2, int entityId2);
Setting AddSetting(Setting setting);
Setting UpdateSetting(Setting setting);
Setting GetSetting(string entityName, int settingId);
Setting GetSetting(string entityName, int entityId, string settingName);
void DeleteSetting(string entityName, int settingId);
void DeleteSettings(string entityName, int entityId);
string GetSettingValue(IEnumerable<Setting> settings, string settingName, string defaultValue);
}
}

View File

@ -1,6 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using Oqtane.Infrastructure;
@ -39,24 +38,27 @@ namespace Oqtane.Repository
public IEnumerable<Setting> GetSettings(string entityName, int entityId)
{
var settings = GetSettings(entityName).ToList();
if (entityName == EntityNames.Site)
return GetSettings(entityName).Where(item => item.EntityId == entityId);
}
public IEnumerable<Setting> GetSettings(string entityName1, int entityId1, string entityName2, int entityId2)
{
// merge settings from entity2 into entity1
var settings1 = GetSettings(entityName1, entityId1).ToList();
foreach (var setting2 in GetSettings(entityName2, entityId2))
{
// site settings can be overridden by host settings
var hostsettings = GetSettings(EntityNames.Host);
foreach (var hostsetting in hostsettings)
var setting1 = settings1.FirstOrDefault(item => item.SettingName == setting2.SettingName);
if (setting1 == null)
{
if (settings.Any(item => item.SettingName == hostsetting.SettingName))
{
settings.First(item => item.SettingName == hostsetting.SettingName).SettingValue = hostsetting.SettingValue;
}
else
{
settings.Add(new Setting { SettingId = -1, EntityName = entityName, EntityId = entityId, SettingName = hostsetting.SettingName, SettingValue = hostsetting.SettingValue, IsPrivate = hostsetting.IsPrivate });
}
settings1.Add(new Setting { EntityName = entityName1, EntityId = entityId1, SettingName = setting2.SettingName, SettingValue = setting2.SettingValue, IsPrivate = setting2.IsPrivate });
}
else
{
setting1.SettingValue = setting2.SettingValue;
setting1.IsPrivate = setting2.IsPrivate;
}
}
return settings.Where(item => item.EntityId == entityId);
return settings1;
}
public Setting AddSetting(Setting setting)
@ -165,6 +167,19 @@ namespace Oqtane.Repository
ManageCache(entityName);
}
public string GetSettingValue(IEnumerable<Setting> settings, string settingName, string defaultValue)
{
var setting = settings.FirstOrDefault(item => item.SettingName == settingName);
if (setting != null)
{
return setting.SettingValue;
}
else
{
return defaultValue;
}
}
private bool IsMaster(string EntityName)
{
return (EntityName == EntityNames.ModuleDefinition || EntityName == EntityNames.Host);

View File

@ -111,7 +111,7 @@ namespace Oqtane.Services
if (site != null && site.SiteId == alias.SiteId)
{
// site settings
site.Settings = _settings.GetSettings(EntityNames.Site, site.SiteId)
site.Settings = _settings.GetSettings(EntityNames.Site, site.SiteId, EntityNames.Host, -1)
.ToDictionary(setting => setting.SettingName, setting => (setting.IsPrivate ? _private : "") + setting.SettingValue);
// populate file extensions

View File

@ -195,9 +195,6 @@ namespace Oqtane
app.UseHsts();
}
// execute any IServerStartup logic
app.ConfigureOqtaneAssemblies(env);
// allow oqtane localization middleware
app.UseOqtaneLocalization();
@ -248,6 +245,9 @@ namespace Oqtane
.AddAdditionalAssemblies(typeof(SiteRouter).Assembly);
});
// execute any IServerStartup logic
app.ConfigureOqtaneAssemblies(env);
// simulate the fallback routing approach of traditional Blazor - allowing the custom SiteRouter to handle all routing concerns
app.UseEndpoints(endpoints =>
{