Merge pull request #51 from sbwalker/master

Support for third party modules, improved error handling, standardardized enum naming, reorganized interface definitions, support for DB script upgrades, added Settings entity
This commit is contained in:
Shaun Walker 2019-08-14 09:36:36 -04:00 committed by GitHub
commit 3ddda9b64f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
78 changed files with 809 additions and 261 deletions

View File

@ -5,7 +5,7 @@
@if (!Installed) @if (!Installed)
{ {
<Installer /> <Installer Installed="@Installed" />
} }
else else
{ {

View File

@ -42,16 +42,17 @@
</AuthorizeView> </AuthorizeView>
@code { @code {
public override SecurityAccessLevelEnum SecurityAccessLevel { get { return SecurityAccessLevelEnum.Anonymous; } } public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Anonymous; } }
public string Message { get; set; } = ""; public string Message { get; set; } = "";
public string Username { get; set; } = ""; public string Username { get; set; } = "";
public string Password { get; set; } = ""; public string Password { get; set; } = "";
public bool Remember { get; set; } = false; public bool Remember { get; set; } = false;
private async Task Login() private async Task Login()
{ {
User user = new User(); User user = new User();
user.SiteId = PageState.Site.SiteId;
user.Username = Username; user.Username = Username;
user.Password = Password; user.Password = Password;
user.IsPersistent = Remember; user.IsPersistent = Remember;
@ -81,11 +82,11 @@ private async Task Login()
{ {
Message = "<div class=\"alert alert-danger\" role=\"alert\">Login Failed. Please Remember That Passwords Are Case Sensitive.</div>"; Message = "<div class=\"alert alert-danger\" role=\"alert\">Login Failed. Please Remember That Passwords Are Case Sensitive.</div>";
} }
} }
private void Cancel() private void Cancel()
{ {
string ReturnUrl = PageState.QueryString["returnurl"]; string ReturnUrl = PageState.QueryString["returnurl"];
UriHelper.NavigateTo(ReturnUrl); UriHelper.NavigateTo(ReturnUrl);
} }
} }

View File

@ -30,7 +30,7 @@ else
} }
@code { @code {
public override SecurityAccessLevelEnum SecurityAccessLevel { get { return SecurityAccessLevelEnum.Host; } } public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } }
List<ModuleDefinition> moduledefinitions; List<ModuleDefinition> moduledefinitions;

View File

@ -63,11 +63,15 @@
</td> </td>
</tr> </tr>
</table> </table>
@DynamicComponent
<button type="button" class="btn btn-success" @onclick="@SaveModule">Save</button> <button type="button" class="btn btn-success" @onclick="@SaveModule">Save</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
@code { @code {
public override SecurityAccessLevelEnum SecurityAccessLevel { get { return SecurityAccessLevelEnum.Edit; } } public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Edit; } }
public override string Title { get { return "Module Settings"; } } public override string Title { get { return "Module Settings"; } }
Dictionary<string, string> containers = new Dictionary<string, string>(); Dictionary<string, string> containers = new Dictionary<string, string>();
@ -77,6 +81,9 @@
string editpermissions; string editpermissions;
string pageid; string pageid;
RenderFragment DynamicComponent { get; set; }
object settings;
protected override async Task OnInitAsync() protected override async Task OnInitAsync()
{ {
title = ModuleState.Title; title = ModuleState.Title;
@ -85,6 +92,17 @@
viewpermissions = ModuleState.ViewPermissions; viewpermissions = ModuleState.ViewPermissions;
editpermissions = ModuleState.EditPermissions; editpermissions = ModuleState.EditPermissions;
pageid = ModuleState.PageId.ToString(); pageid = ModuleState.PageId.ToString();
DynamicComponent = builder =>
{
Type moduleType = Type.GetType(ModuleState.ModuleType);
if (moduleType != null)
{
builder.OpenComponent(0, moduleType);
builder.AddComponentReferenceCapture(1, inst => { settings = Convert.ChangeType(inst, moduleType); });
builder.CloseComponent();
}
};
} }
private async Task SaveModule() private async Task SaveModule()
@ -94,17 +112,19 @@
module.EditPermissions = editpermissions; module.EditPermissions = editpermissions;
await ModuleService.UpdateModuleAsync(module); await ModuleService.UpdateModuleAsync(module);
PageModule pagemodule = new PageModule(); PageModule pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId);
pagemodule.PageModuleId = ModuleState.PageModuleId;
pagemodule.PageId = Int32.Parse(pageid);
pagemodule.ModuleId = ModuleState.ModuleId;
pagemodule.Title = title; pagemodule.Title = title;
pagemodule.Pane = ModuleState.Pane;
pagemodule.Order = ModuleState.Order;
pagemodule.ContainerType = containertype; pagemodule.ContainerType = containertype;
await PageModuleService.UpdatePageModuleAsync(pagemodule); await PageModuleService.UpdatePageModuleAsync(pagemodule);
Type moduleType = Type.GetType(ModuleState.ModuleType);
if (moduleType != null)
{
moduleType.GetMethod("UpdateSettings").Invoke(settings, null); // method must be public in settings component
}
PageState.Reload = Constants.ReloadPage; PageState.Reload = Constants.ReloadPage;
UriHelper.NavigateTo(NavigateUrl()); UriHelper.NavigateTo(NavigateUrl());
} }
} }

View File

@ -115,7 +115,7 @@
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
@code { @code {
public override SecurityAccessLevelEnum SecurityAccessLevel { get { return SecurityAccessLevelEnum.Admin; } } public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } }
Dictionary<string, string> themes = new Dictionary<string, string>(); Dictionary<string, string> themes = new Dictionary<string, string>();
Dictionary<string, string> panelayouts = new Dictionary<string, string>(); Dictionary<string, string> panelayouts = new Dictionary<string, string>();

View File

@ -115,7 +115,7 @@
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
@code { @code {
public override SecurityAccessLevelEnum SecurityAccessLevel { get { return SecurityAccessLevelEnum.Admin; } } public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } }
Dictionary<string, string> themes = new Dictionary<string, string>(); Dictionary<string, string> themes = new Dictionary<string, string>();
Dictionary<string, string> panelayouts = new Dictionary<string, string>(); Dictionary<string, string> panelayouts = new Dictionary<string, string>();

View File

@ -116,7 +116,7 @@
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
@code { @code {
public override SecurityAccessLevelEnum SecurityAccessLevel { get { return SecurityAccessLevelEnum.Admin; } } public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } }
Dictionary<string, string> themes = new Dictionary<string, string>(); Dictionary<string, string> themes = new Dictionary<string, string>();
Dictionary<string, string> panelayouts = new Dictionary<string, string>(); Dictionary<string, string> panelayouts = new Dictionary<string, string>();

View File

@ -36,5 +36,5 @@ else
} }
@code { @code {
public override SecurityAccessLevelEnum SecurityAccessLevel { get { return SecurityAccessLevelEnum.Admin; } } public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Admin; } }
} }

View File

@ -20,7 +20,7 @@
</div> </div>
@code { @code {
public override SecurityAccessLevelEnum SecurityAccessLevel { get { return SecurityAccessLevelEnum.Anonymous; } } public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Anonymous; } }
public string Username { get; set; } = ""; public string Username { get; set; } = "";
public string Password { get; set; } = ""; public string Password { get; set; } = "";
@ -28,6 +28,7 @@
private async Task RegisterUser() private async Task RegisterUser()
{ {
User user = new User(); User user = new User();
user.SiteId = PageState.Site.SiteId;
user.Username = Username; user.Username = Username;
user.DisplayName = Username; user.DisplayName = Username;
user.Roles = "Administrators;"; user.Roles = "Administrators;";

View File

@ -60,7 +60,7 @@ else
} }
@code { @code {
public override SecurityAccessLevelEnum SecurityAccessLevel { get { return SecurityAccessLevelEnum.Host; } } public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } }
List<Tenant> tenants; List<Tenant> tenants;
string tenantid; string tenantid;

