Merge pull request #16 from oqtane/master

Sync master
This commit is contained in:
Jim Spillane 2020-05-30 18:41:08 -04:00 committed by GitHub
commit 47a917a3df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 431 additions and 90 deletions

View File

@ -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)
{

View File

@ -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>&nbsp;</th>
<th>&nbsp;</th>
<th>Name</th>
<th>Version</th>
<th>&nbsp;</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);
}
}
}

View 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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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>

View File

@ -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)

View File

@ -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

View File

@ -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" />

View File

@ -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))

View File

@ -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)
{

View 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

View 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

View File

@ -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');

View File

@ -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));
}
}
}

View File

@ -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;
}
}
}

View File

@ -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