Merge remote-tracking branch 'oqtane/dev' into dev

This commit is contained in:
Leigh Pointer 2023-05-26 16:32:21 +02:00
commit 6bc1507114
36 changed files with 850 additions and 554 deletions

View File

@ -64,6 +64,12 @@
</select> </select>
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="validation" HelpText="Optionally provide a regular expression (RegExp) for validating the value entered" ResourceKey="Validation">Validation: </Label>
<div class="col-sm-9">
<input id="validation" class="form-control" @bind="@_validation" maxlength="200" />
</div>
</div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="private" HelpText="Should this profile item be visible to all users?" ResourceKey="Private">Private? </Label> <Label Class="col-sm-3" For="private" HelpText="Should this profile item be visible to all users?" ResourceKey="Private">Private? </Label>
<div class="col-sm-9"> <div class="col-sm-9">
@ -97,6 +103,7 @@
private string _maxlength = "0"; private string _maxlength = "0";
private string _defaultvalue = string.Empty; private string _defaultvalue = string.Empty;
private string _options = string.Empty; private string _options = string.Empty;
private string _validation = string.Empty;
private string _isrequired = "False"; private string _isrequired = "False";
private string _isprivate = "False"; private string _isprivate = "False";
private string createdby; private string createdby;
@ -126,6 +133,7 @@
_maxlength = profile.MaxLength.ToString(); _maxlength = profile.MaxLength.ToString();
_defaultvalue = profile.DefaultValue; _defaultvalue = profile.DefaultValue;
_options = profile.Options; _options = profile.Options;
_validation = profile.Validation;
_isrequired = profile.IsRequired.ToString(); _isrequired = profile.IsRequired.ToString();
_isprivate = profile.IsPrivate.ToString(); _isprivate = profile.IsPrivate.ToString();
createdby = profile.CreatedBy; createdby = profile.CreatedBy;
@ -169,6 +177,7 @@
profile.MaxLength = int.Parse(_maxlength); profile.MaxLength = int.Parse(_maxlength);
profile.DefaultValue = _defaultvalue; profile.DefaultValue = _defaultvalue;
profile.Options = _options; profile.Options = _options;
profile.Validation = _validation;
profile.IsRequired = (_isrequired == null ? false : Boolean.Parse(_isrequired)); profile.IsRequired = (_isrequired == null ? false : Boolean.Parse(_isrequired));
profile.IsPrivate = (_isprivate == null ? false : Boolean.Parse(_isprivate)); profile.IsPrivate = (_isprivate == null ? false : Boolean.Parse(_isprivate));
if (_profileid != -1) if (_profileid != -1)

View File

@ -145,9 +145,9 @@
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="enabledSSl" HelpText="Specify if SSL is required for your SMTP server" ResourceKey="UseSsl">SSL Enabled: </Label> <Label Class="col-sm-3" For="smtpssl" HelpText="Specify if SSL is required for your SMTP server" ResourceKey="UseSsl">SSL Enabled: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<select id="enabledSSl" class="form-select" @bind="@_smtpssl" > <select id="smtpssl" class="form-select" @bind="@_smtpssl" >
<option value="True">@SharedLocalizer["Yes"]</option> <option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option> <option value="False">@SharedLocalizer["No"]</option>
</select> </select>
@ -183,6 +183,15 @@
</select> </select>
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="smtpenabled" HelpText="Specify if SMTP is enabled for this site" ResourceKey="SMTPEnabled">Enabled? </Label>
<div class="col-sm-9">
<select id="smtpenabled" class="form-select" @bind="@_smtpenabled">
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="retention" HelpText="Number of days of notifications to retain" ResourceKey="Retention">Retention (Days): </Label> <Label Class="col-sm-3" For="retention" HelpText="Number of days of notifications to retain" ResourceKey="Retention">Retention (Days): </Label>
<div class="col-sm-9"> <div class="col-sm-9">
@ -354,6 +363,7 @@
private string _togglesmtppassword = string.Empty; private string _togglesmtppassword = string.Empty;
private string _smtpsender = string.Empty; private string _smtpsender = string.Empty;
private string _smtprelay = "False"; private string _smtprelay = "False";
private string _smtpenabled = "True";
private string _retention = string.Empty; private string _retention = string.Empty;
private string _pwaisenabled; private string _pwaisenabled;
private int _pwaappiconfileid = -1; private int _pwaappiconfileid = -1;
@ -434,6 +444,7 @@
_togglesmtppassword = SharedLocalizer["ShowPassword"]; _togglesmtppassword = SharedLocalizer["ShowPassword"];
_smtpsender = SettingService.GetSetting(settings, "SMTPSender", string.Empty); _smtpsender = SettingService.GetSetting(settings, "SMTPSender", string.Empty);
_smtprelay = SettingService.GetSetting(settings, "SMTPRelay", "False"); _smtprelay = SettingService.GetSetting(settings, "SMTPRelay", "False");
_smtpenabled = SettingService.GetSetting(settings, "SMTPEnabled", "True");
_retention = SettingService.GetSetting(settings, "NotificationRetention", "30"); _retention = SettingService.GetSetting(settings, "NotificationRetention", "30");
// aliases // aliases
@ -600,6 +611,7 @@
settings = SettingService.SetSetting(settings, "SMTPPassword", _smtppassword, true); settings = SettingService.SetSetting(settings, "SMTPPassword", _smtppassword, true);
settings = SettingService.SetSetting(settings, "SMTPSender", _smtpsender, true); settings = SettingService.SetSetting(settings, "SMTPSender", _smtpsender, true);
settings = SettingService.SetSetting(settings, "SMTPRelay", _smtprelay, true); settings = SettingService.SetSetting(settings, "SMTPRelay", _smtprelay, true);
settings = SettingService.SetSetting(settings, "SMTPEnabled", _smtpenabled, true);
settings = SettingService.SetSetting(settings, "NotificationRetention", _retention, true); settings = SettingService.SetSetting(settings, "NotificationRetention", _retention, true);
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId); await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
@ -612,7 +624,7 @@
else else
{ {
AddModuleMessage(Localizer["Success.Settings.SaveSite"], MessageType.Success); AddModuleMessage(Localizer["Success.Settings.SaveSite"], MessageType.Success);
await interop.ScrollTo(0, 0, "smooth"); await ScrollToPageTop();
} }
} }
} }
@ -681,8 +693,7 @@
await NotificationService.AddNotificationAsync(new Notification(PageState.Site.SiteId, PageState.User, PageState.Site.Name + " SMTP Configuration Test", "SMTP Server Is Configured Correctly.")); await NotificationService.AddNotificationAsync(new Notification(PageState.Site.SiteId, PageState.User, PageState.Site.Name + " SMTP Configuration Test", "SMTP Server Is Configured Correctly."));
AddModuleMessage(Localizer["Info.Smtp.SaveSettings"], MessageType.Info); AddModuleMessage(Localizer["Info.Smtp.SaveSettings"], MessageType.Info);
var interop = new Interop(JSRuntime); await ScrollToPageTop();
await interop.ScrollTo(0, 0, "smooth");
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -13,7 +13,7 @@
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="name" HelpText="The name of the module" ResourceKey="Name">Name: </Label> <Label Class="col-sm-3" For="name" HelpText="The name of the module" ResourceKey="Name">Name: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<input id="name" class="form-control" @bind="@_name" disabled /> <input id="name" class="form-control" @bind="@_name" />
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
@ -142,6 +142,7 @@
try try
{ {
var theme = await ThemeService.GetThemeAsync(_themeId, ModuleState.SiteId); var theme = await ThemeService.GetThemeAsync(_themeId, ModuleState.SiteId);
theme.Name = _name;
theme.IsEnabled = (_isenabled == null ? true : bool.Parse(_isenabled)); theme.IsEnabled = (_isenabled == null ? true : bool.Parse(_isenabled));
await ThemeService.UpdateThemeAsync(theme); await ThemeService.UpdateThemeAsync(theme);
await logger.LogInformation("Theme Saved {Theme}", theme); await logger.LogInformation("Theme Saved {Theme}", theme);

View File

@ -1,4 +1,5 @@
@namespace Oqtane.Modules.Admin.UserProfile @namespace Oqtane.Modules.Admin.UserProfile
@using System.Text.RegularExpressions;
@inherits ModuleBase @inherits ModuleBase
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@inject IUserService UserService @inject IUserService UserService
@ -370,6 +371,8 @@ else
{ {
AddModuleMessage(Localizer["Message.Required.ProfileInfo"], MessageType.Warning); AddModuleMessage(Localizer["Message.Required.ProfileInfo"], MessageType.Warning);
} }
await ScrollToPageTop();
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -389,10 +392,15 @@ else
} }
if (!profile.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin)) if (!profile.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
{ {
if (profile.IsRequired && string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty))) if (valid == true && profile.IsRequired && string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty)))
{ {
valid = false; valid = false;
} }
if (valid == true && !string.IsNullOrEmpty(profile.Validation))
{
Regex regex = new Regex(profile.Validation);
valid = regex.Match(SettingService.GetSetting(settings, profile.Name, string.Empty)).Success;
}
} }
} }
return valid; return valid;