View File

@ -31,7 +31,7 @@ else
} }
@code { @code {
public override SecurityAccessLevelEnum SecurityAccessLevel { get { return SecurityAccessLevelEnum.Host; } } public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } }
List<Site> sites; List<Site> sites;

View File

@ -29,7 +29,7 @@ else
} }
@code { @code {
public override SecurityAccessLevelEnum SecurityAccessLevel { get { return SecurityAccessLevelEnum.Host; } } public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } }
List<Theme> Themes; List<Theme> Themes;

View File

@ -30,7 +30,7 @@ else
} }
@code { @code {
public override SecurityAccessLevelEnum SecurityAccessLevel { get { return SecurityAccessLevelEnum.Host; } } public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } }
List<User> Users; List<User> Users;

View File

@ -54,23 +54,26 @@
if (moduleType != null) if (moduleType != null)
{ {
var moduleobject = Activator.CreateInstance(moduleType); var moduleobject = Activator.CreateInstance(moduleType);
SecurityAccessLevelEnum SecurityAccessLevel = (SecurityAccessLevelEnum)moduleType.GetProperty("SecurityAccessLevel").GetValue(moduleobject, null); SecurityAccessLevel SecurityAccessLevel = (SecurityAccessLevel)moduleType.GetProperty("SecurityAccessLevel").GetValue(moduleobject, null);
switch (SecurityAccessLevel) switch (SecurityAccessLevel)
{ {
case SecurityAccessLevelEnum.Anonymous: case SecurityAccessLevel.Anonymous:
authorized = true; authorized = true;
break; break;
case SecurityAccessLevelEnum.View: case SecurityAccessLevel.View:
authorized = UserService.IsAuthorized(PageState.User, ModuleState.ViewPermissions); authorized = UserService.IsAuthorized(PageState.User, ModuleState.ViewPermissions);
break; break;
case SecurityAccessLevelEnum.Edit: case SecurityAccessLevel.Edit:
authorized = UserService.IsAuthorized(PageState.User, ModuleState.EditPermissions); authorized = UserService.IsAuthorized(PageState.User, ModuleState.EditPermissions);
break; break;
case SecurityAccessLevelEnum.Admin: case SecurityAccessLevel.Admin:
authorized = UserService.IsAuthorized(PageState.User, Constants.AdminRole); authorized = UserService.IsAuthorized(PageState.User, Constants.AdminRole);
break; break;
case SecurityAccessLevelEnum.Host: case SecurityAccessLevel.Host:
if (PageState.User != null)
{
authorized = PageState.User.IsSuperUser; authorized = PageState.User.IsSuperUser;
}
break; break;
} }
} }

View File

@ -0,0 +1,44 @@
@using Oqtane.Modules
@inherits ModuleBase
@if (authorized)
{
<div class="@type">
@Message
</div>
}
@code {
[Parameter]
private MessageType Type { get; set; }
[Parameter]
private string Message { get; set; }
string type = "alert alert-success"; // optional
bool authorized = false;
protected override void OnInit()
{
if (PageState.User != null)
{
authorized = PageState.User.IsSuperUser;
}
switch (Type)
{
case MessageType.Success:
type = "alert alert-success";
break;
case MessageType.Info:
type = "alert alert-info";
break;
case MessageType.Warning:
type = "alert alert-warning";
break;
case MessageType.Error:
type = "alert alert-danger";
break;
}
}
}

View File

@ -25,7 +25,7 @@
</form> </form>
@code { @code {
public override SecurityAccessLevelEnum SecurityAccessLevel { get { return SecurityAccessLevelEnum.Edit; } } public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Edit; } }
public override string Title { get { return "Edit Html/Text"; } } public override string Title { get { return "Edit Html/Text"; } }
HtmlTextInfo htmltext; HtmlTextInfo htmltext;

View File

@ -16,7 +16,7 @@
@code { @code {
string content; string content;
protected override async Task OnInitAsync() protected override async Task OnParametersSetAsync()
{ {
HtmlTextService htmltextservice = new HtmlTextService(http, sitestate, UriHelper); HtmlTextService htmltextservice = new HtmlTextService(http, sitestate, UriHelper);
List<HtmlTextInfo> htmltext = await htmltextservice.GetHtmlTextAsync(ModuleState.ModuleId); List<HtmlTextInfo> htmltext = await htmltextservice.GetHtmlTextAsync(ModuleState.ModuleId);

View File

@ -3,7 +3,7 @@
public interface IModuleControl public interface IModuleControl
{ {
string Title { get; } string Title { get; }
SecurityAccessLevelEnum SecurityAccessLevel { get; } SecurityAccessLevel SecurityAccessLevel { get; }
string Actions { get; } // can be specified as a comma delimited set of values string Actions { get; } // can be specified as a comma delimited set of values
} }
} }

View File

@ -0,0 +1,10 @@
namespace Oqtane.Modules
{
public enum MessageType
{
Success,
Info,
Warning,
Error
}
}

View File

@ -14,7 +14,7 @@ namespace Oqtane.Modules
public virtual string Title { get { return ""; } } public virtual string Title { get { return ""; } }
public virtual SecurityAccessLevelEnum SecurityAccessLevel { get { return SecurityAccessLevelEnum.View; } set { } } // default security public virtual SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.View; } set { } } // default security
public virtual string Actions { get { return ""; } } public virtual string Actions { get { return ""; } }
@ -25,7 +25,12 @@ namespace Oqtane.Modules
public string NavigateUrl(string path) public string NavigateUrl(string path)
{ {
return Utilities.NavigateUrl(PageState.Alias.Path, path); return NavigateUrl(path, "");
}
public string NavigateUrl(string path, string parameters)
{
return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters);
} }
public string EditUrl(string action) public string EditUrl(string action)

View File

@ -1,6 +1,6 @@
namespace Oqtane.Modules namespace Oqtane.Modules
{ {
public enum SecurityAccessLevelEnum public enum SecurityAccessLevel
{ {
Anonymous, Anonymous,
View, View,

View File

@ -21,7 +21,7 @@
"environmentVariables": { "environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development" "ASPNETCORE_ENVIRONMENT": "Development"
}, },
"applicationUrl": "http://localhost:14246/" "applicationUrl": "http://localhost:44358/"
} }
} }
} }

View File

