commit
47a917a3df
|
@ -55,20 +55,26 @@ else
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Loading Modules {Error}", ex.Message);
|
if (_moduleDefinitions == null)
|
||||||
AddModuleMessage("Error Loading Modules", MessageType.Error);
|
{
|
||||||
|
await logger.LogError(ex, "Error Loading Modules {Error}", ex.Message);
|
||||||
|
AddModuleMessage("Error Loading Modules", MessageType.Error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool UpgradeAvailable(string moduledefinitionname, string version)
|
private bool UpgradeAvailable(string moduledefinitionname, string version)
|
||||||
{
|
{
|
||||||
var upgradeavailable = false;
|
var upgradeavailable = false;
|
||||||
var package = _packages.Where(item => item.PackageId == Utilities.GetTypeName(moduledefinitionname)).FirstOrDefault();
|
if (_packages != null)
|
||||||
if (package != 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;
|
return upgradeavailable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +92,7 @@ else
|
||||||
await logger.LogError(ex, "Error Downloading Module {ModuleDefinitionName} {Version} {Error}", moduledefinitionname, version, ex.Message);
|
await logger.LogError(ex, "Error Downloading Module {ModuleDefinitionName} {Version} {Error}", moduledefinitionname, version, ex.Message);
|
||||||
AddModuleMessage("Error Downloading Module", MessageType.Error);
|
AddModuleMessage("Error Downloading Module", MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DeleteModule(ModuleDefinition moduleDefinition)
|
private async Task DeleteModule(ModuleDefinition moduleDefinition)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
@namespace Oqtane.Modules.Admin.Themes
|
@namespace Oqtane.Modules.Admin.Themes
|
||||||
|
@using System.Net
|
||||||
@inherits ModuleBase
|
@inherits ModuleBase
|
||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
@inject IThemeService ThemeService
|
@inject IThemeService ThemeService
|
||||||
@inject IPackageService PackageService
|
@inject IPackageService PackageService
|
||||||
|
|
||||||
@if (themes == null)
|
@if (_themes == null)
|
||||||
{
|
{
|
||||||
<p><em>Loading...</em></p>
|
<p><em>Loading...</em></p>
|
||||||
}
|
}
|
||||||
|
@ -12,68 +13,99 @@ else
|
||||||
{
|
{
|
||||||
<ActionLink Action="Add" Text="Install Theme" />
|
<ActionLink Action="Add" Text="Install Theme" />
|
||||||
|
|
||||||
<Pager Items="@themes">
|
<Pager Items="@_themes">
|
||||||
<Header>
|
<Header>
|
||||||
|
<th> </th>
|
||||||
<th> </th>
|
<th> </th>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>Version</th>
|
<th>Version</th>
|
||||||
<th> </th>
|
<th> </th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
|
<td><ActionLink Action="View" Parameters="@($"name=" + WebUtility.UrlEncode(context.ThemeName))" /></td>
|
||||||
<td>
|
<td>
|
||||||
@if (context.AssemblyName != "Oqtane.Client")
|
@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))" />
|
<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>
|
||||||
<td>@context.Name</td>
|
<td>@context.Name</td>
|
||||||
<td>@context.Version</td>
|
<td>@context.Version</td>
|
||||||
<td>
|
<td>
|
||||||
@if (UpgradeAvailable(context.ThemeName, context.Version))
|
@if (UpgradeAvailable(context.ThemeName, context.Version))
|
||||||
{
|
{
|
||||||
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadTheme(context.ThemeName, context.Version))>Upgrade</button>
|
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadTheme(context.ThemeName, context.Version))>Upgrade</button>
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
</Row>
|
</Row>
|
||||||
</Pager>
|
</Pager>
|
||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private List<Theme> themes;
|
private List<Theme> _themes;
|
||||||
private List<Package> packages;
|
private List<Package> _packages;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
themes = await ThemeService.GetThemesAsync();
|
try
|
||||||
packages = await PackageService.GetPackagesAsync("module");
|
{
|
||||||
|
_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)
|
private bool UpgradeAvailable(string themename, string version)
|
||||||
{
|
{
|
||||||
var upgradeavailable = false;
|
var upgradeavailable = false;
|
||||||
var package = packages.Where(item => item.PackageId == Utilities.GetTypeName(themename)).FirstOrDefault();
|
if (_packages != null)
|
||||||
if (package != 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;
|
return upgradeavailable;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DownloadTheme(string themename, string version)
|
private async Task DownloadTheme(string themename, string version)
|
||||||
{
|
{
|
||||||
await PackageService.DownloadPackageAsync(themename, version, "Themes");
|
try
|
||||||
await logger.LogInformation("Theme Downloaded {ThemeName} {Version}", themename, version);
|
{
|
||||||
await ThemeService.InstallThemesAsync();
|
await PackageService.DownloadPackageAsync(themename, version, "Themes");
|
||||||
NavigationManager.NavigateTo(NavigateUrl());
|
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)
|
private async Task DeleteTheme(Theme Theme)
|
||||||
{
|
{
|
||||||
await ThemeService.DeleteThemeAsync(Theme.ThemeName);
|
try
|
||||||
await logger.LogInformation("Theme Deleted {Theme}", Theme);
|
{
|
||||||
NavigationManager.NavigateTo(NavigateUrl());
|
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()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
List<Package> packages = await PackageService.GetPackagesAsync("framework");
|
try
|
||||||
_package = packages.FirstOrDefault();
|
|
||||||
if (_package != null)
|
|
||||||
{
|
{
|
||||||
_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()
|
private async Task Upgrade()
|
||||||
{
|
{
|
||||||
await InstallationService.Upgrade();
|
try
|
||||||
NavigationManager.NavigateTo(NavigateUrl());
|
{
|
||||||
|
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)
|
private async Task Download(string packageid, string version)
|
||||||
{
|
{
|
||||||
await PackageService.DownloadPackageAsync(packageid, version, "Framework");
|
try
|
||||||
await InstallationService.Upgrade();
|
{
|
||||||
NavigationManager.NavigateTo(NavigateUrl());
|
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
|
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">
|
<Pager Items="@userroles">
|
||||||
<Header>
|
<Header>
|
||||||
|
@ -28,16 +32,32 @@ else
|
||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
private List<UserRole> allroles;
|
||||||
private List<UserRole> userroles;
|
private List<UserRole> userroles;
|
||||||
|
private string search;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
userroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId);
|
allroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId);
|
||||||
userroles = userroles.Where(item => item.Role.Name == Constants.RegisteredRole).ToList();
|
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)
|
private async Task DeleteUser(UserRole UserRole)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
if (p.PageId == PageState.Page.PageId)
|
if (p.PageId == PageState.Page.PageId)
|
||||||
{
|
{
|
||||||
<li class="nav-item active">
|
<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)
|
@if (p.Icon != string.Empty)
|
||||||
{
|
{
|
||||||
<span class="oi oi-@p.Icon" aria-hidden="true"></span>
|
<span class="oi oi-@p.Icon" aria-hidden="true"></span>
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<li class="nav-item">
|
<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)
|
@if (p.Icon != string.Empty)
|
||||||
{
|
{
|
||||||
<span class="oi oi-@p.Icon" aria-hidden="true"></span>
|
<span class="oi oi-@p.Icon" aria-hidden="true"></span>
|
||||||
|
|
|
@ -13,6 +13,13 @@
|
||||||
{
|
{
|
||||||
var interop = new Interop(JsRuntime);
|
var interop = new Interop(JsRuntime);
|
||||||
|
|
||||||
|
// handle page redirection
|
||||||
|
if (!string.IsNullOrEmpty(PageState.Page.Url))
|
||||||
|
{
|
||||||
|
NavigationManager.NavigateTo(PageState.Page.Url);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// set page title
|
// set page title
|
||||||
if (!string.IsNullOrEmpty(PageState.Page.Title))
|
if (!string.IsNullOrEmpty(PageState.Page.Title))
|
||||||
{
|
{
|
||||||
|
@ -24,6 +31,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// include page resources
|
// include page resources
|
||||||
|
string batch = DateTime.Now.ToString("yyyyMMddHHmmssfff");
|
||||||
var links = new List<object>();
|
var links = new List<object>();
|
||||||
var scripts = new List<object>();
|
var scripts = new List<object>();
|
||||||
foreach (Resource resource in PageState.Page.Resources)
|
foreach (Resource resource in PageState.Page.Resources)
|
||||||
|
@ -31,18 +39,18 @@
|
||||||
switch (resource.ResourceType)
|
switch (resource.ResourceType)
|
||||||
{
|
{
|
||||||
case ResourceType.Stylesheet:
|
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;
|
break;
|
||||||
case ResourceType.Script:
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await interop.IncludeLinks(links.ToArray());
|
await interop.IncludeLinks(links.ToArray());
|
||||||
await interop.IncludeScripts(scripts.ToArray());
|
await interop.IncludeScripts(scripts.ToArray());
|
||||||
// remove any page resource references which are no longer required for this page
|
// 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-stylesheet", "", "app-stylesheet-" + batch + "-00");
|
||||||
await interop.RemoveElementsById("app-script", "app-script" + scripts.Count.ToString("00"), "");
|
await interop.RemoveElementsById("app-script", "", "app-script-" + batch + "-00");
|
||||||
|
|
||||||
// add favicon
|
// add favicon
|
||||||
if (PageState.Site.FaviconFileId != null)
|
if (PageState.Site.FaviconFileId != null)
|
||||||
|
|
|
@ -47,7 +47,7 @@ namespace Oqtane.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET api/<controller>/name/xxx?sync=yyyyMMddHHmmssfff
|
// GET api/<controller>/name/xxx?sync=yyyyMMddHHmmssfff
|
||||||
[HttpGet("name/{name}")]
|
[HttpGet("name/{**name}")]
|
||||||
public Alias Get(string name, string sync)
|
public Alias Get(string name, string sync)
|
||||||
{
|
{
|
||||||
List<Alias> aliases = _aliases.GetAliases().ToList(); // cached
|
List<Alias> aliases = _aliases.GetAliases().ToList(); // cached
|
||||||
|
|
|
@ -23,7 +23,13 @@
|
||||||
<EmbeddedResource Remove="wwwroot\Modules\Templates\**" />
|
<EmbeddedResource Remove="wwwroot\Modules\Templates\**" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<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\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.0.sql" />
|
||||||
<EmbeddedResource Include="Scripts\Tenant.0.9.1.sql" />
|
<EmbeddedResource Include="Scripts\Tenant.0.9.1.sql" />
|
||||||
<EmbeddedResource Include="Scripts\Tenant.0.9.2.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)
|
private List<ModuleDefinition> LoadModuleDefinitionsFromAssembly(List<ModuleDefinition> moduledefinitions, Assembly assembly)
|
||||||
{
|
{
|
||||||
ModuleDefinition moduledefinition;
|
ModuleDefinition moduledefinition;
|
||||||
|
|
||||||
Type[] modulecontroltypes = assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(IModuleControl))).ToArray();
|
Type[] modulecontroltypes = assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(IModuleControl))).ToArray();
|
||||||
foreach (Type modulecontroltype in modulecontroltypes)
|
foreach (Type modulecontroltype in modulecontroltypes)
|
||||||
{
|
{
|
||||||
|
@ -195,11 +196,11 @@ namespace Oqtane.Repository
|
||||||
if (modulecontroltype.Name == "ModuleBase"
|
if (modulecontroltype.Name == "ModuleBase"
|
||||||
|| modulecontroltype.IsGenericType
|
|| modulecontroltype.IsGenericType
|
||||||
|| modulecontroltype.IsAbstract
|
|| modulecontroltype.IsAbstract
|
||||||
|| Attribute.IsDefined(modulecontroltype, typeof(OqtaneIgnoreAttribute))
|
|| modulecontroltype.IsOqtaneIgnore()
|
||||||
) continue;
|
) continue;
|
||||||
|
|
||||||
string moduleNamespace = modulecontroltype.Namespace;
|
// create namespace root typename
|
||||||
string qualifiedModuleType = moduleNamespace + ", " + modulecontroltype.Assembly.GetName().Name;
|
string qualifiedModuleType = modulecontroltype.Namespace + ", " + modulecontroltype.Assembly.GetName().Name;
|
||||||
|
|
||||||
int index = moduledefinitions.FindIndex(item => item.ModuleDefinitionName == qualifiedModuleType);
|
int index = moduledefinitions.FindIndex(item => item.ModuleDefinitionName == qualifiedModuleType);
|
||||||
if (index == -1)
|
if (index == -1)
|
||||||
|
@ -208,7 +209,7 @@ namespace Oqtane.Repository
|
||||||
Type moduletype = assembly
|
Type moduletype = assembly
|
||||||
.GetTypes()
|
.GetTypes()
|
||||||
.Where(item => item.Namespace != null)
|
.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)));
|
.FirstOrDefault(item => item.GetInterfaces().Contains(typeof(IModule)));
|
||||||
if (moduletype != null)
|
if (moduletype != null)
|
||||||
{
|
{
|
||||||
|
@ -221,8 +222,8 @@ namespace Oqtane.Repository
|
||||||
// set default property values
|
// set default property values
|
||||||
moduledefinition = new ModuleDefinition
|
moduledefinition = new ModuleDefinition
|
||||||
{
|
{
|
||||||
Name = moduleNamespace.Substring(moduleNamespace.LastIndexOf(".") + 1),
|
Name = Utilities.GetTypeNameLastSegment(modulecontroltype.Namespace, 0),
|
||||||
Description = "Manage " + moduleNamespace.Substring(moduleNamespace.LastIndexOf(".") + 1),
|
Description = "Manage " + Utilities.GetTypeNameLastSegment(modulecontroltype.Namespace, 0),
|
||||||
Categories = ((qualifiedModuleType.StartsWith("Oqtane.Modules.Admin.")) ? "Admin" : "")
|
Categories = ((qualifiedModuleType.StartsWith("Oqtane.Modules.Admin.")) ? "Admin" : "")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -230,7 +231,7 @@ namespace Oqtane.Repository
|
||||||
// set internal properties
|
// set internal properties
|
||||||
moduledefinition.ModuleDefinitionName = qualifiedModuleType;
|
moduledefinition.ModuleDefinitionName = qualifiedModuleType;
|
||||||
moduledefinition.Version = ""; // will be populated from database
|
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;
|
moduledefinition.AssemblyName = assembly.GetName().Name;
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(moduledefinition.Categories))
|
if (string.IsNullOrEmpty(moduledefinition.Categories))
|
||||||
|
|
|
@ -44,26 +44,34 @@ namespace Oqtane.Repository
|
||||||
private List<Theme> LoadThemesFromAssembly(List<Theme> themes, Assembly assembly)
|
private List<Theme> LoadThemesFromAssembly(List<Theme> themes, Assembly assembly)
|
||||||
{
|
{
|
||||||
Theme theme;
|
Theme theme;
|
||||||
|
List<Type> themeTypes = new List<Type>();
|
||||||
|
|
||||||
Type[] themeControlTypes = assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(IThemeControl))).ToArray();
|
Type[] themeControlTypes = assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(IThemeControl))).ToArray();
|
||||||
foreach (Type themeControlType in themeControlTypes)
|
foreach (Type themeControlType in themeControlTypes)
|
||||||
{
|
{
|
||||||
// Check if type should be ignored
|
// Check if type should be ignored
|
||||||
if (themeControlType.Name == "ThemeBase"
|
if (themeControlType.Name == "ThemeBase"
|
||||||
|| themeControlType.IsGenericType
|
|| themeControlType.IsGenericType
|
||||||
|| Attribute.IsDefined(themeControlType, typeof(OqtaneIgnoreAttribute))
|
|| themeControlType.IsAbstract
|
||||||
|
|| themeControlType.IsOqtaneIgnore()
|
||||||
) continue;
|
) continue;
|
||||||
|
|
||||||
string themeNamespace = themeControlType.Namespace;
|
// create namespace root typename
|
||||||
string qualifiedModuleType = themeNamespace + ", " + themeControlType.Assembly.GetName().Name;
|
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)
|
if (index == -1)
|
||||||
{
|
{
|
||||||
// determine if this theme implements ITheme
|
// Find all types in the assembly with the same namespace root
|
||||||
Type themetype = assembly.GetTypes()
|
themeTypes = assembly.GetTypes()
|
||||||
|
.Where(item => !item.IsOqtaneIgnore())
|
||||||
.Where(item => item.Namespace != null)
|
.Where(item => item.Namespace != null)
|
||||||
.Where(item => item.Namespace.StartsWith(themeNamespace))
|
.Where(item => item.Namespace == themeControlType.Namespace || item.Namespace.StartsWith(themeControlType.Namespace + "."))
|
||||||
.Where(item => item.GetInterfaces().Contains(typeof(ITheme))).FirstOrDefault();
|
.ToList();
|
||||||
|
|
||||||
|
// determine if this theme implements ITheme
|
||||||
|
Type themetype = themeTypes
|
||||||
|
.FirstOrDefault(item => item.GetInterfaces().Contains(typeof(ITheme)));
|
||||||
if (themetype != null)
|
if (themetype != null)
|
||||||
{
|
{
|
||||||
var themeobject = Activator.CreateInstance(themetype) as ITheme;
|
var themeobject = Activator.CreateInstance(themetype) as ITheme;
|
||||||
|
@ -78,21 +86,19 @@ namespace Oqtane.Repository
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// set internal properties
|
// set internal properties
|
||||||
theme.ThemeName = themeNamespace;
|
theme.ThemeName = qualifiedThemeType;
|
||||||
theme.ThemeControls = "";
|
theme.ThemeControls = "";
|
||||||
theme.PaneLayouts = "";
|
theme.PaneLayouts = "";
|
||||||
theme.ContainerControls = "";
|
theme.ContainerControls = "";
|
||||||
theme.AssemblyName = assembly.FullName.Split(",")[0];
|
theme.AssemblyName = assembly.FullName.Split(",")[0];
|
||||||
themes.Add(theme);
|
themes.Add(theme);
|
||||||
index = themes.FindIndex(item => item.ThemeName == themeNamespace);
|
index = themes.FindIndex(item => item.ThemeName == qualifiedThemeType);
|
||||||
}
|
}
|
||||||
theme = themes[index];
|
theme = themes[index];
|
||||||
theme.ThemeControls += (themeControlType.FullName + ", " + themeControlType.Assembly.GetName().Name + ";");
|
theme.ThemeControls += (themeControlType.FullName + ", " + themeControlType.Assembly.GetName().Name + ";");
|
||||||
|
|
||||||
// layouts
|
// layouts
|
||||||
Type[] layouttypes = assembly.GetTypes()
|
Type[] layouttypes = themeTypes
|
||||||
.Where(item => item.Namespace != null)
|
|
||||||
.Where(item => item.Namespace.StartsWith(themeNamespace))
|
|
||||||
.Where(item => item.GetInterfaces().Contains(typeof(ILayoutControl))).ToArray();
|
.Where(item => item.GetInterfaces().Contains(typeof(ILayoutControl))).ToArray();
|
||||||
foreach (Type layouttype in layouttypes)
|
foreach (Type layouttype in layouttypes)
|
||||||
{
|
{
|
||||||
|
@ -104,9 +110,7 @@ namespace Oqtane.Repository
|
||||||
}
|
}
|
||||||
|
|
||||||
// containers
|
// containers
|
||||||
Type[] containertypes = assembly.GetTypes()
|
Type[] containertypes = themeTypes
|
||||||
.Where(item => item.Namespace != null)
|
|
||||||
.Where(item => item.Namespace.StartsWith(themeNamespace))
|
|
||||||
.Where(item => item.GetInterfaces().Contains(typeof(IContainerControl))).ToArray();
|
.Where(item => item.GetInterfaces().Contains(typeof(IContainerControl))).ToArray();
|
||||||
foreach (Type containertype in containertypes)
|
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);
|
document.head.appendChild(meta);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
if (id !== "") {
|
||||||
|
meta.setAttribute("id", id);
|
||||||
|
}
|
||||||
if (meta.content !== content) {
|
if (meta.content !== content) {
|
||||||
meta.setAttribute("content", content);
|
meta.setAttribute("content", content);
|
||||||
}
|
}
|
||||||
|
@ -77,6 +80,9 @@ Oqtane.Interop = {
|
||||||
document.head.appendChild(link);
|
document.head.appendChild(link);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
if (link.id !== id) {
|
||||||
|
link.setAttribute('id', id);
|
||||||
|
}
|
||||||
if (link.rel !== rel) {
|
if (link.rel !== rel) {
|
||||||
link.setAttribute('rel', rel);
|
link.setAttribute('rel', rel);
|
||||||
}
|
}
|
||||||
|
@ -148,6 +154,9 @@ Oqtane.Interop = {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
if (script.id !== id) {
|
||||||
|
script.setAttribute('id', id);
|
||||||
|
}
|
||||||
if (src !== "") {
|
if (src !== "") {
|
||||||
if (script.src !== this.getAbsoluteUrl(src)) {
|
if (script.src !== this.getAbsoluteUrl(src)) {
|
||||||
script.removeAttribute('integrity');
|
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 => a.GetTypes<IModuleControl>().Any() || a.GetTypes<IThemeControl>().Any() || a.GetTypes<IClientStartup>().Any())
|
||||||
.Where(a => Utilities.GetFullTypeName(a.GetName().Name) != "Oqtane.Client");
|
.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 Oqtane.Models;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -15,7 +16,7 @@ namespace Oqtane.Shared
|
||||||
{
|
{
|
||||||
if (type == null) return null;
|
if (type == null) return null;
|
||||||
var assemblyFullName = type.Assembly.FullName;
|
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}";
|
return $"{type.Namespace}, {assemblyName}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,10 +25,10 @@ namespace Oqtane.Shared
|
||||||
var uriBuilder = new UriBuilder
|
var uriBuilder = new UriBuilder
|
||||||
{
|
{
|
||||||
Path = !string.IsNullOrEmpty(alias)
|
Path = !string.IsNullOrEmpty(alias)
|
||||||
? (!string.IsNullOrEmpty(path))
|
? (!string.IsNullOrEmpty(path))
|
||||||
? $"{alias}/{path}"
|
? $"{alias}/{path}"
|
||||||
: $"{alias}"
|
: $"{alias}"
|
||||||
: $"{path}",
|
: $"{path}",
|
||||||
Query = parameters
|
Query = parameters
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -44,6 +45,7 @@ namespace Oqtane.Shared
|
||||||
path += $"/{action}";
|
path += $"/{action}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return NavigateUrl(alias, path, parameters);
|
return NavigateUrl(alias, path, parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,6 +100,7 @@ namespace Oqtane.Shared
|
||||||
// remove assembly if fully qualified type
|
// remove assembly if fully qualified type
|
||||||
typename = typename.Substring(0, typename.IndexOf(","));
|
typename = typename.Substring(0, typename.IndexOf(","));
|
||||||
}
|
}
|
||||||
|
|
||||||
// segment 0 is the last segment, segment 1 is the second to last segment, etc...
|
// segment 0 is the last segment, segment 1 is the second to last segment, etc...
|
||||||
string[] segments = typename.Split('.');
|
string[] segments = typename.Split('.');
|
||||||
string name = "";
|
string name = "";
|
||||||
|
@ -105,6 +108,7 @@ namespace Oqtane.Shared
|
||||||
{
|
{
|
||||||
name = segments[segments.Length - (segment + 1)];
|
name = segments[segments.Length - (segment + 1)];
|
||||||
}
|
}
|
||||||
|
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,11 +146,14 @@ namespace Oqtane.Shared
|
||||||
stringBuilder.Append('-');
|
stringBuilder.Append('-');
|
||||||
prevdash = true;
|
prevdash = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result = stringBuilder.ToString().Trim('-');
|
result = stringBuilder.ToString().Trim('-');
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,20 +243,23 @@ namespace Oqtane.Shared
|
||||||
if (string.IsNullOrEmpty(email)) return false;
|
if (string.IsNullOrEmpty(email)) return false;
|
||||||
|
|
||||||
return Regex.IsMatch(email,
|
return Regex.IsMatch(email,
|
||||||
@"^(?("")("".+?(?<!\\)""@)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])@))" +
|
@"^(?("")("".+?(?<!\\)""@)|(([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]))$",
|
@"(?(\[)(\[(\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));
|
RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(250));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string PathCombine(params string[] segments)
|
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++){
|
for (int i = 1; i < segments.Length; i++)
|
||||||
if(Path.IsPathRooted(segments[i])){
|
{
|
||||||
|
if (Path.IsPathRooted(segments[i]))
|
||||||
|
{
|
||||||
segments[i] = segments[i].TrimStart(separators);
|
segments[i] = segments[i].TrimStart(separators);
|
||||||
if(String.IsNullOrEmpty(segments[i])){
|
if (String.IsNullOrEmpty(segments[i]))
|
||||||
segments[i]=" ";
|
{
|
||||||
|
segments[i] = " ";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -273,5 +283,54 @@ namespace Oqtane.Shared
|
||||||
!Constants.InvalidFileNameEndingChars.Any(name.EndsWith) &&
|
!Constants.InvalidFileNameEndingChars.Any(name.EndsWith) &&
|
||||||
!Constants.ReservedDevices.Split(',').Contains(name.ToUpper().Split('.')[0]));
|
!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
|
# 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.
|
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
|
# Example Screenshots
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user