View File

@ -1,4 +1,5 @@
@namespace Oqtane.Modules.Admin.Users @namespace Oqtane.Modules.Admin.Users
@using System.Text.RegularExpressions;
@inherits ModuleBase @inherits ModuleBase
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@inject IUserService UserService @inject IUserService UserService
@ -195,10 +196,18 @@
{ {
settings = SettingService.SetSetting(settings, profile.Name, profile.DefaultValue); settings = SettingService.SetSetting(settings, profile.Name, profile.DefaultValue);
} }
if (profile.IsRequired && string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty))) if (!profile.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
{
if (valid == true && profile.IsRequired && string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty)))
{ {
valid = false; valid = false;
} }
if (valid == true && !string.IsNullOrEmpty(profile.Validation))
{
Regex regex = new Regex(profile.Validation);
valid = regex.Match(SettingService.GetSetting(settings, profile.Name, string.Empty)).Success;
}
}
} }
return valid; return valid;
} }

View File

@ -1,4 +1,5 @@
@namespace Oqtane.Modules.Admin.Users @namespace Oqtane.Modules.Admin.Users
@using System.Text.RegularExpressions;
@inherits ModuleBase @inherits ModuleBase
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@inject IUserService UserService @inject IUserService UserService
@ -293,10 +294,18 @@ else
{ {
settings = SettingService.SetSetting(settings, profile.Name, profile.DefaultValue); settings = SettingService.SetSetting(settings, profile.Name, profile.DefaultValue);
} }
if (profile.IsRequired && string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty))) if (!profile.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
{
if (valid == true && profile.IsRequired && string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty)))
{ {
valid = false; valid = false;
} }
if (valid == true && !string.IsNullOrEmpty(profile.Validation))
{
Regex regex = new Regex(profile.Validation);
valid = regex.Match(SettingService.GetSetting(settings, profile.Name, string.Empty)).Success;
}
}
} }
return valid; return valid;
} }

View File

@ -53,7 +53,6 @@
public override List<Resource> Resources => new List<Resource>() public override List<Resource> Resources => new List<Resource>()
{ {
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" },
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill.bubble.css" }, new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill.bubble.css" },
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill.snow.css" } new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill.snow.css" }
}; };

View File

@ -15,11 +15,6 @@
} }
@code { @code {
public override List<Resource> Resources => new List<Resource>()
{
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" }
};
private string content = ""; private string content = "";
protected override async Task OnParametersSetAsync() protected override async Task OnParametersSetAsync()

View File

@ -1,5 +1,7 @@
using System.Collections.Generic;
using Oqtane.Documentation; using Oqtane.Documentation;
using Oqtane.Models; using Oqtane.Models;
using Oqtane.Shared;
namespace Oqtane.Modules.HtmlText namespace Oqtane.Modules.HtmlText
{ {
@ -13,7 +15,11 @@ namespace Oqtane.Modules.HtmlText
Version = "1.0.1", Version = "1.0.1",
ServerManagerType = "Oqtane.Modules.HtmlText.Manager.HtmlTextManager, Oqtane.Server", ServerManagerType = "Oqtane.Modules.HtmlText.Manager.HtmlTextManager, Oqtane.Server",
ReleaseVersions = "1.0.0,1.0.1", ReleaseVersions = "1.0.0,1.0.1",
SettingsType = "Oqtane.Modules.HtmlText.Settings, Oqtane.Client" SettingsType = "Oqtane.Modules.HtmlText.Settings, Oqtane.Client",
Resources = new List<Resource>()
{
new Resource { ResourceType = ResourceType.Stylesheet, Url = "~/Module.css" }
}
}; };
} }
} }

View File

@ -291,6 +291,12 @@ namespace Oqtane.Modules
} }
} }
public async Task ScrollToPageTop()
{
var interop = new Interop(JSRuntime);
await interop.ScrollTo(0, 0, "smooth");
}
// logging methods // logging methods
public async Task Log(Alias alias, LogLevel level, string function, Exception exception, string message, params object[] args) public async Task Log(Alias alias, LogLevel level, string function, Exception exception, string message, params object[] args)
{ {

View File

@ -183,4 +183,10 @@
<data name="Private.Text" xml:space="preserve"> <data name="Private.Text" xml:space="preserve">
<value>Private? </value> <value>Private? </value>
</data> </data>
<data name="Validation.HelpText" xml:space="preserve">
<value>Optionally provide a regular expression (RegExp) for validating the value entered</value>
</data>
<data name="Validation.Text" xml:space="preserve">
<value>Validation:</value>
</data>
</root> </root>

View File

@ -369,4 +369,10 @@
<data name="PageContent.Heading" xml:space="preserve"> <data name="PageContent.Heading" xml:space="preserve">
<value>Page Content</value> <value>Page Content</value>
</data> </data>
<data name="SMTPEnabled.HelpText" xml:space="preserve">
<value>Specify if SMTP is enabled for this site</value>
</data>
<data name="SMTPEnabled.Text" xml:space="preserve">
<value>Enabled?</value>
</data>
</root> </root>

View File

@ -1,3 +1,4 @@
@using System.Net
@namespace Oqtane.Themes.Controls @namespace Oqtane.Themes.Controls
@inherits ThemeControlBase @inherits ThemeControlBase
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@ -501,7 +502,7 @@
module = PageState.Modules.FirstOrDefault(item => item.ModuleDefinitionName == Constants.AdminDashboardModule); module = PageState.Modules.FirstOrDefault(item => item.ModuleDefinitionName == Constants.AdminDashboardModule);
if (module != null) if (module != null)
{ {
NavigationManager.NavigateTo(EditUrl(PageState.Page.Path, module.ModuleId, "Index", "")); NavigationManager.NavigateTo(EditUrl("admin", module.ModuleId, "Index", "returnurl=" + WebUtility.UrlEncode(PageState.Route.PathAndQuery)));
} }
break; break;
case "Add": case "Add":

View File

@ -1,5 +1,7 @@
using System.Collections.Generic;
using Oqtane.Documentation; using Oqtane.Documentation;
using Oqtane.Models; using Oqtane.Models;
using Oqtane.Shared;
namespace Oqtane.Themes.OqtaneTheme namespace Oqtane.Themes.OqtaneTheme
{ {
@ -11,7 +13,13 @@ namespace Oqtane.Themes.OqtaneTheme
Name = "Oqtane Theme", Name = "Oqtane Theme",
Version = "1.0.0", Version = "1.0.0",
ThemeSettingsType = "Oqtane.Themes.OqtaneTheme.ThemeSettings, Oqtane.Client", ThemeSettingsType = "Oqtane.Themes.OqtaneTheme.ThemeSettings, Oqtane.Client",
ContainerSettingsType = "Oqtane.Themes.OqtaneTheme.ContainerSettings, Oqtane.Client" ContainerSettingsType = "Oqtane.Themes.OqtaneTheme.ContainerSettings, Oqtane.Client",
Resources = new List<Resource>()
{
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootswatch/5.2.0/cyborg/bootstrap.min.css", Integrity = "sha512-d6pZJl/sNcj0GFkp4kTjXtPE14deuUsOqFQtxkj0KyBJQl+4e0qsEyuIDcNqrYuGoauAW3sWyDCQp49mhF4Syw==", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Stylesheet, Url = "~/Theme.css" },
new Resource { ResourceType = ResourceType.Script, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.0/js/bootstrap.bundle.min.js", Integrity = "sha512-9GacT4119eY3AcosfWtHMsT5JyZudrexyEVzTBWV3viP/YfB9e2pEy3N7WXL3SV6ASXpTU0vzzSxsbfsuUH4sQ==", CrossOrigin = "anonymous" }
}
}; };
} }
} }