@ -7,6 +7,7 @@ namespace Oqtane.Services
public interface IPageModuleService public interface IPageModuleService
{ {
Task<List<PageModule>> GetPageModulesAsync(); Task<List<PageModule>> GetPageModulesAsync();
Task<PageModule> GetPageModuleAsync(int PageModuleId);
Task<PageModule> AddPageModuleAsync(PageModule PageModule); Task<PageModule> AddPageModuleAsync(PageModule PageModule);
Task<PageModule> UpdatePageModuleAsync(PageModule PageModule); Task<PageModule> UpdatePageModuleAsync(PageModule PageModule);
Task DeletePageModuleAsync(int PageModuleId); Task DeletePageModuleAsync(int PageModuleId);

View File

@ -0,0 +1,27 @@
using Oqtane.Models;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Oqtane.Services
{
public interface ISettingService
{
Task<List<Setting>> GetModuleSettingsAsync(int ModuleId);
Task<Setting> UpdateModuleSettingsAsync(List<Setting> ModuleSettings, int ModuleId, string SettingName, string SettingValue);
Task<List<Setting>> GetSettingsAsync(string EntityName, int EntityId);
Task<Setting> GetSettingAsync(int SettingId);
Task<Setting> AddSettingAsync(Setting Setting);
Task<Setting> UpdateSettingAsync(Setting Setting);
Task DeleteSettingAsync(int SettingId);
string GetSetting(List<Setting> Settings, string SettingName, string DefaultValue);
}
}

View File

@ -31,6 +31,11 @@ namespace Oqtane.Services
return await http.GetJsonAsync<List<PageModule>>(apiurl); return await http.GetJsonAsync<List<PageModule>>(apiurl);
} }
public async Task<PageModule> GetPageModuleAsync(int PageModuleId)
{
return await http.GetJsonAsync<PageModule>(apiurl + "/" + PageModuleId.ToString());
}
public async Task<PageModule> AddPageModuleAsync(PageModule PageModule) public async Task<PageModule> AddPageModuleAsync(PageModule PageModule)
{ {
return await http.PostJsonAsync<PageModule>(apiurl, PageModule); return await http.PostJsonAsync<PageModule>(apiurl, PageModule);

View File

@ -0,0 +1,92 @@
using Oqtane.Models;
using System.Threading.Tasks;
using System.Net.Http;
using System.Linq;
using Microsoft.AspNetCore.Components;
using System.Collections.Generic;
using Oqtane.Shared;
using System;
namespace Oqtane.Services
{
public class SettingService : ServiceBase, ISettingService
{
private readonly HttpClient http;
private readonly SiteState sitestate;
private readonly IUriHelper urihelper;
public SettingService(HttpClient http, SiteState sitestate, IUriHelper urihelper)
{
this.http = http;
this.sitestate = sitestate;
this.urihelper = urihelper;
}
private string apiurl
{
get { return CreateApiUrl(sitestate.Alias, urihelper.GetAbsoluteUri(), "Setting"); }
}
public async Task<List<Setting>> GetModuleSettingsAsync(int ModuleId)
{
return await GetSettingsAsync("Module", ModuleId);
}
public async Task<Setting> UpdateModuleSettingsAsync(List<Setting> ModuleSettings, int ModuleId, string SettingName, string SettingValue)
{
Setting setting = ModuleSettings.Where(item => item.SettingName == SettingName).FirstOrDefault();
if (setting == null)
{
setting = new Setting();
setting.EntityName = "Module";
setting.EntityId = ModuleId;
setting.SettingName = SettingName;
setting.SettingValue = SettingValue;
setting = await AddSettingAsync(setting);
}
else
{
setting.SettingValue = SettingValue;
setting = await UpdateSettingAsync(setting);
}
return setting;
}
public async Task<List<Setting>> GetSettingsAsync(string EntityName, int EntityId)
{
List<Setting> Settings = await http.GetJsonAsync<List<Setting>>(apiurl + "?entityname=" + EntityName + "&entityid=" + EntityId.ToString());
return Settings.OrderBy(item => item.SettingName).ToList();
}
public async Task<Setting> GetSettingAsync(int SettingId)
{
return await http.GetJsonAsync<Setting>(apiurl + "/" + SettingId.ToString());
}
public async Task<Setting> AddSettingAsync(Setting Setting)
{
return await http.PostJsonAsync<Setting>(apiurl, Setting);
}
public async Task<Setting> UpdateSettingAsync(Setting Setting)
{
return await http.PutJsonAsync<Setting>(apiurl + "/" + Setting.SettingId.ToString(), Setting);
}
public async Task DeleteSettingAsync(int SettingId)
{
await http.DeleteAsync(apiurl + "/" + SettingId.ToString());
}
public string GetSetting(List<Setting> Settings, string SettingName, string DefaultValue)
{
string value = DefaultValue;
Setting setting = Settings.Where(item => item.SettingName == SettingName).FirstOrDefault();
if (setting != null)
{
value = setting.SettingValue;
}
return value;
}
}
}

View File

@ -7,6 +7,7 @@
public const string DefaultAdminContainer = "Oqtane.Client.Themes.AdminContainer, Oqtane.Client"; public const string DefaultAdminContainer = "Oqtane.Client.Themes.AdminContainer, Oqtane.Client";
public const string DefaultSettingsControl = "Oqtane.Client.Modules.Admin.ModuleSettings.Index, Oqtane.Client"; public const string DefaultSettingsControl = "Oqtane.Client.Modules.Admin.ModuleSettings.Index, Oqtane.Client";
public const string PageManagementModule = "Oqtane.Client.Modules.Admin.Pages, Oqtane.Client"; public const string PageManagementModule = "Oqtane.Client.Modules.Admin.Pages, Oqtane.Client";
public const string ModuleMessageControl = "Oqtane.Client.Modules.Controls.ModuleMessage, Oqtane.Client";
public const string DefaultControl = "Index"; public const string DefaultControl = "Index";
public const string AdminPane = "Admin"; public const string AdminPane = "Admin";

View File

@ -1,5 +1,6 @@
@using Oqtane.Models @using Oqtane.Models
@using Oqtane.Shared @using Oqtane.Shared
@using Oqtane.Modules
<CascadingValue Value="@ModuleState"> <CascadingValue Value="@ModuleState">
@DynamicComponent @DynamicComponent
@ -26,12 +27,16 @@
Type containerType = Type.GetType(container); Type containerType = Type.GetType(container);
if (containerType != null) if (containerType != null)
{ {
builder.OpenComponent(ModuleState.ModuleId, containerType); // set sequence to moduleid so that component tree is able to differentiate builder.OpenComponent(0, containerType);
builder.CloseComponent(); builder.CloseComponent();
} }
else else
{ {
// container does not exist with type specified // container does not exist with type specified
builder.OpenComponent(0, Type.GetType(Constants.ModuleMessageControl));
builder.AddAttribute(1, "Type", MessageType.Error);
builder.AddAttribute(2, "Message", "Error Loading Module Container " + container);
builder.CloseComponent();
} }
} }
}; };

View File

