commit
47a917a3df
|
@ -55,20 +55,26 @@ else
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading Modules {Error}", ex.Message);
|
||||
AddModuleMessage("Error Loading Modules", MessageType.Error);
|
||||
if (_moduleDefinitions == null)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading Modules {Error}", ex.Message);
|
||||
AddModuleMessage("Error Loading Modules", MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool UpgradeAvailable(string moduledefinitionname, string version)
|
||||
{
|
||||
var upgradeavailable = false;
|
||||
var package = _packages.Where(item => item.PackageId == Utilities.GetTypeName(moduledefinitionname)).FirstOrDefault();
|
||||
if (package != null)
|
||||
if (_packages != null)
|
||||
{
|
||||
upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0);
|
||||
var package = _packages.Where(item => item.PackageId == Utilities.GetTypeName(moduledefinitionname)).FirstOrDefault();
|
||||
if (package != null)
|
||||
{
|
||||
upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return upgradeavailable;
|
||||
}
|
||||
|
||||
|
@ -86,7 +92,7 @@ else
|
|||
await logger.LogError(ex, "Error Downloading Module {ModuleDefinitionName} {Version} {Error}", moduledefinitionname, version, ex.Message);
|
||||
AddModuleMessage("Error Downloading Module", MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DeleteModule(ModuleDefinition moduleDefinition)
|
||||
{
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
@namespace Oqtane.Modules.Admin.Themes
|
||||
@using System.Net
|
||||
@inherits ModuleBase
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IThemeService ThemeService
|
||||
@inject IPackageService PackageService
|
||||
|
||||
@if (themes == null)
|
||||
@if (_themes == null)
|
||||
{
|
||||
<p><em>Loading...</em></p>
|
||||
}
|
||||
|
@ -12,68 +13,99 @@ else
|
|||
{
|
||||
<ActionLink Action="Add" Text="Install Theme" />
|
||||
|
||||
<Pager Items="@themes">
|
||||
<Pager Items="@_themes">
|
||||
<Header>
|
||||
<th> </th>
|
||||
<th> </th>
|
||||
<th>Name</th>
|
||||
<th>Version</th>
|
||||
<th> </th>
|
||||
</Header>
|
||||
<Row>
|
||||
<td><ActionLink Action="View" Parameters="@($"name=" + WebUtility.UrlEncode(context.ThemeName))" /></td>
|
||||
<td>
|
||||
@if (context.AssemblyName != "Oqtane.Client")
|
||||
{
|
||||
{
|
||||
<ActionDialog Header="Delete Theme" Message="@("Are You Sure You Wish To Delete The " + context.Name + " Theme?")" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteTheme(context))" />
|
||||
}
|
||||
}
|
||||
</td>
|
||||
<td>@context.Name</td>
|
||||
<td>@context.Version</td>
|
||||
<td>
|
||||
@if (UpgradeAvailable(context.ThemeName, context.Version))
|
||||
{
|
||||
{
|
||||
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadTheme(context.ThemeName, context.Version))>Upgrade</button>
|
||||
}
|
||||
}
|
||||
</td>
|
||||
</Row>
|
||||
</Pager>
|
||||
}
|
||||
|
||||
@code {
|
||||
private List<Theme> themes;
|
||||
private List<Package> packages;
|
||||
private List<Theme> _themes;
|
||||
private List<Package> _packages;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
themes = await ThemeService.GetThemesAsync();
|
||||
packages = await PackageService.GetPackagesAsync("module");
|
||||
try
|
||||
{
|
||||
_themes = await ThemeService.GetThemesAsync();
|
||||
_packages = await PackageService.GetPackagesAsync("theme");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (_themes == null)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading Themes {Error}", ex.Message);
|
||||
AddModuleMessage("Error Loading Themes", MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool UpgradeAvailable(string themename, string version)
|
||||
{
|
||||
var upgradeavailable = false;
|
||||
var package = packages.Where(item => item.PackageId == Utilities.GetTypeName(themename)).FirstOrDefault();
|
||||
if (package != null)
|
||||
if (_packages != null)
|
||||
{
|
||||
upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0);
|
||||
var package = _packages.Where(item => item.PackageId == Utilities.GetTypeName(themename)).FirstOrDefault();
|
||||
if (package != null)
|
||||
{
|
||||
upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0);
|
||||
}
|
||||
}
|
||||
|
||||
return upgradeavailable;
|
||||
}
|
||||
|
||||
private async Task DownloadTheme(string themename, string version)
|
||||
{
|
||||
await PackageService.DownloadPackageAsync(themename, version, "Themes");
|
||||
await logger.LogInformation("Theme Downloaded {ThemeName} {Version}", themename, version);
|
||||
await ThemeService.InstallThemesAsync();
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
try
|
||||
{
|
||||
await PackageService.DownloadPackageAsync(themename, version, "Themes");
|
||||
await ThemeService.InstallThemesAsync();
|
||||
await logger.LogInformation("Theme Downloaded {ThemeName} {Version}", themename, version);
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Downloading Theme {ThemeName} {Version} {Error}", themename, version, ex.Message);
|
||||
AddModuleMessage("Error Downloading Theme", MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DeleteTheme(Theme Theme)
|
||||
{
|
||||
await ThemeService.DeleteThemeAsync(Theme.ThemeName);
|
||||
await logger.LogInformation("Theme Deleted {Theme}", Theme);
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
try
|
||||
{
|
||||
await ThemeService.DeleteThemeAsync(Theme.ThemeName);
|
||||
await logger.LogInformation("Theme Deleted {Theme}", Theme);
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Deleting Theme {Theme} {Error}", Theme, ex.Message);
|
||||
AddModuleMessage("Error Deleting Theme", MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
101
Oqtane.Client/Modules/Admin/Themes/View.razor
Normal file
101
Oqtane.Client/Modules/Admin/Themes/View.razor
Normal file
|
@ -0,0 +1,101 @@
|
|||
@namespace Oqtane.Modules.Admin.Themes
|
||||
@using System.Net
|
||||
@inherits ModuleBase
|
||||
@inject IThemeService ThemeService
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="name" HelpText="The name of the theme">Name: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="name" class="form-control" @bind="@_name" disabled />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="themename" HelpText="The internal name of the module">Internal Name: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="themename" class="form-control" @bind="@_themeName" disabled />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="version" HelpText="The version of the thene">Version: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="version" class="form-control" @bind="@_version" disabled />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="owner" HelpText="The owner or creator of the theme">Owner: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="owner" class="form-control" @bind="@_owner" disabled />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="url" HelpText="The reference url of the theme">Reference Url: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="url" class="form-control" @bind="@_url" disabled />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="contact" HelpText="The contact for the theme">Contact: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<input id="contact" class="form-control" @bind="@_contact" disabled />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<Label For="license" HelpText="The license of the theme">License: </Label>
|
||||
</td>
|
||||
<td>
|
||||
<textarea id="license" class="form-control" @bind="@_license" rows="5" disabled></textarea>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
|
||||
|
||||
@code {
|
||||
private string _themeName = "";
|
||||
private string _name;
|
||||
private string _version;
|
||||
private string _owner = "";
|
||||
private string _url = "";
|
||||
private string _contact = "";
|
||||
private string _license = "";
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_themeName = WebUtility.UrlDecode(PageState.QueryString["name"]);
|
||||
var themes = await ThemeService.GetThemesAsync();
|
||||
var theme = themes.FirstOrDefault(item => item.ThemeName == _themeName);
|
||||
if (theme != null)
|
||||
{
|
||||
_name = theme.Name;
|
||||
_version = theme.Version;
|
||||
_owner = theme.Owner;
|
||||
_url = theme.Url;
|
||||
_contact = theme.Contact;
|
||||
_license = theme.License;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading Theme {ThemeName} {Error}", _themeName, ex.Message);
|
||||
AddModuleMessage("Error Loading Theme", MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -43,28 +43,54 @@
|
|||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
List<Package> packages = await PackageService.GetPackagesAsync("framework");
|
||||
_package = packages.FirstOrDefault();
|
||||
if (_package != null)
|
||||
try
|
||||
{
|
||||
_upgradeavailable = (Version.Parse(_package.Version).CompareTo(Version.Parse(Constants.Version)) > 0);
|
||||
List<Package> packages = await PackageService.GetPackagesAsync("framework");
|
||||
if (packages != null)
|
||||
{
|
||||
_package = packages.FirstOrDefault();
|
||||
if (_package != null)
|
||||
{
|
||||
_upgradeavailable = (Version.Parse(_package.Version).CompareTo(Version.Parse(Constants.Version)) > 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
_package = new Package { Name = Constants.PackageId, Version = Constants.Version };
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
catch
|
||||
{
|
||||
_package = new Package { Name = Constants.PackageId, Version = Constants.Version };
|
||||
// can be caused by no network connection
|
||||
}
|
||||
}
|
||||
|
||||
private async Task Upgrade()
|
||||
{
|
||||
await InstallationService.Upgrade();
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
try
|
||||
{
|
||||
await InstallationService.Upgrade();
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Executing Upgrade {Error}", ex.Message);
|
||||
AddModuleMessage("Error Executing Upgrade", MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task Download(string packageid, string version)
|
||||
{
|
||||
await PackageService.DownloadPackageAsync(packageid, version, "Framework");
|
||||
await InstallationService.Upgrade();
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
try
|
||||
{
|
||||
await PackageService.DownloadPackageAsync(packageid, version, "Framework");
|
||||
await InstallationService.Upgrade();
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Downloading Framework {Error}", ex.Message);
|
||||
AddModuleMessage("Error Downloading Framework", MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,11 @@
|
|||
}
|
||||
else
|
||||
{
|
||||
<ActionLink Action="Add" Text="Add User" />
|
||||
<ActionLink Action="Add" Text="Add User"/>
|
||||
|
||||
<div class="d-flex p-1">
|
||||
<input class="form-control mr-4" @bind="@search"/><button class="btn btn-outline-primary ml-1" @onclick="OnSearch">Search</button>
|
||||
</div>
|
||||
|
||||
<Pager Items="@userroles">
|
||||
<Header>
|
||||
|
@ -28,16 +32,32 @@ else
|
|||
}
|
||||
|
||||
@code {
|
||||
private List<UserRole> allroles;
|
||||
private List<UserRole> userroles;
|
||||
private string search;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
userroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId);
|
||||
userroles = userroles.Where(item => item.Role.Name == Constants.RegisteredRole).ToList();
|
||||
allroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId);
|
||||
userroles = allroles.Where(item => item.Role.Name == Constants.RegisteredRole).ToList();
|
||||
}
|
||||
|
||||
private void OnSearch()
|
||||
{
|
||||
userroles = allroles
|
||||
.Where(item => item.Role.Name == Constants.RegisteredRole &&
|
||||
(
|
||||
item.User.Username.Contains(search, StringComparison.OrdinalIgnoreCase) ||
|
||||
item.User.Email.Contains(search, StringComparison.OrdinalIgnoreCase) ||
|
||||
item.User.DisplayName.Contains(search, StringComparison.OrdinalIgnoreCase)
|
||||
)
|
||||
)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
|
||||
private async Task DeleteUser(UserRole UserRole)
|
||||
{
|
||||
try
|
||||
|
@ -56,4 +76,4 @@ else
|
|||
AddModuleMessage(ex.Message, MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
if (p.PageId == PageState.Page.PageId)
|
||||
{
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="@NavigateUrl(p.Path)">
|
||||
<a class="nav-link" href="@GetUrl(p)" target="@GetTarget(p)" >
|
||||
@if (p.Icon != string.Empty)
|
||||
{
|
||||
<span class="oi oi-@p.Icon" aria-hidden="true"></span>
|
||||
|
@ -28,7 +28,7 @@
|
|||
else
|
||||
{
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="@NavigateUrl(p.Path)">
|
||||
<a class="nav-link" href="@GetUrl(p)" target="@GetTarget(p)" >
|
||||
@if (p.Icon != string.Empty)
|
||||
{
|
||||
<span class="oi oi-@p.Icon" aria-hidden="true"></span>
|
||||
|
|
|
@ -13,6 +13,13 @@
|
|||
{
|
||||
var interop = new Interop(JsRuntime);
|
||||
|
||||
// handle page redirection
|
||||
if (!string.IsNullOrEmpty(PageState.Page.Url))
|
||||
{
|
||||
NavigationManager.NavigateTo(PageState.Page.Url);
|
||||
return;
|
||||
}
|
||||
|
||||
// set page title
|
||||
if (!string.IsNullOrEmpty(PageState.Page.Title))
|
||||
{
|
||||
|
@ -24,6 +31,7 @@
|
|||
}
|
||||
|
||||
// include page resources
|
||||
string batch = DateTime.Now.ToString("yyyyMMddHHmmssfff");
|
||||
var links = new List<object>();
|
||||
var scripts = new List<object>();
|
||||
foreach (Resource resource in PageState.Page.Resources)
|
||||
|
@ -31,18 +39,18 @@
|
|||
switch (resource.ResourceType)
|
||||
{
|
||||
case ResourceType.Stylesheet:
|
||||
links.Add(new { id = "app-stylesheet" + links.Count.ToString("00"), rel = "stylesheet", href = resource.Url, type = "text/css", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", key = "" });
|
||||
links.Add(new { id = "app-stylesheet-" + batch + "-" + (links.Count + 1).ToString("00"), rel = "stylesheet", href = resource.Url, type = "text/css", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", key = "" });
|
||||
break;
|
||||
case ResourceType.Script:
|
||||
scripts.Add(new { id = "app-script" + scripts.Count.ToString("00"), src = resource.Url, integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", content = "", location = "body", key = "" });
|
||||
scripts.Add(new { id = "app-script-" + batch + "-" + (scripts.Count + 1).ToString("00"), src = resource.Url, integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", content = "", location = "body", key = "" });
|
||||
break;
|
||||
}
|
||||
}
|
||||
await interop.IncludeLinks(links.ToArray());
|
||||
await interop.IncludeScripts(scripts.ToArray());
|
||||
// remove any page resource references which are no longer required for this page
|
||||
await interop.RemoveElementsById("app-stylesheet", "app-stylesheet" + links.Count.ToString("00"), "");
|
||||
await interop.RemoveElementsById("app-script", "app-script" + scripts.Count.ToString("00"), "");
|
||||
await interop.RemoveElementsById("app-stylesheet", "", "app-stylesheet-" + batch + "-00");
|
||||
await interop.RemoveElementsById("app-script", "", "app-script-" + batch + "-00");
|
||||
|
||||
// add favicon
|
||||
if (PageState.Site.FaviconFileId != null)
|
||||
|
|
|
@ -47,7 +47,7 @@ namespace Oqtane.Controllers
|
|||
}
|
||||
|
||||
// GET api/<controller>/name/xxx?sync=yyyyMMddHHmmssfff
|
||||
[HttpGet("name/{name}")]
|
||||
[HttpGet("name/{**name}")]
|
||||
public Alias Get(string name, string sync)
|
||||
{
|
||||
List<Alias> aliases = _aliases.GetAliases().ToList(); // cached
|
||||
|
|
|
@ -23,7 +23,13 @@
|
|||
<EmbeddedResource Remove="wwwroot\Modules\Templates\**" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Scripts\Master.1.0.1.sql" />
|
||||
<None Remove="Scripts\Tenant.1.0.1.sql" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Scripts\Master.1.0.1.sql" />
|
||||
<EmbeddedResource Include="Scripts\Master.0.9.0.sql" />
|
||||
<EmbeddedResource Include="Scripts\Tenant.1.0.1.sql" />
|
||||
<EmbeddedResource Include="Scripts\Tenant.0.9.0.sql" />
|
||||
<EmbeddedResource Include="Scripts\Tenant.0.9.1.sql" />
|
||||
<EmbeddedResource Include="Scripts\Tenant.0.9.2.sql" />
|
||||
|
|
|
@ -188,6 +188,7 @@ namespace Oqtane.Repository
|
|||
private List<ModuleDefinition> LoadModuleDefinitionsFromAssembly(List<ModuleDefinition> moduledefinitions, Assembly assembly)
|
||||
{
|
||||
ModuleDefinition moduledefinition;
|
||||
|
||||
Type[] modulecontroltypes = assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(IModuleControl))).ToArray();
|
||||
foreach (Type modulecontroltype in modulecontroltypes)
|
||||
{
|
||||
|
@ -195,11 +196,11 @@ namespace Oqtane.Repository
|
|||
if (modulecontroltype.Name == "ModuleBase"
|
||||
|| modulecontroltype.IsGenericType
|
||||
|| modulecontroltype.IsAbstract
|
||||
|| Attribute.IsDefined(modulecontroltype, typeof(OqtaneIgnoreAttribute))
|
||||
|| modulecontroltype.IsOqtaneIgnore()
|
||||
) continue;
|
||||
|
||||
string moduleNamespace = modulecontroltype.Namespace;
|
||||
string qualifiedModuleType = moduleNamespace + ", " + modulecontroltype.Assembly.GetName().Name;
|
||||
// create namespace root typename
|
||||
string qualifiedModuleType = modulecontroltype.Namespace + ", " + modulecontroltype.Assembly.GetName().Name;
|
||||
|
||||
int index = moduledefinitions.FindIndex(item => item.ModuleDefinitionName == qualifiedModuleType);
|
||||
if (index == -1)
|
||||
|
@ -208,7 +209,7 @@ namespace Oqtane.Repository
|
|||
Type moduletype = assembly
|
||||
.GetTypes()
|
||||
.Where(item => item.Namespace != null)
|
||||
.Where(item => item.Namespace.StartsWith(moduleNamespace))
|
||||
.Where(item => item.Namespace == modulecontroltype.Namespace || item.Namespace.StartsWith(modulecontroltype.Namespace + "."))
|
||||
.FirstOrDefault(item => item.GetInterfaces().Contains(typeof(IModule)));
|
||||
if (moduletype != null)
|
||||
{
|
||||
|
@ -221,8 +222,8 @@ namespace Oqtane.Repository
|
|||
// set default property values
|
||||
moduledefinition = new ModuleDefinition
|
||||
{
|
||||
Name = moduleNamespace.Substring(moduleNamespace.LastIndexOf(".") + 1),
|
||||
Description = "Manage " + moduleNamespace.Substring(moduleNamespace.LastIndexOf(".") + 1),
|
||||
Name = Utilities.GetTypeNameLastSegment(modulecontroltype.Namespace, 0),
|
||||
Description = "Manage " + Utilities.GetTypeNameLastSegment(modulecontroltype.Namespace, 0),
|
||||
Categories = ((qualifiedModuleType.StartsWith("Oqtane.Modules.Admin.")) ? "Admin" : "")
|
||||
};
|
||||
}
|
||||
|
@ -230,7 +231,7 @@ namespace Oqtane.Repository
|
|||
// set internal properties
|
||||
moduledefinition.ModuleDefinitionName = qualifiedModuleType;
|
||||
moduledefinition.Version = ""; // will be populated from database
|
||||
moduledefinition.ControlTypeTemplate = moduleNamespace + "." + Constants.ActionToken + ", " + modulecontroltype.Assembly.GetName().Name;
|
||||
moduledefinition.ControlTypeTemplate = modulecontroltype.Namespace + "." + Constants.ActionToken + ", " + modulecontroltype.Assembly.GetName().Name;
|
||||
moduledefinition.AssemblyName = assembly.GetName().Name;
|
||||
|
||||
if (string.IsNullOrEmpty(moduledefinition.Categories))
|
||||
|
|
|
@ -44,26 +44,34 @@ namespace Oqtane.Repository
|
|||
private List<Theme> LoadThemesFromAssembly(List<Theme> themes, Assembly assembly)
|
||||
{
|
||||
Theme theme;
|
||||
List<Type> themeTypes = new List<Type>();
|
||||
|
||||
Type[] themeControlTypes = assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(IThemeControl))).ToArray();
|
||||
foreach (Type themeControlType in themeControlTypes)
|
||||
{
|
||||
// Check if type should be ignored
|
||||
if (themeControlType.Name == "ThemeBase"
|
||||
|| themeControlType.IsGenericType
|
||||
|| Attribute.IsDefined(themeControlType, typeof(OqtaneIgnoreAttribute))
|
||||
|| themeControlType.IsAbstract
|
||||
|| themeControlType.IsOqtaneIgnore()
|
||||
) continue;
|
||||
|
||||
string themeNamespace = themeControlType.Namespace;
|
||||
string qualifiedModuleType = themeNamespace + ", " + themeControlType.Assembly.GetName().Name;
|
||||
// create namespace root typename
|
||||
string qualifiedThemeType = themeControlType.Namespace + ", " + themeControlType.Assembly.GetName().Name;
|
||||
|
||||
int index = themes.FindIndex(item => item.ThemeName == themeNamespace);
|
||||
int index = themes.FindIndex(item => item.ThemeName == qualifiedThemeType);
|
||||
if (index == -1)
|
||||
{
|
||||
// determine if this theme implements ITheme
|
||||
Type themetype = assembly.GetTypes()
|
||||
// Find all types in the assembly with the same namespace root
|
||||
themeTypes = assembly.GetTypes()
|
||||
.Where(item => !item.IsOqtaneIgnore())
|
||||
.Where(item => item.Namespace != null)
|
||||
.Where(item => item.Namespace.StartsWith(themeNamespace))
|
||||
.Where(item => item.GetInterfaces().Contains(typeof(ITheme))).FirstOrDefault();
|
||||
.Where(item => item.Namespace == themeControlType.Namespace || item.Namespace.StartsWith(themeControlType.Namespace + "."))
|
||||
.ToList();
|
||||
|
||||
// determine if this theme implements ITheme
|
||||
Type themetype = themeTypes
|
||||
.FirstOrDefault(item => item.GetInterfaces().Contains(typeof(ITheme)));
|
||||
if (themetype != null)
|
||||
{
|
||||
var themeobject = Activator.CreateInstance(themetype) as ITheme;
|
||||
|
@ -78,21 +86,19 @@ namespace Oqtane.Repository
|
|||
};
|
||||
}
|
||||
// set internal properties
|
||||
theme.ThemeName = themeNamespace;
|
||||
theme.ThemeName = qualifiedThemeType;
|
||||
theme.ThemeControls = "";
|
||||
theme.PaneLayouts = "";
|
||||
theme.ContainerControls = "";
|
||||
theme.AssemblyName = assembly.FullName.Split(",")[0];
|
||||
themes.Add(theme);
|
||||
index = themes.FindIndex(item => item.ThemeName == themeNamespace);
|
||||
index = themes.FindIndex(item => item.ThemeName == qualifiedThemeType);
|
||||
}
|
||||
theme = themes[index];
|
||||
theme.ThemeControls += (themeControlType.FullName + ", " + themeControlType.Assembly.GetName().Name + ";");
|
||||
|
||||
// layouts
|
||||
Type[] layouttypes = assembly.GetTypes()
|
||||
.Where(item => item.Namespace != null)
|
||||
.Where(item => item.Namespace.StartsWith(themeNamespace))
|
||||
Type[] layouttypes = themeTypes
|
||||
.Where(item => item.GetInterfaces().Contains(typeof(ILayoutControl))).ToArray();
|
||||
foreach (Type layouttype in layouttypes)
|
||||
{
|
||||
|
@ -104,9 +110,7 @@ namespace Oqtane.Repository
|
|||
}
|
||||
|
||||
// containers
|
||||
Type[] containertypes = assembly.GetTypes()
|
||||
.Where(item => item.Namespace != null)
|
||||
.Where(item => item.Namespace.StartsWith(themeNamespace))
|
||||
Type[] containertypes = themeTypes
|
||||
.Where(item => item.GetInterfaces().Contains(typeof(IContainerControl))).ToArray();
|
||||
foreach (Type containertype in containertypes)
|
||||
{
|
||||
|
|
29
Oqtane.Server/Scripts/Master.1.0.1.sql
Normal file
29
Oqtane.Server/Scripts/Master.1.0.1.sql
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
|
||||
Version 1.0.1 Master migration script
|
||||
|
||||
*/
|
||||
|
||||
CREATE UNIQUE NONCLUSTERED INDEX IX_Tenant ON [dbo].[Tenant]
|
||||
(
|
||||
[Name]
|
||||
) ON [PRIMARY]
|
||||
GO
|
||||
|
||||
CREATE UNIQUE NONCLUSTERED INDEX IX_Alias ON [dbo].[Alias]
|
||||
(
|
||||
[Name]
|
||||
) ON [PRIMARY]
|
||||
GO
|
||||
|
||||
CREATE UNIQUE NONCLUSTERED INDEX IX_ModuleDefinition ON [dbo].[ModuleDefinition]
|
||||
(
|
||||
[ModuleDefinitionName]
|
||||
) ON [PRIMARY]
|
||||
GO
|
||||
|
||||
CREATE UNIQUE NONCLUSTERED INDEX IX_Job ON [dbo].[Job]
|
||||
(
|
||||
[JobType]
|
||||
) ON [PRIMARY]
|
||||
GO
|
33
Oqtane.Server/Scripts/Tenant.1.0.1.sql
Normal file
33
Oqtane.Server/Scripts/Tenant.1.0.1.sql
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
|
||||
Version 1.0.1 Tenant migration script
|
||||
|
||||
*/
|
||||
|
||||
CREATE UNIQUE NONCLUSTERED INDEX IX_Site ON [dbo].[Site]
|
||||
(
|
||||
[TenantId],
|
||||
[Name]
|
||||
) ON [PRIMARY]
|
||||
GO
|
||||
|
||||
CREATE UNIQUE NONCLUSTERED INDEX IX_Role ON [dbo].[Role]
|
||||
(
|
||||
[SiteId],
|
||||
[Name]
|
||||
) ON [PRIMARY]
|
||||
GO
|
||||
|
||||
CREATE UNIQUE NONCLUSTERED INDEX IX_Profile ON [dbo].[Profile]
|
||||
(
|
||||
[SiteId],
|
||||
[Name]
|
||||
) ON [PRIMARY]
|
||||
GO
|
||||
|
||||
CREATE UNIQUE NONCLUSTERED INDEX IX_File ON [dbo].[File]
|
||||
(
|
||||
[FolderId],
|
||||
[Name]
|
||||
) ON [PRIMARY]
|
||||
GO
|
|
@ -45,6 +45,9 @@ Oqtane.Interop = {
|
|||
document.head.appendChild(meta);
|
||||
}
|
||||
else {
|
||||
if (id !== "") {
|
||||
meta.setAttribute("id", id);
|
||||
}
|
||||
if (meta.content !== content) {
|
||||
meta.setAttribute("content", content);
|
||||
}
|
||||
|
@ -77,6 +80,9 @@ Oqtane.Interop = {
|
|||
document.head.appendChild(link);
|
||||
}
|
||||
else {
|
||||
if (link.id !== id) {
|
||||
link.setAttribute('id', id);
|
||||
}
|
||||
if (link.rel !== rel) {
|
||||
link.setAttribute('rel', rel);
|
||||
}
|
||||
|
@ -148,6 +154,9 @@ Oqtane.Interop = {
|
|||
});
|
||||
}
|
||||
else {
|
||||
if (script.id !== id) {
|
||||
script.setAttribute('id', id);
|
||||
}
|
||||
if (src !== "") {
|
||||
if (script.src !== this.getAbsoluteUrl(src)) {
|
||||
script.removeAttribute('integrity');
|
||||
|
|
|
@ -79,5 +79,10 @@ namespace System.Reflection
|
|||
.Where(a => a.GetTypes<IModuleControl>().Any() || a.GetTypes<IThemeControl>().Any() || a.GetTypes<IClientStartup>().Any())
|
||||
.Where(a => Utilities.GetFullTypeName(a.GetName().Name) != "Oqtane.Client");
|
||||
}
|
||||
|
||||
public static bool IsOqtaneIgnore(this Type type)
|
||||
{
|
||||
return Attribute.IsDefined(type, typeof(OqtaneIgnoreAttribute));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using Oqtane.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
@ -15,7 +16,7 @@ namespace Oqtane.Shared
|
|||
{
|
||||
if (type == null) return null;
|
||||
var assemblyFullName = type.Assembly.FullName;
|
||||
var assemblyName = assemblyFullName.Substring(0, assemblyFullName.IndexOf(",", StringComparison.Ordinal));
|
||||
var assemblyName = assemblyFullName.Substring(0, assemblyFullName.IndexOf(",", StringComparison.Ordinal));
|
||||
return $"{type.Namespace}, {assemblyName}";
|
||||
}
|
||||
|
||||
|
@ -24,10 +25,10 @@ namespace Oqtane.Shared
|
|||
var uriBuilder = new UriBuilder
|
||||
{
|
||||
Path = !string.IsNullOrEmpty(alias)
|
||||
? (!string.IsNullOrEmpty(path))
|
||||
? $"{alias}/{path}"
|
||||
: $"{alias}"
|
||||
: $"{path}",
|
||||
? (!string.IsNullOrEmpty(path))
|
||||
? $"{alias}/{path}"
|
||||
: $"{alias}"
|
||||
: $"{path}",
|
||||
Query = parameters
|
||||
};
|
||||
|
||||
|
@ -44,6 +45,7 @@ namespace Oqtane.Shared
|
|||
path += $"/{action}";
|
||||
}
|
||||
}
|
||||
|
||||
return NavigateUrl(alias, path, parameters);
|
||||
}
|
||||
|
||||
|
@ -98,6 +100,7 @@ namespace Oqtane.Shared
|
|||
// remove assembly if fully qualified type
|
||||
typename = typename.Substring(0, typename.IndexOf(","));
|
||||
}
|
||||
|
||||
// segment 0 is the last segment, segment 1 is the second to last segment, etc...
|
||||
string[] segments = typename.Split('.');
|
||||
string name = "";
|
||||
|
@ -105,6 +108,7 @@ namespace Oqtane.Shared
|
|||
{
|
||||
name = segments[segments.Length - (segment + 1)];
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
|
@ -142,11 +146,14 @@ namespace Oqtane.Shared
|
|||
stringBuilder.Append('-');
|
||||
prevdash = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
result = stringBuilder.ToString().Trim('-');
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -236,25 +243,28 @@ namespace Oqtane.Shared
|
|||
if (string.IsNullOrEmpty(email)) return false;
|
||||
|
||||
return Regex.IsMatch(email,
|
||||
@"^(?("")("".+?(?<!\\)""@)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])@))" +
|
||||
@"(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-0-9a-z]*[0-9a-z]*\.)+[a-z0-9][\-a-z0-9]{0,22}[a-z0-9]))$",
|
||||
RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(250));
|
||||
@"^(?("")("".+?(?<!\\)""@)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])@))" +
|
||||
@"(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-0-9a-z]*[0-9a-z]*\.)+[a-z0-9][\-a-z0-9]{0,22}[a-z0-9]))$",
|
||||
RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(250));
|
||||
}
|
||||
|
||||
public static string PathCombine(params string[] segments)
|
||||
{
|
||||
var separators = new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar };
|
||||
var separators = new char[] {Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar};
|
||||
|
||||
for (int i =1;i < segments.Length; i++){
|
||||
if(Path.IsPathRooted(segments[i])){
|
||||
for (int i = 1; i < segments.Length; i++)
|
||||
{
|
||||
if (Path.IsPathRooted(segments[i]))
|
||||
{
|
||||
segments[i] = segments[i].TrimStart(separators);
|
||||
if(String.IsNullOrEmpty(segments[i])){
|
||||
segments[i]=" ";
|
||||
if (String.IsNullOrEmpty(segments[i]))
|
||||
{
|
||||
segments[i] = " ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Path.Combine(segments).TrimEnd();
|
||||
|
||||
return Path.Combine(segments).TrimEnd();
|
||||
}
|
||||
|
||||
public static bool IsPathValid(this Folder folder)
|
||||
|
@ -273,5 +283,54 @@ namespace Oqtane.Shared
|
|||
!Constants.InvalidFileNameEndingChars.Any(name.EndsWith) &&
|
||||
!Constants.ReservedDevices.Split(',').Contains(name.ToUpper().Split('.')[0]));
|
||||
}
|
||||
|
||||
|
||||
public static bool TryGetQueryValue(
|
||||
this Uri uri,
|
||||
string key,
|
||||
out string value,
|
||||
string defaultValue = null)
|
||||
{
|
||||
value = defaultValue;
|
||||
string query = uri.Query;
|
||||
return !string.IsNullOrEmpty(query) && Utilities.ParseQueryString(query).TryGetValue(key, out value);
|
||||
}
|
||||
|
||||
public static bool TryGetQueryValueInt(
|
||||
this Uri uri,
|
||||
string key,
|
||||
out int value,
|
||||
int defaultValue = 0)
|
||||
{
|
||||
value = defaultValue;
|
||||
string s;
|
||||
return uri.TryGetQueryValue(key, out s, (string) null) && int.TryParse(s, out value);
|
||||
}
|
||||
|
||||
public static Dictionary<string, string> ParseQueryString(string query)
|
||||
{
|
||||
Dictionary<string, string> dictionary = new Dictionary<string, string>();
|
||||
if (!string.IsNullOrEmpty(query))
|
||||
{
|
||||
query = query.Substring(1);
|
||||
string str = query;
|
||||
char[] separator = new char[1] {'&'};
|
||||
foreach (string key in str.Split(separator, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
if (key != "")
|
||||
{
|
||||
if (key.Contains("="))
|
||||
{
|
||||
string[] strArray = key.Split('=', StringSplitOptions.None);
|
||||
dictionary.Add(strArray[0], strArray[1]);
|
||||
}
|
||||
else
|
||||
dictionary.Add(key, "true");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dictionary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,9 +51,11 @@ V1.0.0 (MVP)
|
|||
# Background
|
||||
Oqtane was created by [Shaun Walker](https://www.linkedin.com/in/shaunbrucewalker/) and is inspired by the DotNetNuke web application framework. Initially created as a proof of concept, Oqtane is a native Blazor application written from the ground up using modern .NET Core technology. It is a modular application framework offering a fully dynamic page compositing model, multi-site support, designer friendly templates (skins), and extensibility via third party modules.
|
||||
|
||||
# Release Announcement
|
||||
# Release Announcements
|
||||
|
||||
[Announcing Oqtane... a Modular Application Framework for Blazor!](https://www.oqtane.org/Resources/Blog/PostId/520/announcing-oqtane-a-modular-application-framework-for-blazor)
|
||||
[Oqtane 1.0](https://www.oqtane.org/Resources/Blog/PostId/540/announcing-oqtane-10-a-modular-application-framework-for-blazor)
|
||||
|
||||
[Oqtane POC](https://www.oqtane.org/Resources/Blog/PostId/520/announcing-oqtane-a-modular-application-framework-for-blazor)
|
||||
|
||||
# Example Screenshots
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user