View File

@ -110,14 +110,6 @@
public override string Panes => PaneNames.Default + ",Top Full Width,Top 100%,Left 50%,Right 50%,Left 33%,Center 33%,Right 33%,Left Outer 25%,Left Inner 25%,Right Inner 25%,Right Outer 25%,Left 25%,Center 50%,Right 25%,Left Sidebar 66%,Right Sidebar 33%,Left Sidebar 33%,Right Sidebar 66%,Bottom 100%,Bottom Full Width,Footer"; public override string Panes => PaneNames.Default + ",Top Full Width,Top 100%,Left 50%,Right 50%,Left 33%,Center 33%,Right 33%,Left Outer 25%,Left Inner 25%,Right Inner 25%,Right Outer 25%,Left 25%,Center 50%,Right 25%,Left Sidebar 66%,Right Sidebar 33%,Left Sidebar 33%,Right Sidebar 66%,Bottom 100%,Bottom Full Width,Footer";
public override List<Resource> Resources => new List<Resource>()
{
// obtained from https://cdnjs.com/libraries
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootswatch/5.2.0/cyborg/bootstrap.min.css", Integrity = "sha512-d6pZJl/sNcj0GFkp4kTjXtPE14deuUsOqFQtxkj0KyBJQl+4e0qsEyuIDcNqrYuGoauAW3sWyDCQp49mhF4Syw==", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "Theme.css" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.0/js/bootstrap.bundle.min.js", Integrity = "sha512-9GacT4119eY3AcosfWtHMsT5JyZudrexyEVzTBWV3viP/YfB9e2pEy3N7WXL3SV6ASXpTU0vzzSxsbfsuUH4sQ==", CrossOrigin = "anonymous" }
};
private bool _login = true; private bool _login = true;
private bool _register = true; private bool _register = true;
private bool _footer = false; private bool _footer = false;

View File