@ -113,29 +113,23 @@
} }
@code { @code {
[Parameter]
private bool Installed { get; set; }
private string DatabaseType = "LocalDB"; private string DatabaseType = "LocalDB";
private string ServerName = "(LocalDb)\\MSSQLLocalDB"; private string ServerName = "(LocalDb)\\MSSQLLocalDB";
private string DatabaseName = "Oqtane-" + DateTime.Now.ToString("yyyyMMddHHmm"); private string DatabaseName = "Oqtane-" + DateTime.Now.ToString("yyyyMMddHHmm");
private string Username = ""; private string Username = "";
private string Password = ""; private string Password = "";
private string HostUsername = "host"; private string HostUsername = "host";
private string HostPassword = ""; private string HostPassword = "";
private string Message = ""; private string Message = "";
private string IntegratedSecurityDisplay = "display:none;"; private string IntegratedSecurityDisplay = "display:none;";
private string LoadingDisplay = "display:none;"; private string LoadingDisplay = "display:none;";
private bool Installed = true; private void SetIntegratedSecurity(UIChangeEventArgs e)
{
protected override async Task OnInitAsync()
{
var response = await InstallationService.IsInstalled();
Installed = response.Success;
}
private void SetIntegratedSecurity(UIChangeEventArgs e)
{
if (Convert.ToBoolean(e.Value)) if (Convert.ToBoolean(e.Value))
{ {
IntegratedSecurityDisplay = "display:none;"; IntegratedSecurityDisplay = "display:none;";
@ -144,10 +138,10 @@ private void SetIntegratedSecurity(UIChangeEventArgs e)
{ {
IntegratedSecurityDisplay = ""; IntegratedSecurityDisplay = "";
} }
} }
private async Task Install() private async Task Install()
{ {
if (HostPassword.Length >= 6) if (HostPassword.Length >= 6)
{ {
LoadingDisplay = ""; LoadingDisplay = "";
@ -175,6 +169,7 @@ private async Task Install()
if (response.Success) if (response.Success)
{ {
User user = new User(); User user = new User();
user.SiteId = 1;
user.Username = HostUsername; user.Username = HostUsername;
user.DisplayName = HostUsername; user.DisplayName = HostUsername;
user.Password = HostPassword; user.Password = HostPassword;
@ -193,5 +188,5 @@ private async Task Install()
{ {
Message = "<div class=\"alert alert-danger\" role=\"alert\">Password Must Be 6 Characters Or Greater</div>"; Message = "<div class=\"alert alert-danger\" role=\"alert\">Password Must Be 6 Characters Or Greater</div>";
} }
} }
} }

View File

@ -1,5 +1,6 @@
@using Oqtane.Models @using Oqtane.Models
@using Oqtane.Shared @using Oqtane.Shared
@using Oqtane.Modules
@DynamicComponent @DynamicComponent
@ -21,15 +22,23 @@
{ {
typename = Constants.DefaultSettingsControl; typename = Constants.DefaultSettingsControl;
} }
Type moduleType = Type.GetType(typename); Type moduleType = null;
if (typename != null)
{
moduleType = Type.GetType(typename);
}
if (moduleType != null) if (moduleType != null)
{ {
builder.OpenComponent(ModuleState.ModuleId, moduleType); // set sequence to moduleid so that component tree is able to differentiate builder.OpenComponent(0, moduleType);
builder.CloseComponent(); builder.CloseComponent();
} }
else else
{ {
// module control does not exist with typename specified // module does not exist with typename specified
builder.OpenComponent(0, Type.GetType(Constants.ModuleMessageControl));
builder.AddAttribute(1, "Type", MessageType.Error);
builder.AddAttribute(2, "Message", "Error Loading Component For Module " + ModuleState.ModuleDefinitionName);
builder.CloseComponent();
} }
}; };
} }

View File

@ -52,23 +52,23 @@
{ {
var moduleobject = Activator.CreateInstance(moduleType); var moduleobject = Activator.CreateInstance(moduleType);
// verify security access level for this module control // verify security access level for this module control
SecurityAccessLevelEnum SecurityAccessLevel = (SecurityAccessLevelEnum)moduleType.GetProperty("SecurityAccessLevel").GetValue(moduleobject, null); SecurityAccessLevel SecurityAccessLevel = (SecurityAccessLevel)moduleType.GetProperty("SecurityAccessLevel").GetValue(moduleobject, null);
bool authorized = false; bool authorized = false;
switch (SecurityAccessLevel) switch (SecurityAccessLevel)
{ {
case SecurityAccessLevelEnum.Anonymous: case SecurityAccessLevel.Anonymous:
authorized = true; authorized = true;
break; break;
case SecurityAccessLevelEnum.View: case SecurityAccessLevel.View:
authorized = UserService.IsAuthorized(PageState.User, module.ViewPermissions); authorized = UserService.IsAuthorized(PageState.User, module.ViewPermissions);
break; break;
case SecurityAccessLevelEnum.Edit: case SecurityAccessLevel.Edit:
authorized = UserService.IsAuthorized(PageState.User, module.EditPermissions); authorized = UserService.IsAuthorized(PageState.User, module.EditPermissions);
break; break;
case SecurityAccessLevelEnum.Admin: case SecurityAccessLevel.Admin:
authorized = UserService.IsAuthorized(PageState.User, Constants.AdminRole); authorized = UserService.IsAuthorized(PageState.User, Constants.AdminRole);
break; break;
case SecurityAccessLevelEnum.Host: case SecurityAccessLevel.Host:
authorized = PageState.User.IsSuperUser; authorized = PageState.User.IsSuperUser;
break; break;
} }

View File

@ -57,9 +57,17 @@
protected override async Task OnParametersSetAsync() protected override async Task OnParametersSetAsync()
{ {
if (PageState == null) if (PageState == null)
{
// misconfigured api calls should not be processed through the router
if (!_absoluteUri.Contains("~/api/"))
{ {
await Refresh(); await Refresh();
} }
else
{
System.Diagnostics.Debug.WriteLine(this.GetType().FullName + ": Error: " + _absoluteUri + " is not mapped to a Controller");
}
}
} }
private async Task Refresh() private async Task Refresh()

View File

@ -1,4 +1,5 @@
@using Oqtane.Shared @using Oqtane.Shared
@using Oqtane.Modules
@DynamicComponent @DynamicComponent
@ -20,6 +21,10 @@
else else
{ {
// theme does not exist with type specified // theme does not exist with type specified
builder.OpenComponent(0, Type.GetType(Constants.ModuleMessageControl));
builder.AddAttribute(1, "Type", MessageType.Error);
builder.AddAttribute(2, "Message", "Error Loading Page Theme " + PageState.Page.ThemeType);
builder.CloseComponent();
} }
}; };
} }

View File

