" + SiteState.Properties.PageTitle + "";
+ if (title != _title)
+ {
+ _title = title;
+ StateHasChanged();
+ }
+ break;
+ case "HeadContent":
+ var content = RemoveScripts(SiteState.Properties.HeadContent) + "\n";
+ if (content != _content)
+ {
+ _content = content;
+ StateHasChanged();
+ }
+ break;
+ }
+ }
+
+ private string RemoveScripts(string headcontent)
+ {
+ if (!string.IsNullOrEmpty(headcontent))
+ {
+ var index = headcontent.IndexOf("") + 9 - index);
+ index = headcontent.IndexOf("", index) + 9 - index);
+ // get script attributes
+ var attributes = script.Substring(0, script.IndexOf(">")).Replace("\"", "").Split(" ");
+ string id = "";
+ string src = "";
+ string integrity = "";
+ string crossorigin = "";
+ string type = "";
+ foreach (var attribute in attributes)
+ {
+ if (attribute.Contains("="))
+ {
+ var value = attribute.Split("=");
+ switch (value[0])
+ {
+ case "id":
+ id = value[1];
+ break;
+ case "src":
+ src = value[1];
+ break;
+ case "integrity":
+ integrity = value[1];
+ break;
+ case "crossorigin":
+ crossorigin = value[1];
+ break;
+ case "type":
+ type = value[1];
+ break;
+ }
+ }
+ }
+ // inject script
+ if (!string.IsNullOrEmpty(src))
+ {
+ src = (src.Contains("://")) ? src : PageState.Alias.BaseUrl + src;
+ scripts.Add(new { href = src, bundle = "", integrity = integrity, crossorigin = crossorigin, es6module = (type == "module"), location = location });
+ }
+ else
+ {
+ // inline script must have an id attribute
+ if (id == "")
+ {
+ count += 1;
+ id = $"page{PageState.Page.PageId}-script{count}";
+ }
+ index = script.IndexOf(">") + 1;
+ await interop.IncludeScript(id, "", "", "", "", script.Substring(index, script.IndexOf("") - index), location.ToString().ToLower());
+ }
+ index = content.IndexOf("
-
-
+
+
diff --git a/Oqtane.Package/Oqtane.Client.nuspec b/Oqtane.Package/Oqtane.Client.nuspec
index 99de9771..b2b4def4 100644
--- a/Oqtane.Package/Oqtane.Client.nuspec
+++ b/Oqtane.Package/Oqtane.Client.nuspec
@@ -2,7 +2,7 @@
Oqtane.Client
- 3.4.3
+ 4.0.0Shaun Walker.NET FoundationOqtane Framework
@@ -12,13 +12,13 @@
falseMIThttps://github.com/oqtane/oqtane.framework
- https://github.com/oqtane/oqtane.framework/releases/tag/v3.4.3
+ https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.0icon.pngoqtane
-
-
+
+
\ No newline at end of file
diff --git a/Oqtane.Package/Oqtane.Framework.nuspec b/Oqtane.Package/Oqtane.Framework.nuspec
index b2bbe94b..662219e6 100644
--- a/Oqtane.Package/Oqtane.Framework.nuspec
+++ b/Oqtane.Package/Oqtane.Framework.nuspec
@@ -2,7 +2,7 @@
Oqtane.Framework
- 3.4.3
+ 4.0.0Shaun Walker.NET FoundationOqtane Framework
@@ -11,8 +11,8 @@
.NET FoundationfalseMIT
- https://github.com/oqtane/oqtane.framework/releases/download/v3.4.3/Oqtane.Framework.3.4.3.Upgrade.zip
- https://github.com/oqtane/oqtane.framework/releases/tag/v3.4.3
+ https://github.com/oqtane/oqtane.framework/releases/download/v4.0.0/Oqtane.Framework.4.0.0.Upgrade.zip
+ https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.0icon.pngoqtane framework
diff --git a/Oqtane.Package/Oqtane.Server.nuspec b/Oqtane.Package/Oqtane.Server.nuspec
index e4e993ad..70a25469 100644
--- a/Oqtane.Package/Oqtane.Server.nuspec
+++ b/Oqtane.Package/Oqtane.Server.nuspec
@@ -2,7 +2,7 @@
Oqtane.Server
- 3.4.3
+ 4.0.0Shaun Walker.NET FoundationOqtane Framework
@@ -12,13 +12,13 @@
falseMIThttps://github.com/oqtane/oqtane.framework
- https://github.com/oqtane/oqtane.framework/releases/tag/v3.4.3
+ https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.0icon.pngoqtane
-
-
+
+
\ No newline at end of file
diff --git a/Oqtane.Package/Oqtane.Shared.nuspec b/Oqtane.Package/Oqtane.Shared.nuspec
index 965fdcaa..6b073a4a 100644
--- a/Oqtane.Package/Oqtane.Shared.nuspec
+++ b/Oqtane.Package/Oqtane.Shared.nuspec
@@ -2,7 +2,7 @@
Oqtane.Shared
- 3.4.3
+ 4.0.0Shaun Walker.NET FoundationOqtane Framework
@@ -12,13 +12,13 @@
falseMIThttps://github.com/oqtane/oqtane.framework
- https://github.com/oqtane/oqtane.framework/releases/tag/v3.4.3
+ https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.0icon.pngoqtane
-
-
+
+
\ No newline at end of file
diff --git a/Oqtane.Package/Oqtane.Updater.nuspec b/Oqtane.Package/Oqtane.Updater.nuspec
index 12d8391b..9e667cc9 100644
--- a/Oqtane.Package/Oqtane.Updater.nuspec
+++ b/Oqtane.Package/Oqtane.Updater.nuspec
@@ -2,7 +2,7 @@
Oqtane.Updater
- 3.4.3
+ 4.0.0Shaun Walker.NET FoundationOqtane Framework
@@ -12,12 +12,12 @@
falseMIThttps://github.com/oqtane/oqtane.framework
- https://github.com/oqtane/oqtane.framework/releases/tag/v3.4.3
+ https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.0icon.pngoqtane
-
+
\ No newline at end of file
diff --git a/Oqtane.Package/install.ps1 b/Oqtane.Package/install.ps1
index ed7f214e..9d1c4f9e 100644
--- a/Oqtane.Package/install.ps1
+++ b/Oqtane.Package/install.ps1
@@ -1 +1 @@
-Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.4.3.Install.zip" -Force
\ No newline at end of file
+Compress-Archive -Path "..\Oqtane.Server\bin\Release\net7.0\publish\*" -DestinationPath "Oqtane.Framework.4.0.0.Install.zip" -Force
\ No newline at end of file
diff --git a/Oqtane.Package/release.cmd b/Oqtane.Package/release.cmd
index 05404321..c3e281c9 100644
--- a/Oqtane.Package/release.cmd
+++ b/Oqtane.Package/release.cmd
@@ -8,14 +8,14 @@ nuget.exe pack Oqtane.Client.nuspec
nuget.exe pack Oqtane.Server.nuspec
nuget.exe pack Oqtane.Shared.nuspec
nuget.exe pack Oqtane.Framework.nuspec
-del /F/Q/S "..\Oqtane.Server\bin\Release\net6.0\publish" > NUL
-rmdir /Q/S "..\Oqtane.Server\bin\Release\net6.0\publish"
+del /F/Q/S "..\Oqtane.Server\bin\Release\net7.0\publish" > NUL
+rmdir /Q/S "..\Oqtane.Server\bin\Release\net7.0\publish"
dotnet publish ..\Oqtane.Server\Oqtane.Server.csproj /p:Configuration=Release
-del /F/Q/S "..\Oqtane.Server\bin\Release\net6.0\publish\wwwroot\Content" > NUL
-rmdir /Q/S "..\Oqtane.Server\bin\Release\net6.0\publish\wwwroot\Content"
+del /F/Q/S "..\Oqtane.Server\bin\Release\net7.0\publish\wwwroot\Content" > NUL
+rmdir /Q/S "..\Oqtane.Server\bin\Release\net7.0\publish\wwwroot\Content"
setlocal ENABLEDELAYEDEXPANSION
set retain=Oqtane.Modules.Admin.Login,Oqtane.Modules.HtmlText,Templates
-for /D %%i in ("..\Oqtane.Server\bin\Release\net6.0\publish\wwwroot\Modules\*") do (
+for /D %%i in ("..\Oqtane.Server\bin\Release\net7.0\publish\wwwroot\Modules\*") do (
set /A found=0
for %%j in (%retain%) do (
if "%%~nxi" == "%%j" set /A found=1
@@ -23,18 +23,18 @@ if "%%~nxi" == "%%j" set /A found=1
if not !found! == 1 rmdir /Q/S "%%i"
)
set retain=Oqtane.Themes.BlazorTheme,Oqtane.Themes.OqtaneTheme,Templates
-for /D %%i in ("..\Oqtane.Server\bin\Release\net6.0\publish\wwwroot\Themes\*") do (
+for /D %%i in ("..\Oqtane.Server\bin\Release\net7.0\publish\wwwroot\Themes\*") do (
set /A found=0
for %%j in (%retain%) do (
if "%%~nxi" == "%%j" set /A found=1
)
if not !found! == 1 rmdir /Q/S "%%i"
)
-del "..\Oqtane.Server\bin\Release\net6.0\publish\appsettings.json"
-ren "..\Oqtane.Server\bin\Release\net6.0\publish\appsettings.release.json" "appsettings.json"
+del "..\Oqtane.Server\bin\Release\net7.0\publish\appsettings.json"
+ren "..\Oqtane.Server\bin\Release\net7.0\publish\appsettings.release.json" "appsettings.json"
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe ".\install.ps1"
-del "..\Oqtane.Server\bin\Release\net6.0\publish\appsettings.json"
-del "..\Oqtane.Server\bin\Release\net6.0\publish\web.config"
+del "..\Oqtane.Server\bin\Release\net7.0\publish\appsettings.json"
+del "..\Oqtane.Server\bin\Release\net7.0\publish\web.config"
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe ".\upgrade.ps1"
dotnet clean -c Release ..\Oqtane.Updater.sln
dotnet build -c Release ..\Oqtane.Updater.sln
diff --git a/Oqtane.Package/upgrade.ps1 b/Oqtane.Package/upgrade.ps1
index d05ecaee..17d5c251 100644
--- a/Oqtane.Package/upgrade.ps1
+++ b/Oqtane.Package/upgrade.ps1
@@ -1 +1 @@
-Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.4.3.Upgrade.zip" -Force
\ No newline at end of file
+Compress-Archive -Path "..\Oqtane.Server\bin\Release\net7.0\publish\*" -DestinationPath "Oqtane.Framework.4.0.0.Upgrade.zip" -Force
\ No newline at end of file
diff --git a/Oqtane.Server/Controllers/InstallationController.cs b/Oqtane.Server/Controllers/InstallationController.cs
index 9de2e512..c94163ca 100644
--- a/Oqtane.Server/Controllers/InstallationController.cs
+++ b/Oqtane.Server/Controllers/InstallationController.cs
@@ -33,8 +33,10 @@ namespace Oqtane.Controllers
private readonly IHttpContextAccessor _accessor;
private readonly IAliasRepository _aliases;
private readonly ILogger _filelogger;
+ private readonly ITenantManager _tenantManager;
+ private readonly ServerStateManager _serverState;
- public InstallationController(IConfigManager configManager, IInstallationManager installationManager, IDatabaseManager databaseManager, ILocalizationManager localizationManager, IMemoryCache cache, IHttpContextAccessor accessor, IAliasRepository aliases, ILogger filelogger)
+ public InstallationController(IConfigManager configManager, IInstallationManager installationManager, IDatabaseManager databaseManager, ILocalizationManager localizationManager, IMemoryCache cache, IHttpContextAccessor accessor, IAliasRepository aliases, ILogger filelogger, ITenantManager tenantManager, ServerStateManager serverState)
{
_configManager = configManager;
_installationManager = installationManager;
@@ -44,6 +46,8 @@ namespace Oqtane.Controllers
_accessor = accessor;
_aliases = aliases;
_filelogger = filelogger;
+ _tenantManager = tenantManager;
+ _serverState = serverState;
}
// POST api/
@@ -115,7 +119,9 @@ namespace Oqtane.Controllers
private List GetAssemblyList()
{
- return _cache.GetOrCreate("assemblieslist", entry =>
+ int siteId = _tenantManager.GetAlias().SiteId;
+
+ return _cache.GetOrCreate($"assemblieslist:{siteId}", entry =>
{
var binFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
var assemblyList = new List();
@@ -126,78 +132,43 @@ namespace Oqtane.Controllers
{
hashfilename = false;
}
-
- // get list of assemblies which should be downloaded to client
- var assemblies = AppDomain.CurrentDomain.GetOqtaneClientAssemblies();
- var list = assemblies.Select(a => a.GetName().Name).ToList();
- // populate assemblies
- for (int i = 0; i < list.Count; i++)
+ // get site assemblies which should be downloaded to client
+ var assemblies = _serverState.GetServerState(siteId).Assemblies;
+
+ // populate assembly list
+ foreach (var assembly in assemblies)
{
- assemblyList.Add(new ClientAssembly(Path.Combine(binFolder, list[i] + ".dll"), hashfilename));
+ if (assembly != Constants.ClientId)
+ {
+ var filepath = Path.Combine(binFolder, assembly) + ".dll";
+ if (System.IO.File.Exists(filepath))
+ {
+ assemblyList.Add(new ClientAssembly(Path.Combine(binFolder, assembly + ".dll"), hashfilename));
+ }
+ }
}
// insert satellite assemblies at beginning of list
foreach (var culture in _localizationManager.GetInstalledCultures())
{
- var assembliesFolderPath = Path.Combine(binFolder, culture);
- if (culture == Constants.DefaultCulture)
+ if (culture != Constants.DefaultCulture)
{
- continue;
- }
-
- if (Directory.Exists(assembliesFolderPath))
- {
- foreach (var resourceFile in Directory.EnumerateFiles(assembliesFolderPath))
+ var assembliesFolderPath = Path.Combine(binFolder, culture);
+ if (Directory.Exists(assembliesFolderPath))
{
- assemblyList.Insert(0, new ClientAssembly(resourceFile, hashfilename));
- }
- }
- else
- {
- _filelogger.LogError(Utilities.LogMessage(this, $"The Satellite Assembly Folder For {culture} Does Not Exist"));
- }
- }
-
- // insert module and theme dependencies at beginning of list
- foreach (var assembly in assemblies)
- {
- foreach (var type in assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(IModule))))
- {
- var instance = Activator.CreateInstance(type) as IModule;
- foreach (string name in instance.ModuleDefinition.Dependencies.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Reverse())
- {
- var filepath = Path.Combine(binFolder, name.ToLower().EndsWith(".dll") ? name : name + ".dll");
- if (System.IO.File.Exists(filepath))
+ foreach (var assembly in assemblies)
{
- if (!assemblyList.Exists(item => item.FilePath == filepath))
+ var filepath = Path.Combine(assembliesFolderPath, assembly) + ".resources.dll";
+ if (System.IO.File.Exists(filepath))
{
- assemblyList.Insert(0, new ClientAssembly(filepath, hashfilename));
+ assemblyList.Insert(0, new ClientAssembly(Path.Combine(assembliesFolderPath, assembly + ".resources.dll"), hashfilename));
}
}
- else
- {
- _filelogger.LogError(Utilities.LogMessage(this, $"Module {instance.ModuleDefinition.ModuleDefinitionName} Dependency {name}.dll Does Not Exist"));
- }
}
- }
- foreach (var type in assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(ITheme))))
- {
- var instance = Activator.CreateInstance(type) as ITheme;
- foreach (string name in instance.Theme.Dependencies.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Reverse())
+ else
{
- var filepath = Path.Combine(binFolder, name.ToLower().EndsWith(".dll") ? name : name + ".dll");
- if (System.IO.File.Exists(filepath))
- {
- if (!assemblyList.Exists(item => item.FilePath == filepath))
- {
- assemblyList.Insert(0, new ClientAssembly(filepath, hashfilename));
- }
- }
- else
- {
- _filelogger.LogError(Utilities.LogMessage(this, $"Theme {instance.Theme.ThemeName} Dependency {name}.dll Does Not Exist"));
- }
+ _filelogger.LogError(Utilities.LogMessage(this, $"The Satellite Assembly Folder For {culture} Does Not Exist"));
}
}
}
@@ -240,21 +211,24 @@ namespace Oqtane.Controllers
{
foreach (var assembly in assemblies)
{
- if (System.IO.File.Exists(assembly.FilePath))
+ if (Path.GetFileNameWithoutExtension(assembly.FilePath) != Constants.ClientId)
{
- using (var filestream = new FileStream(assembly.FilePath, FileMode.Open, FileAccess.Read))
- using (var entrystream = archive.CreateEntry(assembly.HashedName).Open())
+ if (System.IO.File.Exists(assembly.FilePath))
{
- filestream.CopyTo(entrystream);
+ using (var filestream = new FileStream(assembly.FilePath, FileMode.Open, FileAccess.Read))
+ using (var entrystream = archive.CreateEntry(assembly.HashedName).Open())
+ {
+ filestream.CopyTo(entrystream);
+ }
}
- }
- var pdb = assembly.FilePath.Replace(".dll", ".pdb");
- if (System.IO.File.Exists(pdb))
- {
- using (var filestream = new FileStream(pdb, FileMode.Open, FileAccess.Read))
- using (var entrystream = archive.CreateEntry(assembly.HashedName.Replace(".dll", ".pdb")).Open())
+ var pdb = assembly.FilePath.Replace(".dll", ".pdb");
+ if (System.IO.File.Exists(pdb))
{
- filestream.CopyTo(entrystream);
+ using (var filestream = new FileStream(pdb, FileMode.Open, FileAccess.Read))
+ using (var entrystream = archive.CreateEntry(assembly.HashedName.Replace(".dll", ".pdb")).Open())
+ {
+ filestream.CopyTo(entrystream);
+ }
}
}
}
diff --git a/Oqtane.Server/Controllers/ModuleDefinitionController.cs b/Oqtane.Server/Controllers/ModuleDefinitionController.cs
index a0e6a6a2..c8114821 100644
--- a/Oqtane.Server/Controllers/ModuleDefinitionController.cs
+++ b/Oqtane.Server/Controllers/ModuleDefinitionController.cs
@@ -123,14 +123,14 @@ namespace Oqtane.Controllers
if (moduleDefinition.Template.ToLower().Contains("internal"))
{
rootPath = Utilities.PathCombine(rootFolder.FullName, Path.DirectorySeparatorChar.ToString());
- moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + ", Oqtane.Client";
- moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + ".Manager." + moduleDefinition.Name + "Manager, Oqtane.Server";
+ moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + ".Module." + moduleDefinition.Name + ", Oqtane.Client";
+ moduleDefinition.ServerManagerType = moduleDefinition.Owner + ".Module." + moduleDefinition.Name + ".Manager." + moduleDefinition.Name + "Manager, Oqtane.Server";
}
else
{
- rootPath = Utilities.PathCombine(rootFolder.Parent.FullName, moduleDefinition.Owner + "." + moduleDefinition.Name, Path.DirectorySeparatorChar.ToString());
- moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + ", " + moduleDefinition.Owner + "." + moduleDefinition.Name + ".Client.Oqtane";
- moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + ".Manager." + moduleDefinition.Name + "Manager, " + moduleDefinition.Owner + "." + moduleDefinition.Name + ".Server.Oqtane";
+ rootPath = Utilities.PathCombine(rootFolder.Parent.FullName, moduleDefinition.Owner + ".Module." + moduleDefinition.Name, Path.DirectorySeparatorChar.ToString());
+ moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + ".Module." + moduleDefinition.Name + ", " + moduleDefinition.Owner + ".Module." + moduleDefinition.Name + ".Client.Oqtane";
+ moduleDefinition.ServerManagerType = moduleDefinition.Owner + ".Module." + moduleDefinition.Name + ".Manager." + moduleDefinition.Name + "Manager, " + moduleDefinition.Owner + ".Module." + moduleDefinition.Name + ".Server.Oqtane";
}
ProcessTemplatesRecursively(new DirectoryInfo(templatePath), rootPath, rootFolder.Name, templatePath, moduleDefinition);
@@ -255,10 +255,10 @@ namespace Oqtane.Controllers
_modules.DeleteModule(moduleToRemove.ModuleId);
}
-
// remove module definition
_moduleDefinitions.DeleteModuleDefinition(id);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.ModuleDefinition, moduledefinition.ModuleDefinitionId, SyncEventActions.Delete);
+ _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, moduledefinition.SiteId, SyncEventActions.Refresh);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Definition {ModuleDefinitionName} Deleted", moduledefinition.Name);
}
else
@@ -331,9 +331,9 @@ namespace Oqtane.Controllers
if (moduleDefinition.Version == "local")
{
text = text.Replace("[FrameworkVersion]", Constants.Version);
- text = text.Replace("[ClientReference]", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net6.0\\Oqtane.Client.dll");
- text = text.Replace("[ServerReference]", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net6.0\\Oqtane.Server.dll");
- text = text.Replace("[SharedReference]", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net6.0\\Oqtane.Shared.dll");
+ text = text.Replace("[ClientReference]", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net7.0\\Oqtane.Client.dll");
+ text = text.Replace("[ServerReference]", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net7.0\\Oqtane.Server.dll");
+ text = text.Replace("[SharedReference]", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net7.0\\Oqtane.Shared.dll");
}
else
{
diff --git a/Oqtane.Server/Controllers/PageController.cs b/Oqtane.Server/Controllers/PageController.cs
index d4cc1931..ce81eec2 100644
--- a/Oqtane.Server/Controllers/PageController.cs
+++ b/Oqtane.Server/Controllers/PageController.cs
@@ -10,6 +10,8 @@ using Oqtane.Enums;
using Oqtane.Extensions;
using Oqtane.Infrastructure;
using Oqtane.Repository;
+using Oqtane.Modules.Admin.Users;
+using System.IO;
namespace Oqtane.Controllers
{
@@ -73,19 +75,11 @@ namespace Oqtane.Controllers
return pages;
}
- // GET api//5?userid=x
+ // GET api//5
[HttpGet("{id}")]
- public Page Get(int id, string userid)
+ public Page Get(int id)
{
- Page page = null;
- if (string.IsNullOrEmpty(userid))
- {
- page = _pages.GetPage(id);
- }
- else
- {
- page = _pages.GetPage(id, int.Parse(userid));
- }
+ var page = _pages.GetPage(id);
if (page != null && page.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, page.PermissionList))
{
page.Settings = _settings.GetSettings(EntityNames.Page, page.PageId)
@@ -95,7 +89,7 @@ namespace Oqtane.Controllers
}
else
{
- _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Page Get Attempt {PageId} {UserId}", id, userid);
+ _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Page Get Attempt {PageId}", id);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
@@ -180,29 +174,30 @@ namespace Oqtane.Controllers
{
Page page = null;
Page parent = _pages.GetPage(id);
- if (parent != null && parent.SiteId == _alias.SiteId && parent.IsPersonalizable && _userPermissions.GetUser(User).UserId == int.Parse(userid))
+ User user = _userPermissions.GetUser(User);
+ if (parent != null && parent.SiteId == _alias.SiteId && parent.IsPersonalizable && user.UserId == int.Parse(userid))
{
page = new Page();
page.SiteId = parent.SiteId;
- page.Name = parent.Name;
- page.Title = parent.Title;
- page.Path = parent.Path;
page.ParentId = parent.PageId;
+ page.Name = user.Username;
+ page.Path = parent.Path + "/" + page.Name;
+ page.Title = parent.Name + " - " + page.Name;
page.Order = 0;
page.IsNavigation = false;
page.Url = "";
page.ThemeType = parent.ThemeType;
page.DefaultContainerType = parent.DefaultContainerType;
page.Icon = parent.Icon;
- page.PermissionList = new List {
+ page.PermissionList = new List()
+ {
new Permission(PermissionNames.View, int.Parse(userid), true),
+ new Permission(PermissionNames.View, RoleNames.Everyone, true),
new Permission(PermissionNames.Edit, int.Parse(userid), true)
};
page.IsPersonalizable = false;
page.UserId = int.Parse(userid);
page = _pages.AddPage(page);
- _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Page, page.PageId, SyncEventActions.Create);
- _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, page.SiteId, SyncEventActions.Refresh);
// copy modules
List pagemodules = _pageModules.GetPageModules(page.SiteId).ToList();
@@ -213,8 +208,10 @@ namespace Oqtane.Controllers
module.PageId = page.PageId;
module.ModuleDefinitionName = pm.Module.ModuleDefinitionName;
module.AllPages = false;
- module.PermissionList = new List {
+ module.PermissionList = new List()
+ {
new Permission(PermissionNames.View, int.Parse(userid), true),
+ new Permission(PermissionNames.View, RoleNames.Everyone, true),
new Permission(PermissionNames.Edit, int.Parse(userid), true)
};
module = _modules.AddModule(module);
@@ -235,6 +232,9 @@ namespace Oqtane.Controllers
_pageModules.AddPageModule(pagemodule);
}
+
+ _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Page, page.PageId, SyncEventActions.Create);
+ _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, page.SiteId, SyncEventActions.Refresh);
}
else
{
diff --git a/Oqtane.Server/Controllers/SettingController.cs b/Oqtane.Server/Controllers/SettingController.cs
index f60a5add..c341155a 100644
--- a/Oqtane.Server/Controllers/SettingController.cs
+++ b/Oqtane.Server/Controllers/SettingController.cs
@@ -200,6 +200,8 @@ namespace Oqtane.Controllers
case EntityNames.Tenant:
case EntityNames.ModuleDefinition:
case EntityNames.Host:
+ case EntityNames.Job:
+ case EntityNames.Theme:
if (permissionName == PermissionNames.Edit)
{
authorized = User.IsInRole(RoleNames.Host);
@@ -262,6 +264,8 @@ namespace Oqtane.Controllers
case EntityNames.Tenant:
case EntityNames.ModuleDefinition:
case EntityNames.Host:
+ case EntityNames.Job:
+ case EntityNames.Theme:
filter = !User.IsInRole(RoleNames.Host);
break;
case EntityNames.Site:
diff --git a/Oqtane.Server/Controllers/SiteController.cs b/Oqtane.Server/Controllers/SiteController.cs
index 895744a1..95de3a92 100644
--- a/Oqtane.Server/Controllers/SiteController.cs
+++ b/Oqtane.Server/Controllers/SiteController.cs
@@ -21,6 +21,7 @@ namespace Oqtane.Controllers
{
private readonly ISiteRepository _sites;
private readonly IPageRepository _pages;
+ private readonly IThemeRepository _themes;
private readonly IModuleRepository _modules;
private readonly IPageModuleRepository _pageModules;
private readonly IModuleDefinitionRepository _moduleDefinitions;
@@ -32,10 +33,11 @@ namespace Oqtane.Controllers
private readonly IMemoryCache _cache;
private readonly Alias _alias;
- public SiteController(ISiteRepository sites, IPageRepository pages, IModuleRepository modules, IPageModuleRepository pageModules, IModuleDefinitionRepository moduleDefinitions, ILanguageRepository languages, IUserPermissions userPermissions, ISettingRepository settings, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger, IMemoryCache cache)
+ public SiteController(ISiteRepository sites, IPageRepository pages, IThemeRepository themes, IModuleRepository modules, IPageModuleRepository pageModules, IModuleDefinitionRepository moduleDefinitions, ILanguageRepository languages, IUserPermissions userPermissions, ISettingRepository settings, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger, IMemoryCache cache)
{
_sites = sites;
_pages = pages;
+ _themes = themes;
_modules = modules;
_pageModules = pageModules;
_moduleDefinitions = moduleDefinitions;
@@ -144,6 +146,9 @@ namespace Oqtane.Controllers
var defaultCulture = CultureInfo.GetCultureInfo(Constants.DefaultCulture);
site.Languages.Add(new Language { Code = defaultCulture.Name, Name = defaultCulture.DisplayName, Version = Constants.Version, IsDefault = !site.Languages.Any(l => l.IsDefault) });
+ // themes
+ site.Themes = _themes.FilterThemes(_themes.GetThemes().ToList());
+
return site;
}
else
diff --git a/Oqtane.Server/Controllers/SiteTemplateController.cs b/Oqtane.Server/Controllers/SiteTemplateController.cs
index 489541d0..ff73c348 100644
--- a/Oqtane.Server/Controllers/SiteTemplateController.cs
+++ b/Oqtane.Server/Controllers/SiteTemplateController.cs
@@ -19,7 +19,6 @@ namespace Oqtane.Controllers
// GET: api/
[HttpGet]
- [Authorize(Roles = RoleNames.Host)]
public IEnumerable Get()
{
return _siteTemplates.GetSiteTemplates();
diff --git a/Oqtane.Server/Controllers/ThemeController.cs b/Oqtane.Server/Controllers/ThemeController.cs
index 7ddad124..6606f8f9 100644
--- a/Oqtane.Server/Controllers/ThemeController.cs
+++ b/Oqtane.Server/Controllers/ThemeController.cs
@@ -12,6 +12,8 @@ using Oqtane.Infrastructure;
using Oqtane.Repository;
using System.Text.Json;
using System.Net;
+using System.Reflection.Metadata;
+using System;
// ReSharper disable StringIndexOfIsCultureSpecific.1
@@ -23,14 +25,20 @@ namespace Oqtane.Controllers
private readonly IThemeRepository _themes;
private readonly IInstallationManager _installationManager;
private readonly IWebHostEnvironment _environment;
+ private readonly ITenantManager _tenantManager;
+ private readonly ISyncManager _syncManager;
private readonly ILogManager _logger;
+ private readonly Alias _alias;
- public ThemeController(IThemeRepository themes, IInstallationManager installationManager, IWebHostEnvironment environment, ILogManager logger)
+ public ThemeController(IThemeRepository themes, IInstallationManager installationManager, IWebHostEnvironment environment, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger)
{
_themes = themes;
_installationManager = installationManager;
_environment = environment;
+ _tenantManager = tenantManager;
+ _syncManager = syncManager;
_logger = logger;
+ _alias = tenantManager.GetAlias();
}
// GET: api/
@@ -41,6 +49,41 @@ namespace Oqtane.Controllers
return _themes.GetThemes();
}
+ // GET api//5?siteid=x
+ [HttpGet("{id}")]
+ public Theme Get(int id, string siteid)
+ {
+ int SiteId;
+ if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId)
+ {
+ return _themes.GetTheme(id, SiteId);
+ }
+ else
+ {
+ _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Theme Get Attempt {ThemeId} {SiteId}", id, siteid);
+ HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
+ return null;
+ }
+ }
+
+ // PUT api//5
+ [HttpPut("{id}")]
+ [Authorize(Roles = RoleNames.Admin)]
+ public void Put(int id, [FromBody] Theme theme)
+ {
+ if (ModelState.IsValid && theme.SiteId == _alias.SiteId && _themes.GetTheme(theme.ThemeId,theme.SiteId) != null)
+ {
+ _themes.UpdateTheme(theme);
+ _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Theme, theme.ThemeId, SyncEventActions.Update);
+ _logger.Log(LogLevel.Information, this, LogFunction.Update, "Theme Updated {Theme}", theme);
+ }
+ else
+ {
+ _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Theme Put Attempt {Theme}", theme);
+ HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
+ }
+ }
+
// DELETE api//xxx
[HttpDelete("{themename}")]
[Authorize(Roles = RoleNames.Host)]
@@ -74,7 +117,9 @@ namespace Oqtane.Controllers
}
// remove theme
- _themes.DeleteTheme(theme.ThemeName);
+ _themes.DeleteTheme(theme.ThemeId);
+ _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Theme, theme.ThemeId, SyncEventActions.Delete);
+ _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, theme.SiteId, SyncEventActions.Refresh);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Theme Removed For {ThemeName}", theme.ThemeName);
}
else
@@ -128,12 +173,12 @@ namespace Oqtane.Controllers
if (theme.Template.ToLower().Contains("internal"))
{
rootPath = Utilities.PathCombine(rootFolder.FullName, Path.DirectorySeparatorChar.ToString());
- theme.ThemeName = theme.Owner + "." + theme.Name + ", Oqtane.Client";
+ theme.ThemeName = theme.Owner + ".Theme." + theme.Name + ", Oqtane.Client";
}
else
{
- rootPath = Utilities.PathCombine(rootFolder.Parent.FullName, theme.Owner + "." + theme.Name, Path.DirectorySeparatorChar.ToString());
- theme.ThemeName = theme.Owner + "." + theme.Name + ", " + theme.Owner + "." + theme.Name + ".Client.Oqtane";
+ rootPath = Utilities.PathCombine(rootFolder.Parent.FullName, theme.Owner + ".Theme." + theme.Name, Path.DirectorySeparatorChar.ToString());
+ theme.ThemeName = theme.Owner + ".Theme." + theme.Name + ", " + theme.Owner + ".Theme." + theme.Name + ".Client.Oqtane";
}
ProcessTemplatesRecursively(new DirectoryInfo(templatePath), rootPath, rootFolder.Name, templatePath, theme);
@@ -180,8 +225,8 @@ namespace Oqtane.Controllers
if (theme.Version == "local")
{
text = text.Replace("[FrameworkVersion]", Constants.Version);
- text = text.Replace("[ClientReference]", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net6.0\\Oqtane.Client.dll");
- text = text.Replace("[SharedReference]", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net6.0\\Oqtane.Shared.dll");
+ text = text.Replace("[ClientReference]", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net7.0\\Oqtane.Client.dll");
+ text = text.Replace("[SharedReference]", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net7.0\\Oqtane.Shared.dll");
}
else
{
diff --git a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs
index 2bb65c38..a7239af2 100644
--- a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs
+++ b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs
@@ -61,6 +61,7 @@ namespace Microsoft.Extensions.DependencyInjection
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
+ services.AddSingleton();
return services;
}
@@ -200,9 +201,12 @@ namespace Microsoft.Extensions.DependencyInjection
// set the cookies to allow HttpClient API calls to be authenticated
var httpContextAccessor = s.GetRequiredService();
- foreach (var cookie in httpContextAccessor.HttpContext.Request.Cookies)
+ if (httpContextAccessor.HttpContext != null)
{
- client.DefaultRequestHeaders.Add("Cookie", cookie.Key + "=" + cookie.Value);
+ foreach (var cookie in httpContextAccessor.HttpContext.Request.Cookies)
+ {
+ client.DefaultRequestHeaders.Add("Cookie", cookie.Key + "=" + cookie.Value);
+ }
}
return client;
diff --git a/Oqtane.Server/Infrastructure/DatabaseManager.cs b/Oqtane.Server/Infrastructure/DatabaseManager.cs
index 69776d0c..66dd2cea 100644
--- a/Oqtane.Server/Infrastructure/DatabaseManager.cs
+++ b/Oqtane.Server/Infrastructure/DatabaseManager.cs
@@ -199,10 +199,6 @@ namespace Oqtane.Infrastructure
if (result.Success)
{
result = CreateSite(install);
- if (result.Success)
- {
- result = MigrateSites();
- }
}
}
}
@@ -685,77 +681,6 @@ namespace Oqtane.Infrastructure
return result;
}
- private Installation MigrateSites()
- {
- var result = new Installation { Success = false, Message = string.Empty };
-
- // get site upgrades
- Dictionary siteupgrades = new Dictionary();
- var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies();
- foreach (Assembly assembly in assemblies)
- {
- foreach (var type in assembly.GetTypes(typeof(ISiteMigration)))
- {
- if (Attribute.IsDefined(type, typeof(SiteMigrationAttribute)))
- {
- var attribute = (SiteMigrationAttribute)Attribute.GetCustomAttribute(type, typeof(SiteMigrationAttribute));
- siteupgrades.Add(attribute.AliasName + " " + attribute.Version, type);
- }
- }
- }
-
- // execute site upgrades
- if (siteupgrades.Count > 0)
- {
- using (var scope = _serviceScopeFactory.CreateScope())
- {
- var aliases = scope.ServiceProvider.GetRequiredService();
- var tenantManager = scope.ServiceProvider.GetRequiredService();
- var sites = scope.ServiceProvider.GetRequiredService();
- var logger = scope.ServiceProvider.GetRequiredService();
-
- foreach (var alias in aliases.GetAliases().ToList().Where(item => item.IsDefault))
- {
- foreach (var upgrade in siteupgrades)
- {
- var aliasname = upgrade.Key.Split(' ').First();
- // in the future this equality condition could use RegEx to allow for more flexible matching
- if (string.Equals(alias.Name, aliasname, StringComparison.OrdinalIgnoreCase))
- {
- tenantManager.SetTenant(alias.TenantId);
- var site = sites.GetSites().FirstOrDefault(item => item.SiteId == alias.SiteId);
- if (site != null)
- {
- var version = upgrade.Key.Split(' ').Last();
- if (string.IsNullOrEmpty(site.Version) || Version.Parse(version) > Version.Parse(site.Version))
- {
- try
- {
- var obj = ActivatorUtilities.CreateInstance(scope.ServiceProvider, upgrade.Value) as ISiteMigration;
- if (obj != null)
- {
- obj.Up(site, alias);
- site.Version = version;
- sites.UpdateSite(site);
- logger.Log(alias.SiteId, Shared.LogLevel.Information, "Site Migration", LogFunction.Other, "Site Migrated Successfully To Version {version} For {Alias}", version, alias.Name);
- }
- }
- catch (Exception ex)
- {
- logger.Log(alias.SiteId, Shared.LogLevel.Error, "Site Migration", LogFunction.Other, ex, "An Error Occurred Executing Site Migration {Type} For {Alias} And Version {Version}", upgrade.Value, alias.Name, version);
- }
- }
- }
- }
- }
- }
- }
- }
-
- result.Success = true;
- return result;
- }
-
private string DenormalizeConnectionString(string connectionString)
{
var dataDirectory = AppDomain.CurrentDomain.GetData(Constants.DataDirectory)?.ToString();
diff --git a/Oqtane.Server/Infrastructure/EventSubscribers/CacheInvalidationEventSubscriber.cs b/Oqtane.Server/Infrastructure/EventSubscribers/CacheInvalidationEventSubscriber.cs
new file mode 100644
index 00000000..41dab17d
--- /dev/null
+++ b/Oqtane.Server/Infrastructure/EventSubscribers/CacheInvalidationEventSubscriber.cs
@@ -0,0 +1,24 @@
+using Microsoft.Extensions.Caching.Memory;
+using Oqtane.Models;
+using Oqtane.Shared;
+
+namespace Oqtane.Infrastructure.EventSubscribers
+{
+ public class CacheInvalidationEventSubscriber : IEventSubscriber
+ {
+ private readonly IMemoryCache _cache;
+
+ public CacheInvalidationEventSubscriber(IMemoryCache cache)
+ {
+ _cache = cache;
+ }
+
+ public void EntityChanged(SyncEvent syncEvent)
+ {
+ if (syncEvent.EntityName == EntityNames.Site && syncEvent.Action == SyncEventActions.Refresh)
+ {
+ _cache.Remove($"site:{syncEvent.TenantId}:{syncEvent.EntityId}");
+ }
+ }
+ }
+}
diff --git a/Oqtane.Server/Infrastructure/HostedServices/CacheInvalidationHostedService.cs b/Oqtane.Server/Infrastructure/HostedServices/CacheInvalidationHostedService.cs
deleted file mode 100644
index 68eade75..00000000
--- a/Oqtane.Server/Infrastructure/HostedServices/CacheInvalidationHostedService.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using System.Threading.Tasks;
-using System.Threading;
-using Microsoft.Extensions.Hosting;
-using Oqtane.Models;
-using Microsoft.Extensions.Caching.Memory;
-using Oqtane.Shared;
-
-namespace Oqtane.Infrastructure
-{
- public class CacheInvalidationHostedService : IHostedService
- {
- private readonly ISyncManager _syncManager;
- private readonly IMemoryCache _cache;
-
- public CacheInvalidationHostedService(ISyncManager syncManager, IMemoryCache cache)
- {
- _syncManager = syncManager;
- _cache = cache;
- }
-
- void EntityChanged(object sender, SyncEvent e)
- {
- if (e.EntityName == "Site" && e.Action == SyncEventActions.Refresh)
- {
- _cache.Remove($"site:{e.TenantId}:{e.EntityId}");
- }
- }
-
- public Task StartAsync(CancellationToken cancellationToken)
- {
- _syncManager.EntityChanged += EntityChanged;
-
- return Task.CompletedTask;
- }
-
- public Task StopAsync(CancellationToken cancellationToken)
- {
- return Task.CompletedTask;
- }
- public void Dispose()
- {
- _syncManager.EntityChanged -= EntityChanged;
- }
- }
-}
diff --git a/Oqtane.Server/Infrastructure/HostedServices/EventDistributorHostedService .cs b/Oqtane.Server/Infrastructure/HostedServices/EventDistributorHostedService .cs
new file mode 100644
index 00000000..5c0ffa1c
--- /dev/null
+++ b/Oqtane.Server/Infrastructure/HostedServices/EventDistributorHostedService .cs
@@ -0,0 +1,80 @@
+using System.Threading.Tasks;
+using System.Threading;
+using Microsoft.Extensions.Hosting;
+using Oqtane.Models;
+using System;
+using System.Reflection;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Caching.Memory;
+using System.Collections.Generic;
+using Microsoft.Extensions.Logging;
+using Oqtane.Shared;
+
+namespace Oqtane.Infrastructure
+{
+ public class EventDistributorHostedService : IHostedService
+ {
+ private readonly IServiceProvider _serviceProvider;
+ private readonly ISyncManager _syncManager;
+ private readonly IMemoryCache _cache;
+ private readonly ILogger _filelogger;
+
+ public EventDistributorHostedService(IServiceProvider serviceProvider, ISyncManager syncManager, IMemoryCache cache, ILogger filelogger)
+ {
+ _serviceProvider = serviceProvider;
+ _syncManager = syncManager;
+ _cache = cache;
+ _filelogger = filelogger;
+ }
+
+ void EntityChanged(object sender, SyncEvent syncEvent)
+ {
+ List eventSubscribers = _cache.GetOrCreate($"eventsubscribers", entry =>
+ {
+ eventSubscribers = new List();
+ var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies();
+ foreach (Assembly assembly in assemblies)
+ {
+ foreach (var type in assembly.GetTypes(typeof(IEventSubscriber)))
+ {
+ eventSubscribers.Add(type);
+ }
+ }
+ entry.Priority = CacheItemPriority.NeverRemove;
+ return eventSubscribers;
+ });
+
+ foreach (var type in eventSubscribers)
+ {
+ try
+ {
+ var obj = ActivatorUtilities.CreateInstance(_serviceProvider, type) as IEventSubscriber;
+ if (obj != null)
+ {
+ obj.EntityChanged(syncEvent);
+ }
+ }
+ catch (Exception ex)
+ {
+ _filelogger.LogError(Utilities.LogMessage(this, $"Error In EventSubscriber {type.AssemblyQualifiedName} - {ex.Message}"));
+ }
+ }
+ }
+
+ public Task StartAsync(CancellationToken cancellationToken)
+ {
+ _syncManager.EntityChanged += EntityChanged;
+
+ return Task.CompletedTask;
+ }
+
+ public Task StopAsync(CancellationToken cancellationToken)
+ {
+ return Task.CompletedTask;
+ }
+ public void Dispose()
+ {
+ _syncManager.EntityChanged -= EntityChanged;
+ }
+ }
+}
diff --git a/Oqtane.Server/Infrastructure/Interfaces/IEventSubscriber.cs b/Oqtane.Server/Infrastructure/Interfaces/IEventSubscriber.cs
new file mode 100644
index 00000000..11d154bf
--- /dev/null
+++ b/Oqtane.Server/Infrastructure/Interfaces/IEventSubscriber.cs
@@ -0,0 +1,9 @@
+using Oqtane.Models;
+
+namespace Oqtane.Infrastructure
+{
+ public interface IEventSubscriber
+ {
+ void EntityChanged(SyncEvent syncEvent);
+ }
+}
diff --git a/Oqtane.Server/Infrastructure/Interfaces/IHostResources.cs b/Oqtane.Server/Infrastructure/Interfaces/IHostResources.cs
index f22c53f0..1abb0845 100644
--- a/Oqtane.Server/Infrastructure/Interfaces/IHostResources.cs
+++ b/Oqtane.Server/Infrastructure/Interfaces/IHostResources.cs
@@ -1,11 +1,12 @@
using Oqtane.Models;
+using System;
using System.Collections.Generic;
namespace Oqtane.Infrastructure
{
- // this interface is for declaring global resources and is useful for scenarios where you want to declare resources in a single location for the entire application
public interface IHostResources
{
+ [Obsolete("IHostResources is deprecated. Use module or theme scoped Resources in conjunction with ResourceLevel.Site instead.", false)]
List Resources { get; } // identifies global resources for an application
}
}
diff --git a/Oqtane.Server/Infrastructure/Jobs/HostedServiceBase.cs b/Oqtane.Server/Infrastructure/Jobs/HostedServiceBase.cs
index f3f70ebe..1c9eb377 100644
--- a/Oqtane.Server/Infrastructure/Jobs/HostedServiceBase.cs
+++ b/Oqtane.Server/Infrastructure/Jobs/HostedServiceBase.cs
@@ -5,6 +5,7 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
using Oqtane.Models;
using Oqtane.Repository;
using Oqtane.Shared;
@@ -38,17 +39,23 @@ namespace Oqtane.Infrastructure
{
await Task.Yield(); // required so that this method does not block startup
- try
- {
- while (!stoppingToken.IsCancellationRequested)
+ while (!stoppingToken.IsCancellationRequested)
+ {
+ using (var scope = _serviceScopeFactory.CreateScope())
{
- using (var scope = _serviceScopeFactory.CreateScope())
+ ILogger _filelogger = scope.ServiceProvider.GetRequiredService>();
+
+ try
{
+ var jobs = scope.ServiceProvider.GetRequiredService();
+ var jobLogs = scope.ServiceProvider.GetRequiredService();
+ var tenantRepository = scope.ServiceProvider.GetRequiredService();
+ var tenantManager = scope.ServiceProvider.GetRequiredService();
+
// get name of job
string jobType = Utilities.GetFullTypeName(GetType().AssemblyQualifiedName);
// load jobs and find current job
- IJobRepository jobs = scope.ServiceProvider.GetRequiredService();
Job job = jobs.GetJobs().Where(item => item.JobType == jobType).FirstOrDefault();
if (job != null && job.IsEnabled && !job.IsExecuting)
{
@@ -73,7 +80,9 @@ namespace Oqtane.Infrastructure
// determine if the job should be run
if (NextExecution <= DateTime.UtcNow && (job.EndDate == null || job.EndDate >= DateTime.UtcNow))
{
- IJobLogRepository jobLogs = scope.ServiceProvider.GetRequiredService();
+ // update the job to indicate it is running
+ job.IsExecuting = true;
+ jobs.UpdateJob(job);
// create a job log entry
JobLog log = new JobLog();
@@ -84,16 +93,10 @@ namespace Oqtane.Infrastructure
log.Notes = "";
log = jobLogs.AddJobLog(log);
- // update the job to indicate it is running
- job.IsExecuting = true;
- jobs.UpdateJob(job);
-
// execute the job
try
{
var notes = "";
- var tenantRepository = scope.ServiceProvider.GetRequiredService();
- var tenantManager = scope.ServiceProvider.GetRequiredService();
foreach (var tenant in tenantRepository.GetTenants())
{
// set tenant and execute job
@@ -133,16 +136,19 @@ namespace Oqtane.Infrastructure
}
}
}
-
- // wait 1 minute
- await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
+ catch (Exception ex)
+ {
+ // can occur during the initial installation because the database has not yet been created
+ if (!ex.Message.Contains("No database provider has been configured for this DbContext"))
+ {
+ _filelogger.LogError(Utilities.LogMessage(this, $"An Error Occurred Executing Scheduled Job: {Name} - {ex}"));
+ }
+ }
}
- }
- catch
- {
- // can occur during the initial installation as there is no DBContext
- }
+ // wait 1 minute
+ await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
+ }
}
private DateTime CalculateNextExecution(DateTime nextExecution, Job job)
@@ -191,9 +197,11 @@ namespace Oqtane.Infrastructure
public Task StartAsync(CancellationToken cancellationToken)
{
- try
+ using (var scope = _serviceScopeFactory.CreateScope())
{
- using (var scope = _serviceScopeFactory.CreateScope())
+ ILogger _filelogger = scope.ServiceProvider.GetRequiredService>();
+
+ try
{
string jobTypeName = Utilities.GetFullTypeName(GetType().AssemblyQualifiedName);
IJobRepository jobs = scope.ServiceProvider.GetRequiredService();
@@ -207,7 +215,7 @@ namespace Oqtane.Infrastructure
}
else
{
- // auto registration - job will not run on initial installation but will run after restart
+ // auto registration - job will not run on initial installation due to no DBContext but will run after restart
job = new Job { JobType = jobTypeName };
// optional HostedServiceBase properties
@@ -233,17 +241,21 @@ namespace Oqtane.Infrastructure
jobs.AddJob(job);
}
}
-
- _executingTask = ExecuteAsync(_cancellationTokenSource.Token);
-
- if (_executingTask.IsCompleted)
+ catch (Exception ex)
{
- return _executingTask;
+ // can occur during the initial installation because the database has not yet been created
+ if (!ex.Message.Contains("No database provider has been configured for this DbContext"))
+ {
+ _filelogger.LogError(Utilities.LogMessage(this, $"An Error Occurred Starting Scheduled Job: {Name} - {ex}"));
+ }
}
}
- catch
+
+ _executingTask = ExecuteAsync(_cancellationTokenSource.Token);
+
+ if (_executingTask.IsCompleted)
{
- // can occur during the initial installation because this method is called during startup and the database has not yet been created
+ return _executingTask;
}
return Task.CompletedTask;
@@ -251,9 +263,11 @@ namespace Oqtane.Infrastructure
public async Task StopAsync(CancellationToken cancellationToken)
{
- try
+ using (var scope = _serviceScopeFactory.CreateScope())
{
- using (var scope = _serviceScopeFactory.CreateScope())
+ ILogger _filelogger = scope.ServiceProvider.GetRequiredService>();
+
+ try
{
string jobTypeName = Utilities.GetFullTypeName(GetType().AssemblyQualifiedName);
IJobRepository jobs = scope.ServiceProvider.GetRequiredService();
@@ -266,10 +280,11 @@ namespace Oqtane.Infrastructure
jobs.UpdateJob(job);
}
}
- }
- catch
- {
- // error updating the job
+ catch (Exception ex)
+ {
+ // error updating the job
+ _filelogger.LogError(Utilities.LogMessage(this, $"An Error Occurred Stopping Scheduled Job: {Name} - {ex}"));
+ }
}
// stop called without start
diff --git a/Oqtane.Server/Infrastructure/Jobs/NotificationJob.cs b/Oqtane.Server/Infrastructure/Jobs/NotificationJob.cs
index ea12ac21..a761bd0f 100644
--- a/Oqtane.Server/Infrastructure/Jobs/NotificationJob.cs
+++ b/Oqtane.Server/Infrastructure/Jobs/NotificationJob.cs
@@ -42,119 +42,127 @@ namespace Oqtane.Infrastructure
// get site settings
List sitesettings = settingRepository.GetSettings(EntityNames.Site, site.SiteId).ToList();
Dictionary settings = GetSettings(sitesettings);
- if (settings.ContainsKey("SMTPHost") && settings["SMTPHost"] != "" &&
- settings.ContainsKey("SMTPPort") && settings["SMTPPort"] != "" &&
- settings.ContainsKey("SMTPSSL") && settings["SMTPSSL"] != "" &&
- settings.ContainsKey("SMTPSender") && settings["SMTPSender"] != "")
+ if (!settings.ContainsKey("SMTPEnabled") || settings["SMTPEnabled"] == "True")
{
- // construct SMTP Client
- var client = new SmtpClient()
+ if (settings.ContainsKey("SMTPHost") && settings["SMTPHost"] != "" &&
+ settings.ContainsKey("SMTPPort") && settings["SMTPPort"] != "" &&
+ settings.ContainsKey("SMTPSSL") && settings["SMTPSSL"] != "" &&
+ settings.ContainsKey("SMTPSender") && settings["SMTPSender"] != "")
{
- DeliveryMethod = SmtpDeliveryMethod.Network,
- UseDefaultCredentials = false,
- Host = settings["SMTPHost"],
- Port = int.Parse(settings["SMTPPort"]),
- EnableSsl = bool.Parse(settings["SMTPSSL"])
- };
- if (settings["SMTPUsername"] != "" && settings["SMTPPassword"] != "")
- {
- client.Credentials = new NetworkCredential(settings["SMTPUsername"], settings["SMTPPassword"]);
- }
-
- // iterate through undelivered notifications
- int sent = 0;
- List notifications = notificationRepository.GetNotifications(site.SiteId, -1, -1).ToList();
- foreach (Notification notification in notifications)
- {
- // get sender and receiver information from user object if not provided
- if ((string.IsNullOrEmpty(notification.FromEmail) || string.IsNullOrEmpty(notification.FromDisplayName)) && notification.FromUserId != null)
+ // construct SMTP Client
+ var client = new SmtpClient()
{
- var user = userRepository.GetUser(notification.FromUserId.Value);
- if (user != null)
- {
- notification.FromEmail = (string.IsNullOrEmpty(notification.FromEmail)) ? user.Email : notification.FromEmail;
- notification.FromDisplayName = (string.IsNullOrEmpty(notification.FromDisplayName)) ? user.DisplayName : notification.FromDisplayName;
- }
- }
- if ((string.IsNullOrEmpty(notification.ToEmail) || string.IsNullOrEmpty(notification.ToDisplayName)) && notification.ToUserId != null)
+ DeliveryMethod = SmtpDeliveryMethod.Network,
+ UseDefaultCredentials = false,
+ Host = settings["SMTPHost"],
+ Port = int.Parse(settings["SMTPPort"]),
+ EnableSsl = bool.Parse(settings["SMTPSSL"])
+ };
+ if (settings["SMTPUsername"] != "" && settings["SMTPPassword"] != "")
{
- var user = userRepository.GetUser(notification.ToUserId.Value);
- if (user != null)
- {
- notification.ToEmail = (string.IsNullOrEmpty(notification.ToEmail)) ? user.Email : notification.ToEmail;
- notification.ToDisplayName = (string.IsNullOrEmpty(notification.ToDisplayName)) ? user.DisplayName : notification.ToDisplayName;
- }
+ client.Credentials = new NetworkCredential(settings["SMTPUsername"], settings["SMTPPassword"]);
}
- // validate recipient
- if (string.IsNullOrEmpty(notification.ToEmail))
+ // iterate through undelivered notifications
+ int sent = 0;
+ List notifications = notificationRepository.GetNotifications(site.SiteId, -1, -1).ToList();
+ foreach (Notification notification in notifications)
{
- log += "Recipient Missing For NotificationId: " + notification.NotificationId + " ";
- notification.IsDeleted = true;
- notificationRepository.UpdateNotification(notification);
- }
- else
- {
- MailMessage mailMessage = new MailMessage();
-
- // sender
- if (settings.ContainsKey("SMTPRelay") && settings["SMTPRelay"] == "True" && !string.IsNullOrEmpty(notification.FromEmail))
+ // get sender and receiver information from user object if not provided
+ if ((string.IsNullOrEmpty(notification.FromEmail) || string.IsNullOrEmpty(notification.FromDisplayName)) && notification.FromUserId != null)
{
- if (!string.IsNullOrEmpty(notification.FromDisplayName))
+ var user = userRepository.GetUser(notification.FromUserId.Value);
+ if (user != null)
{
- mailMessage.From = new MailAddress(notification.FromEmail, notification.FromDisplayName);
+ notification.FromEmail = (string.IsNullOrEmpty(notification.FromEmail)) ? user.Email : notification.FromEmail;
+ notification.FromDisplayName = (string.IsNullOrEmpty(notification.FromDisplayName)) ? user.DisplayName : notification.FromDisplayName;
+ }
+ }
+ if ((string.IsNullOrEmpty(notification.ToEmail) || string.IsNullOrEmpty(notification.ToDisplayName)) && notification.ToUserId != null)
+ {
+ var user = userRepository.GetUser(notification.ToUserId.Value);
+ if (user != null)
+ {
+ notification.ToEmail = (string.IsNullOrEmpty(notification.ToEmail)) ? user.Email : notification.ToEmail;
+ notification.ToDisplayName = (string.IsNullOrEmpty(notification.ToDisplayName)) ? user.DisplayName : notification.ToDisplayName;
+ }
+ }
+
+ // validate recipient
+ if (string.IsNullOrEmpty(notification.ToEmail))
+ {
+ log += "Recipient Missing For NotificationId: " + notification.NotificationId + " ";
+ notification.IsDeleted = true;
+ notificationRepository.UpdateNotification(notification);
+ }
+ else
+ {
+ MailMessage mailMessage = new MailMessage();
+
+ // sender
+ if (settings.ContainsKey("SMTPRelay") && settings["SMTPRelay"] == "True" && !string.IsNullOrEmpty(notification.FromEmail))
+ {
+ if (!string.IsNullOrEmpty(notification.FromDisplayName))
+ {
+ mailMessage.From = new MailAddress(notification.FromEmail, notification.FromDisplayName);
+ }
+ else
+ {
+ mailMessage.From = new MailAddress(notification.FromEmail);
+ }
}
else
{
- mailMessage.From = new MailAddress(notification.FromEmail);
+ mailMessage.From = new MailAddress(settings["SMTPSender"], (!string.IsNullOrEmpty(notification.FromDisplayName)) ? notification.FromDisplayName : site.Name);
+ }
+
+ // recipient
+ if (!string.IsNullOrEmpty(notification.ToDisplayName))
+ {
+ mailMessage.To.Add(new MailAddress(notification.ToEmail, notification.ToDisplayName));
+ }
+ else
+ {
+ mailMessage.To.Add(new MailAddress(notification.ToEmail));
+ }
+
+ // subject
+ mailMessage.Subject = notification.Subject;
+
+ //body
+ mailMessage.Body = notification.Body;
+
+ // encoding
+ mailMessage.SubjectEncoding = System.Text.Encoding.UTF8;
+ mailMessage.BodyEncoding = System.Text.Encoding.UTF8;
+ mailMessage.IsBodyHtml = true;
+
+ // send mail
+ try
+ {
+ client.Send(mailMessage);
+ sent++;
+ notification.IsDelivered = true;
+ notification.DeliveredOn = DateTime.UtcNow;
+ notificationRepository.UpdateNotification(notification);
+ }
+ catch (Exception ex)
+ {
+ // error
+ log += ex.Message + " ";
}
}
- else
- {
- mailMessage.From = new MailAddress(settings["SMTPSender"], (!string.IsNullOrEmpty(notification.FromDisplayName)) ? notification.FromDisplayName : site.Name);
- }
-
- // recipient
- if (!string.IsNullOrEmpty(notification.ToDisplayName))
- {
- mailMessage.To.Add(new MailAddress(notification.ToEmail, notification.ToDisplayName));
- }
- else
- {
- mailMessage.To.Add(new MailAddress(notification.ToEmail));
- }
-
- // subject
- mailMessage.Subject = notification.Subject;
-
- //body
- mailMessage.Body = notification.Body;
-
- // encoding
- mailMessage.SubjectEncoding = System.Text.Encoding.UTF8;
- mailMessage.BodyEncoding = System.Text.Encoding.UTF8;
-
- // send mail
- try
- {
- client.Send(mailMessage);
- sent++;
- notification.IsDelivered = true;
- notification.DeliveredOn = DateTime.UtcNow;
- notificationRepository.UpdateNotification(notification);
- }
- catch (Exception ex)
- {
- // error
- log += ex.Message + " ";
- }
}
+ log += "Notifications Delivered: " + sent + " ";
+ }
+ else
+ {
+ log += "SMTP Not Configured Properly In Site Settings - Host, Port, SSL, And Sender Are All Required" + " ";
}
- log += "Notifications Delivered: " + sent + " ";
}
else
{
- log += "SMTP Not Configured Properly In Site Settings - Host, Port, SSL, And Sender Are All Required" + " ";
+ log += "SMTP Disabled In Site Settings" + " ";
}
}
diff --git a/Oqtane.Server/Infrastructure/ServerState.cs b/Oqtane.Server/Infrastructure/ServerState.cs
new file mode 100644
index 00000000..6f3ac283
--- /dev/null
+++ b/Oqtane.Server/Infrastructure/ServerState.cs
@@ -0,0 +1,13 @@
+using System.Collections.Generic;
+using Oqtane.Models;
+
+namespace Oqtane.Infrastructure
+{
+ public class ServerState
+ {
+ public int SiteId { get; set; }
+ public List Assemblies { get; set; } = new List();
+ public ListScripts { get; set; } = new List();
+ public bool IsMigrated { get; set; } = false;
+ }
+}
diff --git a/Oqtane.Server/Infrastructure/ServerStateManager.cs b/Oqtane.Server/Infrastructure/ServerStateManager.cs
new file mode 100644
index 00000000..630ea691
--- /dev/null
+++ b/Oqtane.Server/Infrastructure/ServerStateManager.cs
@@ -0,0 +1,49 @@
+using System.Collections.Generic;
+using System.Linq;
+using Oqtane.Models;
+
+namespace Oqtane.Infrastructure
+{
+ // singleton
+ public class ServerStateManager
+ {
+ private List _serverStates { get; set; }
+
+ public ServerStateManager()
+ {
+ _serverStates = new List();
+ }
+
+ public ServerState GetServerState(int siteId)
+ {
+ var serverState = _serverStates.FirstOrDefault(item => item.SiteId == siteId);
+ if (serverState == null)
+ {
+ serverState = new ServerState();
+ serverState.SiteId = siteId;
+ serverState.Assemblies = new List();
+ serverState.Scripts = new List();
+ return serverState;
+ }
+ else
+ {
+ return serverState;
+ }
+ }
+
+ public void SetServerState(int siteId, ServerState serverState)
+ {
+ var serverstate = _serverStates.FirstOrDefault(item => item.SiteId == siteId);
+ if (serverstate == null)
+ {
+ serverState.SiteId = siteId;
+ _serverStates.Add(serverState);
+ }
+ else
+ {
+ serverstate.Assemblies = serverState.Assemblies;
+ serverstate.Scripts = serverState.Scripts;
+ }
+ }
+ }
+}
diff --git a/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs b/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs
index 9df850ea..6a20b984 100644
--- a/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs
+++ b/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs
@@ -57,10 +57,10 @@ namespace Oqtane.SiteTemplates
new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
},
- Content = "
Oqtane is an open source modular application framework that provides advanced functionality for developing web, mobile, and desktop applications on .NET Core. It leverages the Blazor component model to compose a fully dynamic web development experience which can be hosted either client-side or server-side. Whether you are looking for a platform to accelerate your web development efforts, or simply interested in exploring the anatomy of a large-scale Blazor application, Oqtane provides a solid foundation based on proven enterprise architectural principles.
Blazor is an open source and cross-platform web UI framework for building single-page applications using .NET and C#. Blazor applications can be hosted in a variety of ways. Blazor Server uses SignalR (WebSockets) to host your application on a web server and provide a responsive and robust development experience. Blazor WebAssembly relies on Wasm, an open web standard that does not require plugins in order for applications to run natively in a web browser. Blazor Hybrid is part of .NET MAUI and uses a Web View to render components natively on mobile and desktop devices. Razor components can be used with all of the hosting models without any modification.
" +
- "
Blazor is a feature of .NET Core, the popular cross platform web development framework from Microsoft that extends the .NET developer platform with tools and libraries for building web apps.
"
+ Content = "
Oqtane is an open source CMS and Application Framework that provides advanced functionality for developing web, mobile, and desktop applications on .NET. It leverages Blazor to compose a fully dynamic digital experience. Whether you are looking for a platform to accelerate your web development efforts, or simply interested in exploring the anatomy of a large-scale Blazor application, Oqtane provides a solid foundation based on proven enterprise architectural principles and patterns.
Blazor is an open source and cross-platform web UI framework for building single-page applications using .NET and C#. Blazor applications can be hosted in a variety of ways. Blazor Server uses SignalR (WebSockets) to host your application on a web server and provide a responsive and robust development experience. Blazor WebAssembly relies on Wasm, an open web standard that does not require plugins in order for applications to run natively in a web browser. Blazor Hybrid is part of .NET MAUI and uses a Web View to render components natively on mobile and desktop devices. Razor components can be shared across all of the hosting models without any modification.
" +
+ "
Blazor is a feature of ASP.NET, the popular cross platform development framework from Microsoft that provides powerful tools and libraries for building modern software applications.