@ -531,10 +531,13 @@
{ {
foreach (var resource in resources) foreach (var resource in resources)
{ {
if (!resource.Url.Contains("://") && resource.Url.StartsWith("~/")) if (resource.Url.StartsWith("~"))
{ {
// create local path resource.Url = resource.Url.Replace("~", "/" + type + "/" + name + "/").Replace("//", "/");
resource.Url = resource.Url.Replace("~", alias.BaseUrl + "/" + type + "/" + name); }
if (!resource.Url.Contains("://") && alias.BaseUrl != "" && !resource.Url.StartsWith(alias.BaseUrl))
{
resource.Url = alias.BaseUrl + resource.Url;
} }
// ensure resource does not exist already // ensure resource does not exist already

View File

@ -96,6 +96,30 @@
{ {
await InjectScripts(PageState.Page.BodyContent, ResourceLocation.Body); await InjectScripts(PageState.Page.BodyContent, ResourceLocation.Body);
} }
if (PageState.Page.Resources != null && PageState.Page.Resources.Exists(item => item.ResourceType == ResourceType.Script))
{
var interop = new Interop(JSRuntime);
var scripts = new List<object>();
var inline = 0;
foreach (Resource resource in PageState.Page.Resources.Where(item => item.ResourceType == ResourceType.Script && item.Level != ResourceLevel.Site))
{
if (!string.IsNullOrEmpty(resource.Url))
{
var url = (resource.Url.Contains("://")) ? resource.Url : PageState.Alias.BaseUrl + resource.Url;
scripts.Add(new { href = url, bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", es6module = resource.ES6Module, location = resource.Location.ToString().ToLower() });
}
else
{
inline += 1;
await interop.IncludeScript(GetType().Namespace.ToLower() + inline.ToString(), "", "", "", resource.Content, resource.Location.ToString().ToLower());
}
}
if (scripts.Any())
{
await interop.IncludeScripts(scripts.ToArray());
}
}
} }
} }

View File

@ -33,8 +33,10 @@ namespace Oqtane.Controllers
private readonly IHttpContextAccessor _accessor; private readonly IHttpContextAccessor _accessor;
private readonly IAliasRepository _aliases; private readonly IAliasRepository _aliases;
private readonly ILogger<InstallationController> _filelogger; private readonly ILogger<InstallationController> _filelogger;
private readonly ITenantManager _tenantManager;
private readonly ServerStateManager _serverState;
public InstallationController(IConfigManager configManager, IInstallationManager installationManager, IDatabaseManager databaseManager, ILocalizationManager localizationManager, IMemoryCache cache, IHttpContextAccessor accessor, IAliasRepository aliases, ILogger<InstallationController> filelogger) public InstallationController(IConfigManager configManager, IInstallationManager installationManager, IDatabaseManager databaseManager, ILocalizationManager localizationManager, IMemoryCache cache, IHttpContextAccessor accessor, IAliasRepository aliases, ILogger<InstallationController> filelogger, ITenantManager tenantManager, ServerStateManager serverState)
{ {
_configManager = configManager; _configManager = configManager;
_installationManager = installationManager; _installationManager = installationManager;
@ -44,6 +46,8 @@ namespace Oqtane.Controllers
_accessor = accessor; _accessor = accessor;
_aliases = aliases; _aliases = aliases;
_filelogger = filelogger; _filelogger = filelogger;
_tenantManager = tenantManager;
_serverState = serverState;
} }
// POST api/<controller> // POST api/<controller>
@ -115,7 +119,9 @@ namespace Oqtane.Controllers
private List<ClientAssembly> GetAssemblyList() private List<ClientAssembly> GetAssemblyList()
{ {
return _cache.GetOrCreate("assemblieslist", entry => int siteId = _tenantManager.GetAlias().SiteId;
return _cache.GetOrCreate($"assemblieslist:{siteId}", entry =>
{ {
var binFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); var binFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
var assemblyList = new List<ClientAssembly>(); var assemblyList = new List<ClientAssembly>();
@ -127,30 +133,37 @@ namespace Oqtane.Controllers
hashfilename = false; hashfilename = false;
} }
// get list of assemblies which should be downloaded to client // get site assemblies which should be downloaded to client
var assemblies = AppDomain.CurrentDomain.GetOqtaneClientAssemblies(); var assemblies = _serverState.GetServerState(siteId).Assemblies;
var list = assemblies.Select(a => a.GetName().Name).ToList();
// populate assemblies // populate assembly list
for (int i = 0; i < list.Count; i++) foreach (var assembly in assemblies)
{ {
assemblyList.Add(new ClientAssembly(Path.Combine(binFolder, list[i] + ".dll"), hashfilename)); if (assembly != Constants.ClientId)
{
var filepath = Path.Combine(binFolder, assembly) + ".dll";
if (System.IO.File.Exists(filepath))
{
assemblyList.Add(new ClientAssembly(Path.Combine(binFolder, assembly + ".dll"), hashfilename));
}
}
} }
// insert satellite assemblies at beginning of list // insert satellite assemblies at beginning of list
foreach (var culture in _localizationManager.GetInstalledCultures()) foreach (var culture in _localizationManager.GetInstalledCultures())
{ {
var assembliesFolderPath = Path.Combine(binFolder, culture); if (culture != Constants.DefaultCulture)
if (culture == Constants.DefaultCulture)
{ {
continue; var assembliesFolderPath = Path.Combine(binFolder, culture);
}
if (Directory.Exists(assembliesFolderPath)) if (Directory.Exists(assembliesFolderPath))
{ {
foreach (var resourceFile in Directory.EnumerateFiles(assembliesFolderPath)) foreach (var assembly in assemblies)
{ {
assemblyList.Insert(0, new ClientAssembly(resourceFile, hashfilename)); var filepath = Path.Combine(assembliesFolderPath, assembly) + ".resources.dll";
if (System.IO.File.Exists(filepath))
{
assemblyList.Insert(0, new ClientAssembly(Path.Combine(assembliesFolderPath, assembly + ".resources.dll"), hashfilename));
}
} }
} }
else else
@ -158,48 +171,6 @@ namespace Oqtane.Controllers
_filelogger.LogError(Utilities.LogMessage(this, $"The Satellite Assembly Folder For {culture} Does Not Exist")); _filelogger.LogError(Utilities.LogMessage(this, $"The Satellite Assembly Folder For {culture} Does Not Exist"));
} }
} }
// insert module and theme dependencies at beginning of list
foreach (var assembly in assemblies)
{
foreach (var type in assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(IModule))))
{
var instance = Activator.CreateInstance(type) as IModule;
foreach (string name in instance.ModuleDefinition.Dependencies.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Reverse())
{
var filepath = Path.Combine(binFolder, name.ToLower().EndsWith(".dll") ? name : name + ".dll");
if (System.IO.File.Exists(filepath))
{
if (!assemblyList.Exists(item => item.FilePath == filepath))
{
assemblyList.Insert(0, new ClientAssembly(filepath, hashfilename));
}
}
else
{
_filelogger.LogError(Utilities.LogMessage(this, $"Module {instance.ModuleDefinition.ModuleDefinitionName} Dependency {name}.dll Does Not Exist"));
}
}
}
foreach (var type in assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(ITheme))))
{
var instance = Activator.CreateInstance(type) as ITheme;
foreach (string name in instance.Theme.Dependencies.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Reverse())
{
var filepath = Path.Combine(binFolder, name.ToLower().EndsWith(".dll") ? name : name + ".dll");
if (System.IO.File.Exists(filepath))
{
if (!assemblyList.Exists(item => item.FilePath == filepath))
{
assemblyList.Insert(0, new ClientAssembly(filepath, hashfilename));
}
}
else
{
_filelogger.LogError(Utilities.LogMessage(this, $"Theme {instance.Theme.ThemeName} Dependency {name}.dll Does Not Exist"));
}
}
}
} }
return assemblyList; return assemblyList;
@ -239,6 +210,8 @@ namespace Oqtane.Controllers
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true)) using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
{ {
foreach (var assembly in assemblies) foreach (var assembly in assemblies)
{
if (Path.GetFileNameWithoutExtension(assembly.FilePath) != Constants.ClientId)
{ {
if (System.IO.File.Exists(assembly.FilePath)) if (System.IO.File.Exists(assembly.FilePath))
{ {
@ -259,6 +232,7 @@ namespace Oqtane.Controllers
} }
} }
} }
}
return memoryStream.ToArray(); return memoryStream.ToArray();
} }

View File

@ -200,6 +200,8 @@ namespace Oqtane.Controllers
case EntityNames.Tenant: case EntityNames.Tenant:
case EntityNames.ModuleDefinition: case EntityNames.ModuleDefinition:
case EntityNames.Host: case EntityNames.Host:
case EntityNames.Job:
case EntityNames.Theme:
if (permissionName == PermissionNames.Edit) if (permissionName == PermissionNames.Edit)
{ {
authorized = User.IsInRole(RoleNames.Host); authorized = User.IsInRole(RoleNames.Host);
@ -262,6 +264,8 @@ namespace Oqtane.Controllers
case EntityNames.Tenant: case EntityNames.Tenant:
case EntityNames.ModuleDefinition: case EntityNames.ModuleDefinition:
case EntityNames.Host: case EntityNames.Host:
case EntityNames.Job:
case EntityNames.Theme:
filter = !User.IsInRole(RoleNames.Host); filter = !User.IsInRole(RoleNames.Host);
break; break;
case EntityNames.Site: case EntityNames.Site:

View File

@ -61,6 +61,7 @@ namespace Microsoft.Extensions.DependencyInjection
services.AddSingleton<ILoggerProvider, FileLoggerProvider>(); services.AddSingleton<ILoggerProvider, FileLoggerProvider>();
services.AddSingleton<AutoValidateAntiforgeryTokenFilter>(); services.AddSingleton<AutoValidateAntiforgeryTokenFilter>();
services.AddSingleton<IAuthorizationPolicyProvider, AuthorizationPolicyProvider>(); services.AddSingleton<IAuthorizationPolicyProvider, AuthorizationPolicyProvider>();
services.AddSingleton<ServerStateManager>();
return services; return services;
} }

View File

@ -42,6 +42,8 @@ namespace Oqtane.Infrastructure
// get site settings // get site settings
List<Setting> sitesettings = settingRepository.GetSettings(EntityNames.Site, site.SiteId).ToList(); List<Setting> sitesettings = settingRepository.GetSettings(EntityNames.Site, site.SiteId).ToList();
Dictionary<string, string> settings = GetSettings(sitesettings); Dictionary<string, string> settings = GetSettings(sitesettings);
if (!settings.ContainsKey("SMTPEnabled") || settings["SMTPEnabled"] == "True")
{
if (settings.ContainsKey("SMTPHost") && settings["SMTPHost"] != "" && if (settings.ContainsKey("SMTPHost") && settings["SMTPHost"] != "" &&
settings.ContainsKey("SMTPPort") && settings["SMTPPort"] != "" && settings.ContainsKey("SMTPPort") && settings["SMTPPort"] != "" &&
settings.ContainsKey("SMTPSSL") && settings["SMTPSSL"] != "" && settings.ContainsKey("SMTPSSL") && settings["SMTPSSL"] != "" &&
@ -133,6 +135,7 @@ namespace Oqtane.Infrastructure
// encoding // encoding
mailMessage.SubjectEncoding = System.Text.Encoding.UTF8; mailMessage.SubjectEncoding = System.Text.Encoding.UTF8;
mailMessage.BodyEncoding = System.Text.Encoding.UTF8; mailMessage.BodyEncoding = System.Text.Encoding.UTF8;
mailMessage.IsBodyHtml = true;
// send mail // send mail
try try
@ -157,6 +160,11 @@ namespace Oqtane.Infrastructure
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, SSL, And Sender Are All Required" + "<br />";
} }
} }
else
{
log += "SMTP Disabled In Site Settings" + "<br />";
}
}
return log; return log;
} }