@ -6,7 +6,7 @@ namespace Oqtane.Shared
public class Utilities public class Utilities
{ {
public static string NavigateUrl(string alias, string path) public static string NavigateUrl(string alias, string path, string parameters)
{ {
string url = ""; string url = "";
if (alias != "") if (alias != "")
@ -17,6 +17,10 @@ namespace Oqtane.Shared
{ {
url += path + "/"; url += path + "/";
} }
if (!string.IsNullOrEmpty(parameters))
{
url += "?" + parameters;
}
if (!url.StartsWith("/")) if (!url.StartsWith("/"))
{ {
url = "/" + url; url = "/" + url;
@ -26,7 +30,7 @@ namespace Oqtane.Shared
public static string EditUrl(string alias, string path, int moduleid, string action, string parameters) public static string EditUrl(string alias, string path, int moduleid, string action, string parameters)
{ {
string url = NavigateUrl(alias, path); string url = NavigateUrl(alias, path, "");
if ( url == "/" ) if ( url == "/" )
{ {
url = ""; url = "";

View File

@ -46,7 +46,7 @@ namespace Oqtane.Client
services.AddScoped<IModuleService, ModuleService>(); services.AddScoped<IModuleService, ModuleService>();
services.AddScoped<IPageModuleService, PageModuleService>(); services.AddScoped<IPageModuleService, PageModuleService>();
services.AddScoped<IUserService, UserService>(); services.AddScoped<IUserService, UserService>();
services.AddScoped<ISettingService, SettingService>();
// dynamically register module contexts and repository services // dynamically register module contexts and repository services
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
@ -57,7 +57,7 @@ namespace Oqtane.Client
.ToArray(); .ToArray();
foreach (Type implementationtype in implementationtypes) foreach (Type implementationtype in implementationtypes)
{ {
Type servicetype = Type.GetType(implementationtype.FullName.Replace(implementationtype.Name, "I" + implementationtype.Name)); Type servicetype = Type.GetType(implementationtype.AssemblyQualifiedName.Replace(implementationtype.Name, "I" + implementationtype.Name));
if (servicetype != null) if (servicetype != null)
{ {
services.AddScoped(servicetype, implementationtype); // traditional service interface services.AddScoped(servicetype, implementationtype); // traditional service interface

View File

@ -21,14 +21,13 @@ namespace Oqtane.Themes
public string NavigateUrl(string path) public string NavigateUrl(string path)
{ {
return Utilities.NavigateUrl(PageState.Alias.Path, path); return NavigateUrl(path, "");
} }
public string EditUrl(string action) public string NavigateUrl(string path, string parameters)
{ {
return EditUrl(ModuleState.ModuleId, action); return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters);
} }
public string EditUrl(string action, string parameters) public string EditUrl(string action, string parameters)
{ {
return EditUrl(ModuleState.ModuleId, action, parameters); return EditUrl(ModuleState.ModuleId, action, parameters);

View File

@ -6,11 +6,12 @@
@code { @code {
string logo = ""; string logo = "";
protected override void OnInit() protected override Task OnParametersSetAsync()
{ {
if (PageState.Site.Logo != "") if (PageState.Site.Logo != "")
{ {
logo = "<a href=\"" + PageState.Alias.Url + "\"><img src=\"/Sites/" + PageState.Site.SiteId.ToString() + "/" + PageState.Site.Logo + "\" alt=\"" + PageState.Site.Name + "\"/></a>"; logo = "<a href=\"" + PageState.Alias.Url + "\"><img src=\"/Sites/" + PageState.Site.SiteId.ToString() + "/" + PageState.Site.Logo + "\" alt=\"" + PageState.Site.Name + "\"/></a>";
} }
return Task.CompletedTask;
} }
} }

View File

@ -34,7 +34,7 @@
List<Page> pages; List<Page> pages;
Page parent = null; Page parent = null;
protected override void OnInit() protected override Task OnParametersSetAsync()
{ {
// if current page has no children // if current page has no children
if (PageState.Pages.Where(item => item.ParentId == PageState.Page.PageId).FirstOrDefault() == null) if (PageState.Pages.Where(item => item.ParentId == PageState.Page.PageId).FirstOrDefault() == null)
@ -54,5 +54,6 @@
// current page is parent // current page is parent
parent = PageState.Pages.Where(item => item.ParentId == PageState.Page.ParentId).FirstOrDefault(); parent = PageState.Pages.Where(item => item.ParentId == PageState.Page.ParentId).FirstOrDefault();
} }
return Task.CompletedTask;
} }
} }

View File

@ -6,12 +6,13 @@
@code { @code {
string title = ""; string title = "";
protected override void OnInit() protected override Task OnParametersSetAsync()
{ {
title = ModuleState.Title; title = ModuleState.Title;
if (PageState.Control == "Settings") if (PageState.Control == "Settings")
{ {
title = PageState.Control; title = PageState.Control;
} }
return Task.CompletedTask;
} }
} }

View File

@ -17,7 +17,12 @@ namespace Oqtane.Themes
public string NavigateUrl(string path) public string NavigateUrl(string path)
{ {
return Utilities.NavigateUrl(PageState.Alias.Path, path); return NavigateUrl(path, "");
}
public string NavigateUrl(string path, string parameters)
{
return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters);
} }
} }

View File

@ -15,7 +15,12 @@ namespace Oqtane.Themes
public string NavigateUrl(string path) public string NavigateUrl(string path)
{ {
return Utilities.NavigateUrl(PageState.Alias.Path, path); return NavigateUrl(path, "");
}
public string NavigateUrl(string path, string parameters)
{
return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters);
} }
public string EditUrl(int moduleid, string action) public string EditUrl(int moduleid, string action)

View File

@ -1,11 +1,15 @@
using DbUp; using DbUp;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Oqtane.Models; using Oqtane.Models;
using Oqtane.Repository;
using System; using System;
using System.Collections.Generic;
using System.Data.SqlClient; using System.Data.SqlClient;
using System.IO; using System.IO;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading; using System.Threading;
@ -182,11 +186,71 @@ namespace Oqtane.Controllers
response.Success = !dbUpgrade.IsUpgradeRequired(); response.Success = !dbUpgrade.IsUpgradeRequired();
if (!response.Success) if (!response.Success)
{ {
response.Message = "Scripts Have Not Been Run"; response.Message = "Master Installation Scripts Have Not Been Executed";
}
else
{
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies()
.Where(item => item.FullName.Contains(".Module.")).ToArray();
// get tenants
using (var db = new InstallationContext(connectionString))
{
foreach (Tenant tenant in db.Tenant.ToList())
{
// upgrade framework
dbUpgradeConfig = DeployChanges.To.SqlDatabase(tenant.DBConnectionString)
.WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly());
dbUpgrade = dbUpgradeConfig.Build();
if (dbUpgrade.IsUpgradeRequired())
{
var result = dbUpgrade.PerformUpgrade();
if (!result.Successful)
{
// TODO: log result.Error.Message;
}
}
// iterate through Oqtane module assemblies and execute any database scripts
foreach (Assembly assembly in assemblies)
{
InstallModule(assembly, tenant.DBConnectionString);
}
}
}
} }
} }
return response; return response;
} }
private void InstallModule(Assembly assembly, string connectionstring)
{
var dbUpgradeConfig = DeployChanges.To.SqlDatabase(connectionstring)
.WithScriptsEmbeddedInAssembly(assembly); // scripts must be included as Embedded Resources
var dbUpgrade = dbUpgradeConfig.Build();
if (dbUpgrade.IsUpgradeRequired())
{
var result = dbUpgrade.PerformUpgrade();
if (!result.Successful)
{
// TODO: log result.Error.Message;
}
}
}
}
public class InstallationContext : DbContext
{
private readonly string _connectionString;
public InstallationContext(string connectionString)
{
_connectionString = connectionString;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseSqlServer(_connectionString);
public virtual DbSet<Tenant> Tenant { get; set; }
} }
} }

View File

@ -0,0 +1,61 @@
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Oqtane.Repository;
using Oqtane.Models;
namespace Oqtane.Controllers
{
[Route("{site}/api/[controller]")]
public class SettingController : Controller
{
private readonly ISettingRepository Settings;
public SettingController(ISettingRepository Settings)
{
this.Settings = Settings;
}
// GET: api/<controller>
[HttpGet]
public IEnumerable<Setting> Get(string entityname, int entityid)
{
return Settings.GetSettings(entityname, entityid);
}
// GET api/<controller>/5
[HttpGet("{id}")]
public Setting Get(int id)
{
return Settings.GetSetting(id);
}
// POST api/<controller>
[HttpPost]
public Setting Post([FromBody] Setting Setting)
{
if (ModelState.IsValid)
{
Setting = Settings.AddSetting(Setting);
}
return Setting;
}
// PUT api/<controller>/5
[HttpPut("{id}")]
public Setting Put(int id, [FromBody] Setting Setting)
{
if (ModelState.IsValid)
{
Setting = Settings.UpdateSetting(Setting);
}
return Setting;
}
// DELETE api/<controller>/5
[HttpDelete("{id}")]
public void Delete(int id)
{
Settings.DeleteSetting(id);
}
}
}

View File

@ -4,6 +4,7 @@ using Oqtane.Repository;
using Oqtane.Models; using Oqtane.Models;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Linq;
namespace Oqtane.Controllers namespace Oqtane.Controllers
{ {
@ -11,12 +12,14 @@ namespace Oqtane.Controllers
public class UserController : Controller public class UserController : Controller
{ {
private readonly IUserRepository users; private readonly IUserRepository users;
private readonly ISiteUserRepository siteusers;
private readonly UserManager<IdentityUser> identityUserManager; private readonly UserManager<IdentityUser> identityUserManager;
private readonly SignInManager<IdentityUser> identitySignInManager; private readonly SignInManager<IdentityUser> identitySignInManager;
public UserController(IUserRepository Users, UserManager<IdentityUser> IdentityUserManager, SignInManager<IdentityUser> IdentitySignInManager) public UserController(IUserRepository Users, ISiteUserRepository SiteUsers, UserManager<IdentityUser> IdentityUserManager, SignInManager<IdentityUser> IdentitySignInManager)
{ {
users = Users; users = Users;
siteusers = SiteUsers;
identityUserManager = IdentityUserManager; identityUserManager = IdentityUserManager;
identitySignInManager = IdentitySignInManager; identitySignInManager = IdentitySignInManager;
} }
@ -39,6 +42,8 @@ namespace Oqtane.Controllers
[HttpPost] [HttpPost]
public async Task<User> Post([FromBody] User User) public async Task<User> Post([FromBody] User User)
{ {
User user = null;
if (ModelState.IsValid) if (ModelState.IsValid)
{ {
IdentityUser identityuser = await identityUserManager.FindByNameAsync(User.Username); IdentityUser identityuser = await identityUserManager.FindByNameAsync(User.Username);
@ -50,11 +55,17 @@ namespace Oqtane.Controllers
var result = await identityUserManager.CreateAsync(identityuser, User.Password); var result = await identityUserManager.CreateAsync(identityuser, User.Password);
if (result.Succeeded) if (result.Succeeded)
{ {
User = users.AddUser(User); user = users.AddUser(User);
SiteUser SiteUser = new SiteUser();
SiteUser.SiteId = User.SiteId;
SiteUser.UserId = user.UserId;
SiteUser.IsAuthorized = true;
siteusers.AddSiteUser(SiteUser);
} }
} }
} }
return User;
return user;
} }
// PUT api/<controller>/5 // PUT api/<controller>/5
@ -84,36 +95,38 @@ namespace Oqtane.Controllers
// POST api/<controller>/login // POST api/<controller>/login
[HttpPost("login")] [HttpPost("login")]
public async Task<User> Login([FromBody] User user) public async Task<User> Login([FromBody] User User)
{ {
User user = new Models.User { Username = User.Username, IsAuthenticated = false };
if (ModelState.IsValid) if (ModelState.IsValid)
{ {
IdentityUser identityuser = await identityUserManager.FindByNameAsync(user.Username); IdentityUser identityuser = await identityUserManager.FindByNameAsync(User.Username);
if (identityuser != null) if (identityuser != null)
{ {
var result = await identitySignInManager.CheckPasswordSignInAsync(identityuser, user.Password, false); var result = await identitySignInManager.CheckPasswordSignInAsync(identityuser, User.Password, false);
if (result.Succeeded) if (result.Succeeded)
{ {
await identitySignInManager.SignInAsync(identityuser, user.IsPersistent);
user = users.GetUser(identityuser.UserName); user = users.GetUser(identityuser.UserName);
if (user != null)
{
SiteUser siteuser = siteusers.GetSiteUsers(User.SiteId, user.UserId).FirstOrDefault();
if (siteuser.IsAuthorized)
{
await identitySignInManager.SignInAsync(identityuser, User.IsPersistent);
user.IsAuthenticated = true; user.IsAuthenticated = true;
} }
else
{
user = new Models.User { Username = user.Username, IsAuthenticated = false };
} }
} }
else
{
user = new Models.User { Username = user.Username, IsAuthenticated = false };
} }
} }
return user; return user;
} }
// POST api/<controller>/logout // POST api/<controller>/logout
[HttpPost("logout")] [HttpPost("logout")]
public async Task Logout([FromBody] User user) public async Task Logout([FromBody] User User)
{ {
await identitySignInManager.SignOutAsync(); await identitySignInManager.SignOutAsync();
} }

View File

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
namespace Microsoft.Extensions.DependencyInjection
{
public static class MvcModuleExtensions
{
public static IMvcBuilder AddModuleAssemblies(this IMvcBuilder mvcBuilder, List<Assembly> assemblies)
{
// load MVC application parts from module assemblies
foreach (Assembly assembly in assemblies)
{
// check if assembly contains MVC Controllers
if (assembly.GetTypes().Where(item => item.IsSubclassOf(typeof(Controller))).ToArray().Length > 0)
{
var partFactory = ApplicationPartFactory.GetApplicationPartFactory(assembly);
foreach (var part in partFactory.GetApplicationParts(assembly))
{
mvcBuilder.PartManager.ApplicationParts.Add(part);
}
}
}
return mvcBuilder;
}
}
}

View File

@ -26,14 +26,14 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Remove="Scripts\Identity.sql" /> <None Remove="Scripts\00.00.01.sql" />
<None Remove="Scripts\Master.sql" /> <None Remove="Scripts\Master.sql" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="Scripts\Master.sql" /> <Content Include="Scripts\Master.sql" />
<EmbeddedResource Include="Scripts\Identity.sql" /> <EmbeddedResource Include="Scripts\00.00.01.sql" />
<EmbeddedResource Include="Scripts\Tenant.sql" /> <EmbeddedResource Include="Scripts\00.00.00.sql" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -12,6 +12,7 @@ namespace Oqtane.Repository
public virtual DbSet<Module> Module { get; set; } public virtual DbSet<Module> Module { get; set; }
public virtual DbSet<User> User { get; set; } public virtual DbSet<User> User { get; set; }
public virtual DbSet<SiteUser> SiteUser { get; set; } public virtual DbSet<SiteUser> SiteUser { get; set; }
public virtual DbSet<Setting> Setting { get; set; }
public TenantDBContext(ITenantResolver TenantResolver, IHttpContextAccessor accessor) : base(TenantResolver, accessor) public TenantDBContext(ITenantResolver TenantResolver, IHttpContextAccessor accessor) : base(TenantResolver, accessor)
{ {

View File

@ -0,0 +1,14 @@
using System.Collections.Generic;
using Oqtane.Models;
namespace Oqtane.Repository
{
public interface ISettingRepository
{
IEnumerable<Setting> GetSettings(string EntityName, int EntityId);
Setting AddSetting(Setting Setting);
Setting UpdateSetting(Setting Setting);
Setting GetSetting(int SettingId);
void DeleteSetting(int SettingId);
}
}

View File

@ -22,13 +22,12 @@ namespace Oqtane.Repository
List<ModuleDefinition> moduledefinitions = new List<ModuleDefinition>(); List<ModuleDefinition> moduledefinitions = new List<ModuleDefinition>();
// iterate through Oqtane module assemblies // iterate through Oqtane module assemblies
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies()
{ .Where(item => item.FullName.StartsWith("Oqtane.") || item.FullName.Contains(".Module.")).ToArray();
if (assembly.FullName.StartsWith("Oqtane.Client") || assembly.FullName.StartsWith("Oqtane.Module.")) foreach (Assembly assembly in assemblies)
{ {
moduledefinitions = LoadModuleDefinitionsFromAssembly(moduledefinitions, assembly); moduledefinitions = LoadModuleDefinitionsFromAssembly(moduledefinitions, assembly);
} }
}
return moduledefinitions; return moduledefinitions;
} }
@ -119,6 +118,5 @@ namespace Oqtane.Repository
return moduledefinitions; return moduledefinitions;
} }
} }
} }

View File

@ -0,0 +1,85 @@
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using Oqtane.Models;
namespace Oqtane.Repository
{
public class SettingRepository : ISettingRepository
{
private TenantDBContext db;
public SettingRepository(TenantDBContext context)
{
db = context;
}
public IEnumerable<Setting> GetSettings(string EntityName, int EntityId)
{
try
{
return db.Setting.Where(item => item.EntityName == EntityName)
.Where(item => item.EntityId == EntityId).ToList();
}
catch
{
throw;
}
}
public Setting AddSetting(Setting Setting)
{
try
{
db.Setting.Add(Setting);
db.SaveChanges();
return Setting;
}
catch
{
throw;
}
}
public Setting UpdateSetting(Setting Setting)
{
try
{
db.Entry(Setting).State = EntityState.Modified;
db.SaveChanges();
return Setting;
}
catch
{
throw;
}
}
public Setting GetSetting(int SettingId)
{
try
{
Setting Setting = db.Setting.Find(SettingId);
return Setting;
}
catch
{
throw;
}
}
public void DeleteSetting(int SettingId)
{
try
{
Setting Setting = db.Setting.Find(SettingId);
db.Setting.Remove(Setting);
db.SaveChanges();
}
catch
{
throw;
}
}
}
}

View File