View File

@ -0,0 +1,12 @@
using System.Collections.Generic;
using Oqtane.Models;
namespace Oqtane.Infrastructure
{
public class ServerState
{
public int SiteId { get; set; }
public List<string> Assemblies { get; set; } = new List<string>();
public List<Resource>Scripts { get; set; } = new List<Resource>();
}
}

View File

@ -0,0 +1,49 @@
using System.Collections.Generic;
using System.Linq;
using Oqtane.Models;
namespace Oqtane.Infrastructure
{
// singleton
public class ServerStateManager
{
private List<ServerState> _serverStates { get; set; }
public ServerStateManager()
{
_serverStates = new List<ServerState>();
}
public ServerState GetServerState(int siteId)
{
var serverState = _serverStates.FirstOrDefault(item => item.SiteId == siteId);
if (serverState == null)
{
serverState = new ServerState();
serverState.SiteId = siteId;
serverState.Assemblies = new List<string>();
serverState.Scripts = new List<Resource>();
return serverState;
}
else
{
return serverState;
}
}
public void SetServerState(int siteId, ServerState serverState)
{
var serverstate = _serverStates.FirstOrDefault(item => item.SiteId == siteId);
if (serverstate == null)
{
serverState.SiteId = siteId;
_serverStates.Add(serverState);
}
else
{
serverstate.Assemblies = serverState.Assemblies;
serverstate.Scripts = serverState.Scripts;
}
}
}
}

View File

@ -0,0 +1,28 @@
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Oqtane.Databases.Interfaces;
using Oqtane.Migrations.EntityBuilders;
using Oqtane.Repository;
namespace Oqtane.Migrations.Master
{
[DbContext(typeof(MasterDBContext))]
[Migration("Master.04.00.00.02")]
public class AddThemeName : MultiDatabaseMigration
{
public AddThemeName(IDatabase database) : base(database)
{
}
protected override void Up(MigrationBuilder migrationBuilder)
{
var themeEntityBuilder = new ThemeEntityBuilder(migrationBuilder, ActiveDatabase);
themeEntityBuilder.AddStringColumn("Name", 200, true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
// not implemented
}
}
}

View File

@ -0,0 +1,29 @@
using Microsoft.AspNetCore.Components.Web;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Oqtane.Databases.Interfaces;
using Oqtane.Migrations.EntityBuilders;
using Oqtane.Repository;
namespace Oqtane.Migrations.Tenant
{
[DbContext(typeof(TenantDBContext))]
[Migration("Tenant.04.00.00.03")]
public class AddProfileValidation : MultiDatabaseMigration
{
public AddProfileValidation(IDatabase database) : base(database)
{
}
protected override void Up(MigrationBuilder migrationBuilder)
{
var profileEntityBuilder = new ProfileEntityBuilder(migrationBuilder, ActiveDatabase);
profileEntityBuilder.AddStringColumn("Validation", 200, true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
// not implemented
}
}
}

View File

@ -36,9 +36,10 @@ namespace Oqtane.Pages
private readonly IVisitorRepository _visitors; private readonly IVisitorRepository _visitors;
private readonly IAliasRepository _aliases; private readonly IAliasRepository _aliases;
private readonly ISettingRepository _settings; private readonly ISettingRepository _settings;
private readonly ServerStateManager _serverState;
private readonly ILogManager _logger; private readonly ILogManager _logger;
public HostModel(IConfigManager configuration, ITenantManager tenantManager, ILocalizationManager localizationManager, ILanguageRepository languages, IAntiforgery antiforgery, IJwtManager jwtManager, ISiteRepository sites, IPageRepository pages, IUrlMappingRepository urlMappings, IVisitorRepository visitors, IAliasRepository aliases, ISettingRepository settings, ILogManager logger) public HostModel(IConfigManager configuration, ITenantManager tenantManager, ILocalizationManager localizationManager, ILanguageRepository languages, IAntiforgery antiforgery, IJwtManager jwtManager, ISiteRepository sites, IPageRepository pages, IUrlMappingRepository urlMappings, IVisitorRepository visitors, IAliasRepository aliases, ISettingRepository settings, ServerStateManager serverState, ILogManager logger)
{ {
_configuration = configuration; _configuration = configuration;
_tenantManager = tenantManager; _tenantManager = tenantManager;
@ -52,6 +53,7 @@ namespace Oqtane.Pages
_visitors = visitors; _visitors = visitors;
_aliases = aliases; _aliases = aliases;
_settings = settings; _settings = settings;
_serverState = serverState;
_logger = logger; _logger = logger;
} }
@ -121,28 +123,6 @@ namespace Oqtane.Pages
{ {
RenderMode = site.RenderMode; RenderMode = site.RenderMode;
} }
if (Runtime == "Server")
{
ReconnectScript = CreateReconnectScript();
}
if (site.PwaIsEnabled && site.PwaAppIconFileId != null && site.PwaSplashIconFileId != null)
{
PWAScript = CreatePWAScript(alias, site, route);
}
// site level scripts
HeadResources += ParseScripts(site.HeadContent);
BodyResources += ParseScripts(site.BodyContent);
// get jwt token for downstream APIs
if (User.Identity.IsAuthenticated)
{
var sitesettings = HttpContext.GetSiteSettings();
var secret = sitesettings.GetValue("JwtOptions:Secret", "");
if (!string.IsNullOrEmpty(secret))
{
AuthorizationToken = _jwtManager.GenerateToken(alias, (ClaimsIdentity)User.Identity, secret, sitesettings.GetValue("JwtOptions:Issuer", ""), sitesettings.GetValue("JwtOptions:Audience", ""), int.Parse(sitesettings.GetValue("JwtOptions:Lifetime", "20")));
}
}
if (site.VisitorTracking) if (site.VisitorTracking)
{ {
@ -172,11 +152,33 @@ namespace Oqtane.Pages
} }
} }
// include global resources // get jwt token for downstream APIs
var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies(); if (User.Identity.IsAuthenticated)
foreach (Assembly assembly in assemblies)
{ {
ProcessHostResources(assembly, alias); var sitesettings = HttpContext.GetSiteSettings();
var secret = sitesettings.GetValue("JwtOptions:Secret", "");
if (!string.IsNullOrEmpty(secret))
{
AuthorizationToken = _jwtManager.GenerateToken(alias, (ClaimsIdentity)User.Identity, secret, sitesettings.GetValue("JwtOptions:Issuer", ""), sitesettings.GetValue("JwtOptions:Audience", ""), int.Parse(sitesettings.GetValue("JwtOptions:Lifetime", "20")));
}
}
// inject scripts
if (Runtime == "Server")
{
ReconnectScript = CreateReconnectScript();
}
if (site.PwaIsEnabled && site.PwaAppIconFileId != null && site.PwaSplashIconFileId != null)
{
PWAScript = CreatePWAScript(alias, site, route);
}
HeadResources += ParseScripts(site.HeadContent);
BodyResources += ParseScripts(site.BodyContent);
_sites.InitializeSite(site.SiteId); // populates server state
var scripts = _serverState.GetServerState(site.SiteId).Scripts;
foreach (var script in scripts)
{
AddScript(script, alias);
} }
// set culture if not specified // set culture if not specified
@ -409,20 +411,6 @@ namespace Oqtane.Pages
"</script>"; "</script>";
} }
private void ProcessHostResources(Assembly assembly, Alias alias)
{
var types = assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(IHostResources)));
foreach (var type in types)
{
var obj = Activator.CreateInstance(type) as IHostResources;
foreach (var resource in obj.Resources)
{
resource.Level = ResourceLevel.App;
ProcessResource(resource, 0, alias);
}
}
}
private string ParseScripts(string headcontent) private string ParseScripts(string headcontent)
{ {
// iterate scripts // iterate scripts
@ -439,60 +427,39 @@ namespace Oqtane.Pages
return scripts; return scripts;
} }
private void ProcessResource(Resource resource, int count, Alias alias) private void AddScript(Resource resource, Alias alias)
{
var script = CreateScript(resource, alias);
if (resource.Location == Shared.ResourceLocation.Head)
{
if (!HeadResources.Contains(script))
{
HeadResources += script + Environment.NewLine;
}
}
else
{
if (!BodyResources.Contains(script))
{
BodyResources += script + Environment.NewLine;
}
}
}
private string CreateScript(Resource resource, Alias alias)
{
if (!string.IsNullOrEmpty(resource.Url))
{ {
var url = (resource.Url.Contains("://")) ? resource.Url : alias.BaseUrl + resource.Url; var url = (resource.Url.Contains("://")) ? resource.Url : alias.BaseUrl + resource.Url;
switch (resource.ResourceType) return "<script src=\"" + url + "\"" +
{ ((!string.IsNullOrEmpty(resource.CrossOrigin)) ? " crossorigin=\"" + resource.CrossOrigin + "\"" : "") +
case ResourceType.Stylesheet: ((!string.IsNullOrEmpty(resource.Integrity)) ? " integrity=\"" + resource.Integrity + "\"" : "") +
if (!HeadResources.Contains(url, StringComparison.OrdinalIgnoreCase)) "></script>";
{
string id = "";
if (resource.Level == ResourceLevel.Page)
{
id = "id=\"app-stylesheet-" + resource.Level.ToString().ToLower() + "-" + DateTime.UtcNow.ToString("yyyyMMddHHmmssfff") + "-" + count.ToString("00") + "\" ";
}
HeadResources += "<link " + id + "rel=\"stylesheet\" href=\"" + url + "\"" + CrossOrigin(resource.CrossOrigin) + Integrity(resource.Integrity) + " type=\"text/css\"/>" + Environment.NewLine;
}
break;
case ResourceType.Script:
if (resource.Location == Shared.ResourceLocation.Body)
{
if (!BodyResources.Contains(url, StringComparison.OrdinalIgnoreCase))
{
BodyResources += "<script src=\"" + url + "\"" + CrossOrigin(resource.CrossOrigin) + Integrity(resource.Integrity) + "></script>" + Environment.NewLine;
}
} }
else else
{ {
if (!HeadResources.Contains(resource.Url, StringComparison.OrdinalIgnoreCase)) // inline script
{ return "<script>" + resource.Content + "</script>";
HeadResources += "<script src=\"" + url + "\"" + CrossOrigin(resource.CrossOrigin) + Integrity(resource.Integrity) + "></script>" + Environment.NewLine;
}
}
break;
}
}
private string CrossOrigin(string crossorigin)
{
if (!string.IsNullOrEmpty(crossorigin))
{
return " crossorigin=\"" + crossorigin + "\"";
}
else
{
return "";
}
}
private string Integrity(string integrity)
{
if (!string.IsNullOrEmpty(integrity))
{
return " integrity=\"" + integrity + "\"";
}
else
{
return "";
} }
} }