@ -22,14 +22,12 @@ namespace Oqtane.Repository
List<Theme> themes = new List<Theme>(); List<Theme> themes = new List<Theme>();
// iterate through Oqtane theme assemblies // iterate through Oqtane theme assemblies
// TODO: Remove restriction on assembly Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies()
.Where(item => item.FullName.StartsWith("Oqtane.") || item.FullName.Contains(".Theme.")).ToArray();
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
if (assembly.FullName.StartsWith("Oqtane.Client") || assembly.FullName.StartsWith("Oqtane.Theme."))
{ {
themes = LoadThemesFromAssembly(themes, assembly); themes = LoadThemesFromAssembly(themes, assembly);
} }
}
return themes; return themes;
} }

View File

@ -127,6 +127,23 @@ CREATE TABLE [dbo].[SiteUser](
) )
) )
GO GO
CREATE TABLE [dbo].[Setting](
[SettingId] [int] IDENTITY(1,1) NOT NULL,
[EntityName] [nvarchar](50) NOT NULL,
[EntityId] [int] NOT NULL,
[SettingName] [nvarchar](50) NOT NULL,
[SettingValue] [nvarchar](max) NOT NULL,
[CreatedBy] [nvarchar](256) NOT NULL,
[CreatedOn] [datetime] NOT NULL,
[ModifiedBy] [nvarchar](256) NOT NULL,
[ModifiedOn] [datetime] NOT NULL,
CONSTRAINT [PK_Setting] PRIMARY KEY CLUSTERED
(
[SettingId] ASC
)
)
GO
/* /*
Create foreign key relationships Create foreign key relationships
@ -167,6 +184,20 @@ GO
/* /*
Create indexes
*/
CREATE UNIQUE NONCLUSTERED INDEX IX_Setting ON dbo.Setting
(
EntityName,
EntityId,
SettingName
) ON [PRIMARY]
GO
/*
Create seed data Create seed data
*/ */
@ -303,19 +334,19 @@ INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane]
VALUES (2, 1, 2, N'Counter', N'Left', 1, N'Oqtane.Client.Themes.Theme1.Container1, Oqtane.Client', '', getdate(), '', getdate()) VALUES (2, 1, 2, N'Counter', N'Left', 1, N'Oqtane.Client.Themes.Theme1.Container1, Oqtane.Client', '', getdate(), '', getdate())
GO GO
INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane], [Order], [ContainerType], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane], [Order], [ContainerType], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn])
VALUES (3, 1, 3, N'Text', N'Left', 0, N'Oqtane.Client.Themes.Theme1.Container1, Oqtane.Client', '', getdate(), '', getdate()) VALUES (3, 1, 3, N'Lorem ipsum', N'Left', 0, N'Oqtane.Client.Themes.Theme1.Container1, Oqtane.Client', '', getdate(), '', getdate())
GO GO
INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane], [Order], [ContainerType], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane], [Order], [ContainerType], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn])
VALUES (4, 2, 4, N'Weather', N'Top', 0, N'Oqtane.Client.Themes.Theme2.Container2, Oqtane.Client', '', getdate(), '', getdate()) VALUES (4, 2, 4, N'Weather', N'Top', 0, N'Oqtane.Client.Themes.Theme2.Container2, Oqtane.Client', '', getdate(), '', getdate())
GO GO
INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane], [Order], [ContainerType], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane], [Order], [ContainerType], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn])
VALUES (5, 2, 5, N'Text', N'Top', 1, N'Oqtane.Client.Themes.Theme1.Container1, Oqtane.Client', '', getdate(), '', getdate()) VALUES (5, 2, 5, N'Enim sed', N'Top', 1, N'Oqtane.Client.Themes.Theme1.Container1, Oqtane.Client', '', getdate(), '', getdate())
GO GO
INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane], [Order], [ContainerType], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane], [Order], [ContainerType], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn])
VALUES (6, 3, 6, N'Text', N'Left', 0, N'Oqtane.Client.Themes.Theme1.Container1, Oqtane.Client', '', getdate(), '', getdate()) VALUES (6, 3, 6, N'Id consectetur', N'Left', 0, N'Oqtane.Client.Themes.Theme1.Container1, Oqtane.Client', '', getdate(), '', getdate())
GO GO
INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane], [Order], [ContainerType], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane], [Order], [ContainerType], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn])
VALUES (7, 3, 7, N'Text', N'Right', 0, N'Oqtane.Client.Themes.Theme1.Container1, Oqtane.Client', '', getdate(), '', getdate()) VALUES (7, 3, 7, N'Ornare arcu', N'Right', 0, N'Oqtane.Client.Themes.Theme1.Container1, Oqtane.Client', '', getdate(), '', getdate())
GO GO
INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane], [Order], [ContainerType], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane], [Order], [ContainerType], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn])
VALUES (8, 5, 8, N'Page Management', N'Top', 0, N'Oqtane.Client.Themes.Theme2.Container2, Oqtane.Client', '', getdate(), '', getdate()) VALUES (8, 5, 8, N'Page Management', N'Top', 0, N'Oqtane.Client.Themes.Theme2.Container2, Oqtane.Client', '', getdate(), '', getdate())
@ -342,10 +373,10 @@ INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane]
VALUES (15, 11, 15, N'Theme Management', N'Top', 0, N'Oqtane.Client.Themes.Theme2.Container2, Oqtane.Client', '', getdate(), '', getdate()) VALUES (15, 11, 15, N'Theme Management', N'Top', 0, N'Oqtane.Client.Themes.Theme2.Container2, Oqtane.Client', '', getdate(), '', getdate())
GO GO
INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane], [Order], [ContainerType], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane], [Order], [ContainerType], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn])
VALUES (16, 12, 16, N'Text', N'Top', 1, N'Oqtane.Client.Themes.Theme2.Container2, Oqtane.Client', '', getdate(), '', getdate()) VALUES (16, 12, 16, N'Id consectetur', N'Top', 1, N'Oqtane.Client.Themes.Theme2.Container2, Oqtane.Client', '', getdate(), '', getdate())
GO GO
INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane], [Order], [ContainerType], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane], [Order], [ContainerType], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn])
VALUES (17, 13, 17, N'Text', N'Top', 1, N'Oqtane.Client.Themes.Theme2.Container2, Oqtane.Client', '', getdate(), '', getdate()) VALUES (17, 13, 17, N'Lorem ipsum', N'Top', 1, N'Oqtane.Client.Themes.Theme2.Container2, Oqtane.Client', '', getdate(), '', getdate())
GO GO
INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane], [Order], [ContainerType], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn]) INSERT [dbo].[PageModule] ([PageModuleId], [PageId], [ModuleId], [Title], [Pane], [Order], [ContainerType], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn])
VALUES (18, 14, 18, N'Login', N'Top', 0, N'Oqtane.Client.Themes.Theme2.Container2, Oqtane.Client', '', getdate(), '', getdate()) VALUES (18, 14, 18, N'Login', N'Top', 0, N'Oqtane.Client.Themes.Theme2.Container2, Oqtane.Client', '', getdate(), '', getdate())

View File