View File

@ -11,6 +11,7 @@ namespace Oqtane.Repository
Site GetSite(int siteId); Site GetSite(int siteId);
Site GetSite(int siteId, bool tracking); Site GetSite(int siteId, bool tracking);
void DeleteSite(int siteId); void DeleteSite(int siteId);
void InitializeSite(int siteId);
void CreatePages(Site site, List<PageTemplate> pageTemplates); void CreatePages(Site site, List<PageTemplate> pageTemplates);
} }
} }

View File

@ -4,12 +4,14 @@ using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Xml;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Caching.Memory;
using Oqtane.Infrastructure; using Oqtane.Infrastructure;
using Oqtane.Models; using Oqtane.Models;
using Oqtane.Modules; using Oqtane.Modules;
using Oqtane.Shared; using Oqtane.Shared;
using Oqtane.Themes;
namespace Oqtane.Repository namespace Oqtane.Repository
{ {
@ -20,15 +22,17 @@ namespace Oqtane.Repository
private readonly IPermissionRepository _permissions; private readonly IPermissionRepository _permissions;
private readonly ITenantManager _tenants; private readonly ITenantManager _tenants;
private readonly ISettingRepository _settings; private readonly ISettingRepository _settings;
private readonly ServerStateManager _serverState;
private readonly string settingprefix = "SiteEnabled:"; private readonly string settingprefix = "SiteEnabled:";
public ModuleDefinitionRepository(MasterDBContext context, IMemoryCache cache, IPermissionRepository permissions, ITenantManager tenants, ISettingRepository settings) public ModuleDefinitionRepository(MasterDBContext context, IMemoryCache cache, IPermissionRepository permissions, ITenantManager tenants, ISettingRepository settings, ServerStateManager serverState)
{ {
_db = context; _db = context;
_cache = cache; _cache = cache;
_permissions = permissions; _permissions = permissions;
_tenants = tenants; _tenants = tenants;
_settings = settings; _settings = settings;
_serverState = serverState;
} }
public IEnumerable<ModuleDefinition> GetModuleDefinitions() public IEnumerable<ModuleDefinition> GetModuleDefinitions()
@ -182,6 +186,7 @@ namespace Oqtane.Repository
var settings = _settings.GetSettings(EntityNames.ModuleDefinition).ToList(); var settings = _settings.GetSettings(EntityNames.ModuleDefinition).ToList();
// populate module definition site settings and permissions // populate module definition site settings and permissions
var serverState = _serverState.GetServerState(siteId);
foreach (ModuleDefinition moduledefinition in ModuleDefinitions) foreach (ModuleDefinition moduledefinition in ModuleDefinitions)
{ {
moduledefinition.SiteId = siteId; moduledefinition.SiteId = siteId;
@ -196,6 +201,36 @@ namespace Oqtane.Repository
moduledefinition.IsEnabled = moduledefinition.IsAutoEnabled; moduledefinition.IsEnabled = moduledefinition.IsAutoEnabled;
} }
if (moduledefinition.IsEnabled)
{
// build list of assemblies for site
if (!serverState.Assemblies.Contains(moduledefinition.AssemblyName))
{
serverState.Assemblies.Add(moduledefinition.AssemblyName);
}
if (!string.IsNullOrEmpty(moduledefinition.Dependencies))
{
foreach (var assembly in moduledefinition.Dependencies.Replace(".dll", "").Split(',', StringSplitOptions.RemoveEmptyEntries).Reverse())
{
if (!serverState.Assemblies.Contains(assembly))
{
serverState.Assemblies.Insert(0, assembly);
}
}
}
// build list of scripts for site
if (moduledefinition.Resources != null)
{
foreach (var resource in moduledefinition.Resources.Where(item => item.Level == ResourceLevel.Site))
{
if (!serverState.Scripts.Contains(resource))
{
serverState.Scripts.Add(resource);
}
}
}
}
if (permissions.Count == 0) if (permissions.Count == 0)
{ {
// no module definition permissions exist for this site // no module definition permissions exist for this site
@ -216,6 +251,7 @@ namespace Oqtane.Repository
} }
} }
} }
_serverState.SetServerState(siteId, serverState);
// clean up any orphaned permissions // clean up any orphaned permissions
var ids = new HashSet<int>(ModuleDefinitions.Select(item => item.ModuleDefinitionId)); var ids = new HashSet<int>(ModuleDefinitions.Select(item => item.ModuleDefinitionId));
@ -295,6 +331,16 @@ namespace Oqtane.Repository
moduledefinition.ModuleDefinitionName = qualifiedModuleType; moduledefinition.ModuleDefinitionName = qualifiedModuleType;
moduledefinition.ControlTypeTemplate = modulecontroltype.Namespace + "." + Constants.ActionToken + ", " + modulecontroltype.Assembly.GetName().Name; moduledefinition.ControlTypeTemplate = modulecontroltype.Namespace + "." + Constants.ActionToken + ", " + modulecontroltype.Assembly.GetName().Name;
moduledefinition.AssemblyName = assembly.GetName().Name; moduledefinition.AssemblyName = assembly.GetName().Name;
if (moduledefinition.Resources != null)
{
foreach (var resource in moduledefinition.Resources)
{
if (resource.Url.StartsWith("~"))
{
resource.Url = resource.Url.Replace("~", "/Modules/" + Utilities.GetTypeName(moduledefinition.ModuleDefinitionName) + "/").Replace("//", "/");
}
}
}
moduledefinition.IsPortable = false; moduledefinition.IsPortable = false;
if (!string.IsNullOrEmpty(moduledefinition.ServerManagerType)) if (!string.IsNullOrEmpty(moduledefinition.ServerManagerType))
@ -312,12 +358,23 @@ namespace Oqtane.Repository
} }
if (moduledefinition.Categories == "Admin") if (moduledefinition.Categories == "Admin")
{
var shortName = moduledefinition.ModuleDefinitionName.Replace("Oqtane.Modules.Admin.", "").Replace(", Oqtane.Client", "");
if (Constants.DefaultHostModuleTypes.Contains(shortName))
{
moduledefinition.PermissionList = new List<Permission>
{
new Permission(PermissionNames.Utilize, RoleNames.Host, true)
};
}
else
{ {
moduledefinition.PermissionList = new List<Permission> moduledefinition.PermissionList = new List<Permission>
{ {
new Permission(PermissionNames.Utilize, RoleNames.Admin, true) new Permission(PermissionNames.Utilize, RoleNames.Admin, true)
}; };
} }
}
else else
{ {
moduledefinition.PermissionList = new List<Permission> moduledefinition.PermissionList = new List<Permission>

View File

@ -24,11 +24,12 @@ namespace Oqtane.Repository
private readonly IModuleRepository _moduleRepository; private readonly IModuleRepository _moduleRepository;
private readonly IPageModuleRepository _pageModuleRepository; private readonly IPageModuleRepository _pageModuleRepository;
private readonly IModuleDefinitionRepository _moduleDefinitionRepository; private readonly IModuleDefinitionRepository _moduleDefinitionRepository;
private readonly IThemeRepository _themeRepository;
private readonly IServiceProvider _serviceProvider; private readonly IServiceProvider _serviceProvider;
private readonly IConfigurationRoot _config; private readonly IConfigurationRoot _config;
public SiteRepository(TenantDBContext context, IRoleRepository roleRepository, IProfileRepository profileRepository, IFolderRepository folderRepository, IPageRepository pageRepository, public SiteRepository(TenantDBContext context, IRoleRepository roleRepository, IProfileRepository profileRepository, IFolderRepository folderRepository, IPageRepository pageRepository,
IModuleRepository moduleRepository, IPageModuleRepository pageModuleRepository, IModuleDefinitionRepository moduleDefinitionRepository, IServiceProvider serviceProvider, IModuleRepository moduleRepository, IPageModuleRepository pageModuleRepository, IModuleDefinitionRepository moduleDefinitionRepository, IThemeRepository themeRepository, IServiceProvider serviceProvider,
IConfigurationRoot config) IConfigurationRoot config)
{ {
_db = context; _db = context;
@ -39,6 +40,7 @@ namespace Oqtane.Repository
_moduleRepository = moduleRepository; _moduleRepository = moduleRepository;
_pageModuleRepository = pageModuleRepository; _pageModuleRepository = pageModuleRepository;
_moduleDefinitionRepository = moduleDefinitionRepository; _moduleDefinitionRepository = moduleDefinitionRepository;
_themeRepository = themeRepository;
_serviceProvider = serviceProvider; _serviceProvider = serviceProvider;
_config = config; _config = config;
} }
@ -88,6 +90,12 @@ namespace Oqtane.Repository
_db.SaveChanges(); _db.SaveChanges();
} }
public void InitializeSite(int siteId)
{
_moduleDefinitionRepository.GetModuleDefinitions(siteId);
_themeRepository.GetThemes();
}
private void CreateSite(Site site) private void CreateSite(Site site)
{ {
// create default entities for site // create default entities for site

View File

@ -12,6 +12,7 @@ using Oqtane.Models;
using Oqtane.Shared; using Oqtane.Shared;
using Oqtane.Themes; using Oqtane.Themes;
using System.Reflection.Metadata; using System.Reflection.Metadata;
using Oqtane.Migrations.Master;
namespace Oqtane.Repository namespace Oqtane.Repository
{ {
@ -21,14 +22,16 @@ namespace Oqtane.Repository
private readonly IMemoryCache _cache; private readonly IMemoryCache _cache;
private readonly ITenantManager _tenants; private readonly ITenantManager _tenants;
private readonly ISettingRepository _settings; private readonly ISettingRepository _settings;
private readonly ServerStateManager _serverState;
private readonly string settingprefix = "SiteEnabled:"; private readonly string settingprefix = "SiteEnabled:";
public ThemeRepository(MasterDBContext context, IMemoryCache cache, ITenantManager tenants, ISettingRepository settings) public ThemeRepository(MasterDBContext context, IMemoryCache cache, ITenantManager tenants, ISettingRepository settings, ServerStateManager serverState)
{ {
_db = context; _db = context;
_cache = cache; _cache = cache;
_tenants = tenants; _tenants = tenants;
_settings = settings; _settings = settings;
_serverState = serverState;
} }
public IEnumerable<Theme> GetThemes() public IEnumerable<Theme> GetThemes()
@ -123,6 +126,12 @@ namespace Oqtane.Repository
} }
else else
{ {
// override user customizable property values
Theme.Name = (!string.IsNullOrEmpty(theme.Name)) ? theme.Name : Theme.Name;
foreach (var themecontrol in Theme.Themes)
{
themecontrol.Name = Theme.Name + " - " + themecontrol.Name;
}
// remove theme from list as it is already synced // remove theme from list as it is already synced
themes.Remove(theme); themes.Remove(theme);
} }
@ -147,6 +156,7 @@ namespace Oqtane.Repository
var settings = _settings.GetSettings(EntityNames.Theme).ToList(); var settings = _settings.GetSettings(EntityNames.Theme).ToList();
// populate theme site settings // populate theme site settings
var serverState = _serverState.GetServerState(siteId);
foreach (Theme theme in Themes) foreach (Theme theme in Themes)
{ {
theme.SiteId = siteId; theme.SiteId = siteId;
@ -160,7 +170,38 @@ namespace Oqtane.Repository
{ {
theme.IsEnabled = theme.IsAutoEnabled; theme.IsEnabled = theme.IsAutoEnabled;
} }
if (theme.IsEnabled)
{
// build list of assemblies for site
if (!serverState.Assemblies.Contains(theme.AssemblyName))
{
serverState.Assemblies.Add(theme.AssemblyName);
} }
if (!string.IsNullOrEmpty(theme.Dependencies))
{
foreach (var assembly in theme.Dependencies.Replace(".dll", "").Split(',', StringSplitOptions.RemoveEmptyEntries).Reverse())
{
if (!serverState.Assemblies.Contains(assembly))
{
serverState.Assemblies.Insert(0, assembly);
}
}
}
// build list of scripts for site
if (theme.Resources != null)
{
foreach (var resource in theme.Resources.Where(item => item.Level == ResourceLevel.Site))
{
if (!serverState.Scripts.Contains(resource))
{
serverState.Scripts.Add(resource);
}
}
}
}
}
_serverState.SetServerState(siteId, serverState);
} }
return Themes; return Themes;
@ -225,12 +266,22 @@ namespace Oqtane.Repository
Version = new Version(1, 0, 0).ToString() Version = new Version(1, 0, 0).ToString()
}; };
} }
// set internal properties // set internal properties
theme.ThemeName = qualifiedThemeType; theme.ThemeName = qualifiedThemeType;
theme.Themes = new List<ThemeControl>(); theme.Themes = new List<ThemeControl>();
theme.Containers = new List<ThemeControl>(); theme.Containers = new List<ThemeControl>();
theme.AssemblyName = assembly.FullName.Split(",")[0]; theme.AssemblyName = assembly.FullName.Split(",")[0];
if (theme.Resources != null)
{
foreach (var resource in theme.Resources)
{
if (resource.Url.StartsWith("~"))
{
resource.Url = resource.Url.Replace("~", "/Themes/" + Utilities.GetTypeName(theme.ThemeName) + "/").Replace("//", "/");
}
}
}
Debug.WriteLine($"Oqtane Info: Registering Theme {theme.ThemeName}"); Debug.WriteLine($"Oqtane Info: Registering Theme {theme.ThemeName}");
themes.Add(theme); themes.Add(theme);
index = themes.FindIndex(item => item.ThemeName == qualifiedThemeType); index = themes.FindIndex(item => item.ThemeName == qualifiedThemeType);
@ -242,7 +293,7 @@ namespace Oqtane.Repository
new ThemeControl new ThemeControl
{ {
TypeName = themeControlType.FullName + ", " + themeControlType.Assembly.GetName().Name, TypeName = themeControlType.FullName + ", " + themeControlType.Assembly.GetName().Name,
Name = theme.Name + " - " + ((string.IsNullOrEmpty(themecontrolobject.Name)) ? Utilities.GetTypeNameLastSegment(themeControlType.FullName, 0) : themecontrolobject.Name), Name = ((string.IsNullOrEmpty(themecontrolobject.Name)) ? Utilities.GetTypeNameLastSegment(themeControlType.FullName, 0) : themecontrolobject.Name),
Thumbnail = themecontrolobject.Thumbnail, Thumbnail = themecontrolobject.Thumbnail,
Panes = themecontrolobject.Panes Panes = themecontrolobject.Panes
} }

View File

@ -3,6 +3,7 @@ namespace Oqtane.Shared
public enum ResourceLevel public enum ResourceLevel
{ {
App, App,
Site,
Page, Page,
Module Module
} }

View File

@ -48,7 +48,7 @@ namespace Oqtane.Models
public string ModuleDefinitionName { get; set; } public string ModuleDefinitionName { get; set; }
/// <summary> /// <summary>
/// Nice name to show in admin / edit dialogs. /// Friendly name to show in UI
/// </summary> /// </summary>
public string Name { get; set; } public string Name { get; set; }

View File

@ -68,5 +68,10 @@ namespace Oqtane.Models
/// This gives possible values for dropdown input fields. /// This gives possible values for dropdown input fields.
/// </summary> /// </summary>
public string Options { get; set; } public string Options { get; set; }
/// <summary>
/// Optional RegExp validation expression
/// </summary>
public string Validation { get; set; }
} }
} }

View File

@ -35,10 +35,12 @@ namespace Oqtane.Models
/// </summary> /// </summary>
public string ThemeName { get; set; } public string ThemeName { get; set; }
// additional ITheme properties /// <summary>
[NotMapped] /// Friendly name to show in UI
/// </summary>
public string Name { get; set; } public string Name { get; set; }
// additional ITheme properties
[NotMapped] [NotMapped]
public string Version { get; set; } public string Version { get; set; }