@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.ResponseCompression; using Microsoft.AspNetCore.ResponseCompression; // needed for WASM
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System.Linq; using System.Linq;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
@ -19,6 +19,7 @@ using Microsoft.AspNetCore.Components;
using Oqtane.Shared; using Oqtane.Shared;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Collections.Generic;
namespace Oqtane.Server namespace Oqtane.Server
{ {
@ -77,27 +78,7 @@ namespace Oqtane.Server
services.AddScoped<IModuleService, ModuleService>(); services.AddScoped<IModuleService, ModuleService>();
services.AddScoped<IPageModuleService, PageModuleService>(); services.AddScoped<IPageModuleService, PageModuleService>();
services.AddScoped<IUserService, UserService>(); services.AddScoped<IUserService, UserService>();
services.AddScoped<ISettingService, SettingService>();
// dynamically register module contexts and repository services
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly assembly in assemblies)
{
Type[] implementationtypes = assembly.GetTypes()
.Where(item => item.GetInterfaces().Contains(typeof(IService)))
.ToArray();
foreach (Type implementationtype in implementationtypes)
{
Type servicetype = Type.GetType(implementationtype.FullName.Replace(implementationtype.Name, "I" + implementationtype.Name));
if (servicetype != null)
{
services.AddScoped(servicetype, implementationtype); // traditional service interface
}
else
{
services.AddScoped(implementationtype, implementationtype); // no interface defined for service
}
}
}
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
@ -139,9 +120,26 @@ namespace Oqtane.Server
}; };
}); });
services.AddMemoryCache(); // get list of loaded assemblies
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
services.AddMvc().AddNewtonsoftJson(); // iterate through Oqtane module assemblies in /bin ( filter is narrow to optimize loading process )
string path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
DirectoryInfo folder = new DirectoryInfo(path);
List<Assembly> moduleassemblies = new List<Assembly>();
foreach (FileInfo file in folder.EnumerateFiles("*.Module.*.dll"))
{
// check if assembly is already loaded
Assembly assembly = assemblies.Where(item => item.Location == file.FullName).FirstOrDefault();
if (assembly == null)
{
// load assembly ( as long as dependencies are in /bin they will load as well )
assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(file.FullName);
moduleassemblies.Add(assembly);
}
}
services.AddMvc().AddModuleAssemblies(moduleassemblies).AddNewtonsoftJson();
// register singleton scoped core services // register singleton scoped core services
services.AddSingleton<IConfigurationRoot>(Configuration); services.AddSingleton<IConfigurationRoot>(Configuration);
@ -157,26 +155,12 @@ namespace Oqtane.Server
services.AddTransient<IModuleRepository, ModuleRepository>(); services.AddTransient<IModuleRepository, ModuleRepository>();
services.AddTransient<IPageModuleRepository, PageModuleRepository>(); services.AddTransient<IPageModuleRepository, PageModuleRepository>();
services.AddTransient<IUserRepository, UserRepository>(); services.AddTransient<IUserRepository, UserRepository>();
services.AddTransient<ISiteUserRepository, SiteUserRepository>();
services.AddTransient<ISettingRepository, SettingRepository>();
// get list of loaded assemblies // dynamically register module services, contexts, and repository classes
assemblies = AppDomain.CurrentDomain.GetAssemblies(); assemblies = AppDomain.CurrentDomain.GetAssemblies()
// get path to /bin .Where(item => item.FullName.StartsWith("Oqtane.") || item.FullName.Contains(".Module.")).ToArray();
string path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
DirectoryInfo folder = new DirectoryInfo(path);
// iterate through Oqtane assemblies in /bin ( filter is narrow to optimize loading process )
foreach (FileInfo file in folder.EnumerateFiles("Oqtane.*.dll"))
{
// check if assembly is already loaded
Assembly assembly = assemblies.Where(item => item.Location == file.FullName).FirstOrDefault();
if (assembly == null)
{
// load assembly ( as long as dependencies are in /bin they will load as well )
assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(file.FullName);
}
}
// dynamically register module contexts and repository services
assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly assembly in assemblies) foreach (Assembly assembly in assemblies)
{ {
Type[] implementationtypes = assembly.GetTypes() Type[] implementationtypes = assembly.GetTypes()
@ -184,7 +168,7 @@ namespace Oqtane.Server
.ToArray(); .ToArray();
foreach (Type implementationtype in implementationtypes) foreach (Type implementationtype in implementationtypes)
{ {
Type servicetype = Type.GetType(implementationtype.FullName.Replace(implementationtype.Name, "I" + implementationtype.Name)); Type servicetype = Type.GetType(implementationtype.AssemblyQualifiedName.Replace(implementationtype.Name, "I" + implementationtype.Name));
if (servicetype != null) if (servicetype != null)
{ {
services.AddScoped(servicetype, implementationtype); // traditional service interface services.AddScoped(servicetype, implementationtype); // traditional service interface
@ -273,9 +257,26 @@ namespace Oqtane.Server
}; };
}); });
services.AddMemoryCache(); // get list of loaded assemblies
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
services.AddMvc().AddNewtonsoftJson(); // iterate through Oqtane module assemblies in /bin ( filter is narrow to optimize loading process )
string path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
DirectoryInfo folder = new DirectoryInfo(path);
List<Assembly> moduleassemblies = new List<Assembly>();
foreach (FileInfo file in folder.EnumerateFiles("*.Module.*.dll"))
{
// check if assembly is already loaded
Assembly assembly = assemblies.Where(item => item.Location == file.FullName).FirstOrDefault();
if (assembly == null)
{
// load assembly ( as long as dependencies are in /bin they will load as well )
assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(file.FullName);
moduleassemblies.Add(assembly);
}
}
services.AddMvc().AddModuleAssemblies(moduleassemblies).AddNewtonsoftJson();
// register singleton scoped core services // register singleton scoped core services
services.AddSingleton<IConfigurationRoot>(Configuration); services.AddSingleton<IConfigurationRoot>(Configuration);
@ -291,26 +292,12 @@ namespace Oqtane.Server
services.AddTransient<IModuleRepository, ModuleRepository>(); services.AddTransient<IModuleRepository, ModuleRepository>();
services.AddTransient<IPageModuleRepository, PageModuleRepository>(); services.AddTransient<IPageModuleRepository, PageModuleRepository>();
services.AddTransient<IUserRepository, UserRepository>(); services.AddTransient<IUserRepository, UserRepository>();
services.AddTransient<ISiteUserRepository, SiteUserRepository>();
services.AddTransient<ISettingRepository, SettingRepository>();
// get list of loaded assemblies // dynamically register module services, contexts, and repository classes
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); assemblies = AppDomain.CurrentDomain.GetAssemblies()
// get path to /bin .Where(item => item.FullName.StartsWith("Oqtane.") || item.FullName.Contains(".Module.")).ToArray();
string path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
DirectoryInfo folder = new DirectoryInfo(path);
// iterate through Oqtane assemblies in /bin ( filter is narrow to optimize loading process )
foreach (FileInfo file in folder.EnumerateFiles("Oqtane.*.dll"))
{
// check if assembly is already loaded
Assembly assembly = assemblies.Where(item => item.Location == file.FullName).FirstOrDefault();
if (assembly == null)
{
// load assembly ( as long as dependencies are in /bin they will load as well )
assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(file.FullName);
}
}
// dynamically register module contexts and repository services
assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly assembly in assemblies) foreach (Assembly assembly in assemblies)
{ {
Type[] implementationtypes = assembly.GetTypes() Type[] implementationtypes = assembly.GetTypes()
@ -318,7 +305,7 @@ namespace Oqtane.Server
.ToArray(); .ToArray();
foreach (Type implementationtype in implementationtypes) foreach (Type implementationtype in implementationtypes)
{ {
Type servicetype = Type.GetType(implementationtype.FullName.Replace(implementationtype.Name, "I" + implementationtype.Name)); Type servicetype = Type.GetType(implementationtype.AssemblyQualifiedName.Replace(implementationtype.Name, "I" + implementationtype.Name));
if (servicetype != null) if (servicetype != null)
{ {
services.AddScoped(servicetype, implementationtype); // traditional service interface services.AddScoped(servicetype, implementationtype); // traditional service interface

View File

@ -0,0 +1,18 @@
using System;
namespace Oqtane.Models
{
public class Setting : IAuditable
{
public int SettingId { get; set; }
public string EntityName { get; set; }
public int EntityId { get; set; }
public string SettingName { get; set; }
public string SettingValue { get; set; }
public string CreatedBy { get; set; }
public DateTime CreatedOn { get; set; }
public string ModifiedBy { get; set; }
public DateTime ModifiedOn { get; set; }
}
}