View File

@ -1,4 +1,7 @@
using System; using System;
using Oqtane.Models;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace Oqtane.Shared namespace Oqtane.Shared
{ {
@ -39,6 +42,8 @@ namespace Oqtane.Shared
public const string DefaultSiteTemplate = "Oqtane.SiteTemplates.DefaultSiteTemplate, Oqtane.Server"; public const string DefaultSiteTemplate = "Oqtane.SiteTemplates.DefaultSiteTemplate, Oqtane.Server";
public static readonly string[] DefaultHostModuleTypes = new[] { "Upgrade", "Themes", "SystemInfo", "Sql", "Sites", "ModuleDefinitions", "Logs", "Jobs", "ModuleCreator" };
public const string FileUrl = "/files/"; public const string FileUrl = "/files/";
public const string ImageUrl = "/api/file/image/"; public const string ImageUrl = "/api/file/image/";
public const int UserFolderCapacity = 20; // megabytes public const int UserFolderCapacity = 20; // megabytes

View File

@ -48,7 +48,6 @@ This project is open source, and therefore is a work in progress...
Backlog (TBD) Backlog (TBD)
- [ ] Azure Autoscale support (ie. web farm) - [ ] Azure Autoscale support (ie. web farm)
- [ ] Routable Modules (ie. declarative configuration) - [ ] Routable Modules (ie. declarative configuration)
- [ ] Client Assembly Loading / Site
- [ ] Folder Providers - [ ] Folder Providers
- [ ] Generative AI Integration - [ ] Generative AI Integration
@ -56,7 +55,9 @@ Backlog (TBD)
- [ ] Migration to .NET 8 - [ ] Migration to .NET 8
4.0.0 (Q2 2023) 4.0.0 (Q2 2023)
- [ ] Migration to .NET 7 - [x] Migration to .NET 7
- [x] Improved JavaScript, CSS, and Meta support
- [x] Optimized Client Assembly Loading
[3.4.3](https://github.com/oqtane/oqtane.framework/releases/tag/v3.4.3) ( May 3, 2023 ) [3.4.3](https://github.com/oqtane/oqtane.framework/releases/tag/v3.4.3) ( May 3, 2023 )
- [x] Stabilization improvements - [x] Stabilization improvements