Merge remote-tracking branch 'upstream/dev' into dev
@@ -1,12 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Version>1.0.0</Version>
|
||||
<AssemblyName>Oqtane.Application.AppHost</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Server\Oqtane.Application.Server.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,29 +0,0 @@
|
||||
{
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:44358/",
|
||||
"sslPort": 0
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"Oqtane.Application": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"applicationUrl": "http://localhost:44358/"
|
||||
},
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
The _content folder should only contain static resources from shared razor component libraries (RCLs). Static resources can be extracted from shared RCL Nuget packages by executing a Publish task on the module's Server project to a local folder and copying the files from the _content folder which is created. Each shared RCL would have its own appropriately named subfolder within the module's _content folder.
|
||||
|
||||
ie.
|
||||
|
||||
/_content
|
||||
/Radzen.Blazor
|
||||
/css
|
||||
/fonts
|
||||
/syncfusion.blazor
|
||||
/scripts
|
||||
/styles
|
||||
@@ -1 +0,0 @@
|
||||
This is the location where static resources such as images or style sheets should be located
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
public override List<Resource> Resources => new List<Resource>()
|
||||
{
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" }
|
||||
new Stylesheet(ModulePath() + "Module.css")
|
||||
};
|
||||
|
||||
private ElementReference form;
|
||||
|
||||
@@ -38,12 +38,10 @@ else
|
||||
}
|
||||
|
||||
@code {
|
||||
public override string RenderMode => RenderModes.Static;
|
||||
|
||||
public override List<Resource> Resources => new List<Resource>()
|
||||
{
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" },
|
||||
new Resource { ResourceType = ResourceType.Script, Url = ModulePath() + "Module.js" }
|
||||
new Stylesheet(ModulePath() + "Module.css"),
|
||||
new Script(ModulePath() + "Module.js")
|
||||
};
|
||||
|
||||
List<Models.MyModule> _MyModules;
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Version>1.0.0</Version>
|
||||
<AssemblyName>Oqtane.Application.Client.Oqtane</AssemblyName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Version>1.0.0</Version>
|
||||
<AssemblyName>Oqtane.Application.Client.Oqtane</AssemblyName>
|
||||
<NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>
|
||||
<StaticWebAssetProjectMode>Default</StaticWebAssetProjectMode>
|
||||
<BlazorWebAssemblyEnableLinking>false</BlazorWebAssemblyEnableLinking>
|
||||
<BlazorWebAssemblyLoadAllGlobalizationData>true</BlazorWebAssemblyLoadAllGlobalizationData>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Shared\Oqtane.Application.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Shared\Oqtane.Application.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Oqtane.Client" Version="6.1.6" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- there may be other elements here -->
|
||||
<BlazorWebAssemblyEnableLinking>false</BlazorWebAssemblyEnableLinking>
|
||||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Oqtane.Client" Version="6.1.6" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
13
Oqtane.Application/Client/Program.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Oqtane.Application.Client
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
static async Task Main(string[] args)
|
||||
{
|
||||
// defer client startup to Oqtane - do not modify
|
||||
await Oqtane.Client.Program.Main(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,10 @@ namespace Oqtane.Application.Startup
|
||||
{
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddScoped<IMyModuleService, MyModuleService>();
|
||||
if (!services.Any(s => s.ServiceType == typeof(IMyModuleService)))
|
||||
{
|
||||
services.AddScoped<IMyModuleService, MyModuleService>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@ namespace Oqtane.Application.MyTheme
|
||||
ContainerSettingsType = "Oqtane.Application.MyTheme.ContainerSettings, Oqtane.Application.Client.Oqtane",
|
||||
Resources = new List<Resource>()
|
||||
{
|
||||
new Script(Constants.BootstrapStylesheetUrl, Constants.BootstrapStylesheetIntegrity, "anonymous"),
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "~/Theme.css" },
|
||||
new Stylesheet(Constants.BootstrapStylesheetUrl, Constants.BootstrapStylesheetIntegrity, "anonymous"),
|
||||
new Stylesheet("~/Theme.css"),
|
||||
new Script(Constants.BootstrapScriptUrl, Constants.BootstrapScriptIntegrity, "anonymous")
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<metadata>
|
||||
<id>Oqtane.Application.Template</id>
|
||||
<version>6.1.6</version>
|
||||
<title>Oqtane Application Solution For Blazor</title>
|
||||
<title>Oqtane Application Template For Blazor</title>
|
||||
<authors>Shaun Walker</authors>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
|
||||
@@ -2,12 +2,10 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.12.35506.116 d17.12
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Oqtane.Application.AppHost", "AppHost\Oqtane.Application.AppHost.csproj", "{5BDDA15B-05CF-41B2-BF12-D532D1A561D1}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oqtane.Application.Server", "Server\Oqtane.Application.Server.csproj", "{04B05448-788F-433D-92C0-FED35122D45A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oqtane.Application.Client", "Client\Oqtane.Application.Client.csproj", "{AA8E58A1-CD09-4208-BF66-A8BB341FD669}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oqtane.Application.Server", "Server\Oqtane.Application.Server.csproj", "{04B05448-788F-433D-92C0-FED35122D45A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oqtane.Application.Shared", "Shared\Oqtane.Application.Shared.csproj", "{18D73F73-D7BE-4388-85BA-FBD9AC96FCA2}"
|
||||
EndProject
|
||||
Global
|
||||
@@ -16,18 +14,14 @@ Global
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{5BDDA15B-05CF-41B2-BF12-D532D1A561D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5BDDA15B-05CF-41B2-BF12-D532D1A561D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5BDDA15B-05CF-41B2-BF12-D532D1A561D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5BDDA15B-05CF-41B2-BF12-D532D1A561D1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{04B05448-788F-433D-92C0-FED35122D45A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{04B05448-788F-433D-92C0-FED35122D45A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{04B05448-788F-433D-92C0-FED35122D45A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{04B05448-788F-433D-92C0-FED35122D45A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{18D73F73-D7BE-4388-85BA-FBD9AC96FCA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{18D73F73-D7BE-4388-85BA-FBD9AC96FCA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{18D73F73-D7BE-4388-85BA-FBD9AC96FCA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
@@ -36,7 +30,4 @@ Global
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {1D016F15-46FE-4726-8DFD-2E4FD4DC7668}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
@@ -1,25 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
|
||||
<Version>1.0.0</Version>
|
||||
<AssemblyName>Oqtane.Application.Server.Oqtane</AssemblyName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Version>1.0.0</Version>
|
||||
<AssemblyName>Oqtane.Application.Server.Oqtane</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Client\Oqtane.Application.Client.csproj" />
|
||||
<ProjectReference Include="..\Shared\Oqtane.Application.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Client\Oqtane.Application.Client.csproj" />
|
||||
<ProjectReference Include="..\Shared\Oqtane.Application.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Oqtane.Server" Version="6.1.6" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="CopyStaticAssets" AfterTargets="Build">
|
||||
<ItemGroup>
|
||||
<StaticAssets Include="$(ProjectDir)wwwroot\**\*.*" />
|
||||
</ItemGroup>
|
||||
<Copy SourceFiles="@(StaticAssets)" DestinationFolder="$(ProjectDir)..\AppHost\wwwroot\%(RecursiveDir)" SkipUnchangedFiles="true" />
|
||||
</Target>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Oqtane.Server" Version="6.1.6" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.AspNetCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Oqtane.Infrastructure;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Oqtane.Infrastructure;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Oqtane.Application.AppHost
|
||||
namespace Oqtane.Application.Server
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
// defer server startup to Oqtane - do not modify
|
||||
var host = BuildWebHost(args);
|
||||
var databaseManager = host.Services.GetService<IDatabaseManager>();
|
||||
var install = databaseManager.Install();
|
||||
@@ -20,7 +20,7 @@ namespace Oqtane.Application.AppHost
|
||||
var filelogger = host.Services.GetRequiredService<ILogger<Program>>();
|
||||
if (filelogger != null)
|
||||
{
|
||||
filelogger.LogError($"[Oqtane.Application.AppHost.Program.Main] {install.Message}");
|
||||
filelogger.LogError($"[Oqtane.Application.Server.Program.Main] {install.Message}");
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -35,9 +35,8 @@ namespace Oqtane.Application.AppHost
|
||||
.AddCommandLine(args)
|
||||
.AddEnvironmentVariables()
|
||||
.Build())
|
||||
.UseStartup<Oqtane.Startup>()
|
||||
.UseStartup<Startup>()
|
||||
.ConfigureLocalizationSettings()
|
||||
.Build();
|
||||
}
|
||||
}
|
||||
|
||||
25
Oqtane.Application/Server/Properties/launchSettings.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
|
||||
"applicationUrl": "http://localhost:5084",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
|
||||
"applicationUrl": "https://localhost:7035;http://localhost:5084",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
45
Oqtane.Application/Server/Startup.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Oqtane.Extensions;
|
||||
using Oqtane.Infrastructure;
|
||||
using Oqtane.Shared;
|
||||
using Microsoft.AspNetCore.Cors.Infrastructure;
|
||||
|
||||
namespace Oqtane.Application.Server
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
private readonly IConfigurationRoot _configuration;
|
||||
private readonly IWebHostEnvironment _environment;
|
||||
|
||||
public Startup(IWebHostEnvironment environment)
|
||||
{
|
||||
AppDomain.CurrentDomain.SetData(Constants.DataDirectory, Path.Combine(environment.ContentRootPath, "Data"));
|
||||
|
||||
var builder = new ConfigurationBuilder()
|
||||
.SetBasePath(environment.ContentRootPath)
|
||||
.AddJsonFile("appsettings.json", false, true)
|
||||
.AddJsonFile($"appsettings.{environment.EnvironmentName}.json", true, true)
|
||||
.AddEnvironmentVariables();
|
||||
|
||||
_configuration = builder.Build();
|
||||
_environment = environment;
|
||||
}
|
||||
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
// defer server startup to Oqtane - do not modify
|
||||
services.AddOqtane(_configuration, _environment);
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app, IConfigurationRoot configuration, IWebHostEnvironment environment, ICorsService corsService, ICorsPolicyProvider corsPolicyProvider, ISyncManager sync)
|
||||
{
|
||||
// defer server startup to Oqtane - do not modify
|
||||
app.UseOqtane(configuration, environment, corsService, corsPolicyProvider, sync);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"RenderMode": "Interactive",
|
||||
"RenderMode": "Static",
|
||||
"Runtime": "Server",
|
||||
"Database": {
|
||||
"DefaultDBType": "Oqtane.Database.SqlServer.SqlServerDatabase, Oqtane.Server"
|
||||
"DefaultDBType": ""
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"DefaultConnection": ""
|
||||
@@ -57,8 +57,7 @@
|
||||
}
|
||||
},
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Notify": "Error"
|
||||
"Default": "Information"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
The _content folder should only contain static resources from shared razor component libraries (RCLs). Static resources can be extracted from shared RCL Nuget packages by executing a Publish task on the module's Server project to a local folder and copying the files from the _content folder which is created. Each shared RCL would have its own appropriately named subfolder within the module's _content folder.
|
||||
|
||||
ie.
|
||||
|
||||
/_content
|
||||
/Radzen.Blazor
|
||||
/css
|
||||
/fonts
|
||||
/syncfusion.blazor
|
||||
/scripts
|
||||
/styles
|
||||
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 318 B After Width: | Height: | Size: 318 B |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 427 B After Width: | Height: | Size: 427 B |
|
Before Width: | Height: | Size: 875 B After Width: | Height: | Size: 875 B |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 801 B After Width: | Height: | Size: 801 B |
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 7.9 KiB |
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 7.8 KiB |
|
Before Width: | Height: | Size: 177 B After Width: | Height: | Size: 177 B |
|
Before Width: | Height: | Size: 438 B After Width: | Height: | Size: 438 B |
|
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 8.0 KiB |
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
@@ -1,13 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Version>1.0.0</Version>
|
||||
<AssemblyName>Oqtane.Application.Shared.Oqtane</AssemblyName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Version>1.0.0</Version>
|
||||
<AssemblyName>Oqtane.Application.Shared.Oqtane</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Oqtane.Shared" Version="6.1.6" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Oqtane.Shared" Version="6.1.6" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -182,7 +182,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
_databaseName = "LocalDB";
|
||||
_databaseName = Constants.DefaultDBName;
|
||||
}
|
||||
LoadDatabaseConfigComponent();
|
||||
|
||||
@@ -269,8 +269,8 @@
|
||||
SiteName = Constants.DefaultSite,
|
||||
Register = _register,
|
||||
SiteTemplate = _template,
|
||||
RenderMode = RenderModes.Static,
|
||||
Runtime = Runtimes.Server
|
||||
RenderMode = "", // provided by appsettings.json
|
||||
Runtime = "" // provided by appsettings.json
|
||||
};
|
||||
|
||||
var installation = await InstallationService.Install(config);
|
||||
|
||||
@@ -665,7 +665,8 @@
|
||||
if (tenant != null)
|
||||
{
|
||||
_tenant = tenant.Name;
|
||||
_database = _databases.Find(item => item.DBType == tenant.DBType && item.Name != "LocalDB")?.Name;
|
||||
// hack - there are 3 providers with SqlServerDatabase DBTypes - so we are choosing the last one in alphabetical order
|
||||
_database = _databases.Where(item => item.DBType == tenant.DBType).OrderBy(item => item.Name).Last()?.Name;
|
||||
_connectionstring = tenant.DBConnectionString;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,7 +237,7 @@ else
|
||||
}
|
||||
else
|
||||
{
|
||||
_databaseName = "LocalDB";
|
||||
_databaseName = Constants.DefaultDBName;
|
||||
}
|
||||
LoadDatabaseConfigComponent();
|
||||
}
|
||||
|
||||
@@ -200,7 +200,8 @@ else
|
||||
if (tenant != null)
|
||||
{
|
||||
_tenant = tenant.Name;
|
||||
_databasetype = _databases.FirstOrDefault(item => item.DBType == tenant.DBType && item.Name != "LocalDB").Name;
|
||||
// hack - there are 3 providers with SqlServerDatabase DBTypes - so we are choosing the last one in alphabetical order
|
||||
_databasetype = _databases.Where(item => item.DBType == tenant.DBType).OrderBy(item => item.Name).Last()?.Name;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -211,7 +212,7 @@ else
|
||||
}
|
||||
else
|
||||
{
|
||||
_databasetype = "LocalDB";
|
||||
_databasetype = Constants.DefaultDBName;
|
||||
}
|
||||
_showConnectionString = false;
|
||||
LoadDatabaseConfigComponent();
|
||||
|
||||
@@ -114,6 +114,12 @@ else
|
||||
<input id="cookiename" class="form-control" @bind="@_cookiename" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="cookiedomain" HelpText="If you would like to share cookies across subdomains you will need to specify a root domain with a leading dot (ie. '.example.com')" ResourceKey="CookieDomain">Cookie Domain:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="cookiedomain" class="form-control" @bind="@_cookiedomain" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="cookieexpiration" HelpText="You can choose to use a custom authentication cookie expiration timespan for each site (e.g. '08:00:00' for 8 hours). The default is 14 days if not specified." ResourceKey="CookieExpiration">Cookie Expiration Timespan:</Label>
|
||||
<div class="col-sm-9">
|
||||
@@ -314,6 +320,15 @@ else
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="requirenonce" HelpText="Specify if Nonce validation is required for the ID token (the default is true)" ResourceKey="RequireNonce">Require Nonce?</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="requirenonce" class="form-select" @bind="@_requirenonce" required>
|
||||
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||
<option value="false">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="scopes" HelpText="A list of Scopes to request from the provider (separated by commas). If none are specified, standard Scopes will be used by default." ResourceKey="Scopes">Scopes:</Label>
|
||||
@@ -516,6 +531,7 @@ else
|
||||
private string _requireconfirmedemail;
|
||||
private string _twofactor;
|
||||
private string _cookiename;
|
||||
private string _cookiedomain;
|
||||
private string _cookieexpiration;
|
||||
private string _alwaysremember;
|
||||
private string _logouteverywhere;
|
||||
@@ -543,6 +559,7 @@ else
|
||||
private string _clientsecrettype = "password";
|
||||
private string _toggleclientsecret = string.Empty;
|
||||
private string _authresponsetype;
|
||||
private string _requirenonce;
|
||||
private string _scopes;
|
||||
private string _parameters;
|
||||
private string _pkce;
|
||||
@@ -590,6 +607,7 @@ else
|
||||
{
|
||||
_twofactor = SettingService.GetSetting(settings, "LoginOptions:TwoFactor", "false");
|
||||
_cookiename = SettingService.GetSetting(settings, "LoginOptions:CookieName", ".AspNetCore.Identity.Application");
|
||||
_cookiedomain = SettingService.GetSetting(settings, "LoginOptions:CookieDomain", "");
|
||||
_cookieexpiration = SettingService.GetSetting(settings, "LoginOptions:CookieExpiration", "");
|
||||
_alwaysremember = SettingService.GetSetting(settings, "LoginOptions:AlwaysRemember", "false");
|
||||
_logouteverywhere = SettingService.GetSetting(settings, "LoginOptions:LogoutEverywhere", "false");
|
||||
@@ -629,6 +647,7 @@ else
|
||||
_clientsecret = SettingService.GetSetting(settings, "ExternalLogin:ClientSecret", "");
|
||||
_toggleclientsecret = SharedLocalizer["ShowPassword"];
|
||||
_authresponsetype = SettingService.GetSetting(settings, "ExternalLogin:AuthResponseType", "code");
|
||||
_requirenonce = SettingService.GetSetting(settings, "ExternalLogin:RequireNonce", "true");
|
||||
_scopes = SettingService.GetSetting(settings, "ExternalLogin:Scopes", "");
|
||||
_parameters = SettingService.GetSetting(settings, "ExternalLogin:Parameters", "");
|
||||
_pkce = SettingService.GetSetting(settings, "ExternalLogin:PKCE", "false");
|
||||
@@ -725,6 +744,7 @@ else
|
||||
settings = SettingService.SetSetting(settings, "LoginOptions:RequireConfirmedEmail", _requireconfirmedemail, false);
|
||||
settings = SettingService.SetSetting(settings, "LoginOptions:TwoFactor", _twofactor, false);
|
||||
settings = SettingService.SetSetting(settings, "LoginOptions:CookieName", _cookiename, true);
|
||||
settings = SettingService.SetSetting(settings, "LoginOptions:CookieDomain", _cookiedomain, true);
|
||||
settings = SettingService.SetSetting(settings, "LoginOptions:CookieExpiration", _cookieexpiration, true);
|
||||
settings = SettingService.SetSetting(settings, "LoginOptions:AlwaysRemember", _alwaysremember, false);
|
||||
settings = SettingService.SetSetting(settings, "LoginOptions:LogoutEverywhere", _logouteverywhere, false);
|
||||
@@ -750,6 +770,7 @@ else
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:ClientId", _clientid, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:ClientSecret", _clientsecret, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:AuthResponseType", _authresponsetype, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:RequireNonce", _requirenonce, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:Scopes", _scopes, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:Parameters", _parameters, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:PKCE", _pkce, true);
|
||||
|
||||
@@ -140,13 +140,22 @@ namespace Oqtane.Modules
|
||||
}
|
||||
}
|
||||
|
||||
// path method
|
||||
// path methods
|
||||
|
||||
public string ModulePath()
|
||||
{
|
||||
return PageState?.Alias.BaseUrl + "/Modules/" + GetType().Namespace + "/";
|
||||
}
|
||||
|
||||
public string StaticAssetPath
|
||||
{
|
||||
get
|
||||
{
|
||||
// requires module to have implemented IModule
|
||||
return PageState?.Alias.BaseUrl + "_content/" + ModuleState.ModuleDefinition?.PackageName + "/";
|
||||
}
|
||||
}
|
||||
|
||||
// fingerprint hash code for static assets
|
||||
|
||||
public string Fingerprint
|
||||
|
||||
@@ -513,6 +513,12 @@
|
||||
<data name="OIDC" xml:space="preserve">
|
||||
<value>OpenID Connect (OIDC)</value>
|
||||
</data>
|
||||
<data name="RequireNonce.Text" xml:space="preserve">
|
||||
<value>Require Nonce?</value>
|
||||
</data>
|
||||
<data name="RequireNonce.HelpText" xml:space="preserve">
|
||||
<value>Specify if Nonce validation is required for the ID token (the default is true)</value>
|
||||
</data>
|
||||
<data name="SaveTokens.Text" xml:space="preserve">
|
||||
<value>Save Tokens?</value>
|
||||
</data>
|
||||
@@ -543,4 +549,10 @@
|
||||
<data name="Deleted Users" xml:space="preserve">
|
||||
<value>Deleted Users</value>
|
||||
</data>
|
||||
<data name="CookieDomain.Text" xml:space="preserve">
|
||||
<value>Cookie Domain:</value>
|
||||
</data>
|
||||
<data name="CookieDomain.HelpText" xml:space="preserve">
|
||||
<value>If you would like to share cookies across subdomains you will need to specify a root domain with a leading dot (ie. '.example.com')</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -108,13 +108,22 @@ namespace Oqtane.Themes
|
||||
}
|
||||
}
|
||||
|
||||
// path method
|
||||
// path methods
|
||||
|
||||
public string ThemePath()
|
||||
{
|
||||
return PageState?.Alias.BaseUrl + "/Themes/" + GetType().Namespace + "/";
|
||||
}
|
||||
|
||||
public string StaticAssetPath
|
||||
{
|
||||
get
|
||||
{
|
||||
// requires theme to have implemented ITheme
|
||||
return PageState?.Alias.BaseUrl + "_content/" + ThemeState?.PackageName + "/";
|
||||
}
|
||||
}
|
||||
|
||||
// fingerprint hash code for static assets
|
||||
public string Fingerprint
|
||||
{
|
||||
|
||||
BIN
Oqtane.Package/FixProps.exe
Normal file
@@ -2,14 +2,105 @@ using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Cors.Infrastructure;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Oqtane.Components;
|
||||
using Oqtane.Infrastructure;
|
||||
using Oqtane.Shared;
|
||||
using Oqtane.UI;
|
||||
using OqtaneSSR.Extensions;
|
||||
|
||||
namespace Oqtane.Extensions
|
||||
{
|
||||
public static class ApplicationBuilderExtensions
|
||||
{
|
||||
public static IApplicationBuilder UseOqtane(this IApplicationBuilder app, IConfigurationRoot configuration, IWebHostEnvironment environment, ICorsService corsService, ICorsPolicyProvider corsPolicyProvider, ISyncManager sync)
|
||||
{
|
||||
ServiceActivator.Configure(app.ApplicationServices);
|
||||
|
||||
if (environment.IsDevelopment())
|
||||
{
|
||||
app.UseWebAssemblyDebugging();
|
||||
app.UseForwardedHeaders();
|
||||
}
|
||||
else
|
||||
{
|
||||
app.UseForwardedHeaders();
|
||||
app.UseExceptionHandler("/Error", createScopeForErrors: true);
|
||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
||||
app.UseHsts();
|
||||
}
|
||||
|
||||
// allow oqtane localization middleware
|
||||
app.UseOqtaneLocalization();
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
app.UseStaticFiles(new StaticFileOptions
|
||||
{
|
||||
OnPrepareResponse = (ctx) =>
|
||||
{
|
||||
// static asset caching
|
||||
var cachecontrol = configuration.GetSection("CacheControl");
|
||||
if (!string.IsNullOrEmpty(cachecontrol.Value))
|
||||
{
|
||||
ctx.Context.Response.Headers.Append(HeaderNames.CacheControl, cachecontrol.Value);
|
||||
}
|
||||
// CORS headers for .NET MAUI clients
|
||||
var policy = corsPolicyProvider.GetPolicyAsync(ctx.Context, Constants.MauiCorsPolicy)
|
||||
.ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
corsService.ApplyResult(corsService.EvaluatePolicy(ctx.Context, policy), ctx.Context.Response);
|
||||
}
|
||||
});
|
||||
app.UseExceptionMiddleWare();
|
||||
app.UseTenantResolution();
|
||||
app.UseJwtAuthorization();
|
||||
app.UseRouting();
|
||||
app.UseCors();
|
||||
app.UseOutputCache();
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
app.UseAntiforgery();
|
||||
|
||||
// execute any IServerStartup logic
|
||||
app.ConfigureOqtaneAssemblies(environment);
|
||||
|
||||
if (configuration.GetSection("UseSwagger").Value != "false")
|
||||
{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/" + Constants.Version + "/swagger.json", Constants.PackageId + " " + Constants.Version); });
|
||||
}
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapControllers();
|
||||
endpoints.MapRazorPages();
|
||||
});
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapRazorComponents<App>()
|
||||
.AddInteractiveServerRenderMode()
|
||||
.AddInteractiveWebAssemblyRenderMode()
|
||||
.AddAdditionalAssemblies(typeof(SiteRouter).Assembly);
|
||||
});
|
||||
|
||||
// simulate the fallback routing approach of traditional Blazor - allowing the custom SiteRouter to handle all routing concerns
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapFallback();
|
||||
});
|
||||
|
||||
// create a global sync event to identify server application startup
|
||||
sync.AddSyncEvent(-1, -1, EntityNames.Host, -1, SyncEventActions.Reload);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
public static IApplicationBuilder ConfigureOqtaneAssemblies(this IApplicationBuilder app, IWebHostEnvironment env)
|
||||
{
|
||||
var startUps = AppDomain.CurrentDomain
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.IO;
|
||||
@@ -11,12 +12,17 @@ using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication.OAuth;
|
||||
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Oqtane.Extensions;
|
||||
using Oqtane.Infrastructure;
|
||||
using Oqtane.Interfaces;
|
||||
using Oqtane.Managers;
|
||||
@@ -31,10 +37,126 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||
{
|
||||
public static class OqtaneServiceCollectionExtensions
|
||||
{
|
||||
public static IServiceCollection AddOqtane(this IServiceCollection services, string[] installedCultures)
|
||||
public static IServiceCollection AddOqtane(this IServiceCollection services, IConfigurationRoot configuration, IWebHostEnvironment environment)
|
||||
{
|
||||
// process forwarded headers on load balancers and proxy servers
|
||||
services.Configure<ForwardedHeadersOptions>(options =>
|
||||
{
|
||||
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
|
||||
});
|
||||
|
||||
// register localization services
|
||||
services.AddLocalization(options => options.ResourcesPath = "Resources");
|
||||
|
||||
services.AddOptions<List<Oqtane.Models.Database>>().Bind(configuration.GetSection(SettingKeys.AvailableDatabasesSection));
|
||||
|
||||
// register scoped core services
|
||||
services.AddScoped<IAuthorizationHandler, PermissionHandler>()
|
||||
.AddOqtaneServerScopedServices();
|
||||
|
||||
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||
|
||||
// setup HttpClient for server side in a client side compatible fashion ( with auth cookie )
|
||||
services.AddHttpClients();
|
||||
|
||||
// register singleton scoped core services
|
||||
services.AddSingleton(configuration)
|
||||
.AddOqtaneSingletonServices();
|
||||
|
||||
// install any modules or themes ( this needs to occur BEFORE the assemblies are loaded into the app domain )
|
||||
InstallationManager.InstallPackages(environment.WebRootPath, environment.ContentRootPath);
|
||||
|
||||
// register transient scoped core services
|
||||
services.AddOqtaneTransientServices();
|
||||
|
||||
// load the external assemblies into the app domain, install services
|
||||
services.AddOqtaneAssemblies();
|
||||
services.AddOqtaneDbContext();
|
||||
|
||||
services.AddAntiforgery(options =>
|
||||
{
|
||||
options.HeaderName = Constants.AntiForgeryTokenHeaderName;
|
||||
options.Cookie.Name = Constants.AntiForgeryTokenCookieName;
|
||||
options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Strict;
|
||||
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
|
||||
options.Cookie.HttpOnly = true;
|
||||
});
|
||||
|
||||
services.AddIdentityCore<IdentityUser>(options => { })
|
||||
.AddEntityFrameworkStores<TenantDBContext>()
|
||||
.AddSignInManager()
|
||||
.AddDefaultTokenProviders()
|
||||
.AddClaimsPrincipalFactory<ClaimsPrincipalFactory<IdentityUser>>(); // role claims
|
||||
|
||||
services.ConfigureOqtaneIdentityOptions(configuration);
|
||||
|
||||
services.AddCascadingAuthenticationState();
|
||||
services.AddScoped<AuthenticationStateProvider, IdentityRevalidatingAuthenticationStateProvider>();
|
||||
services.AddAuthorization();
|
||||
|
||||
services.AddAuthentication(options =>
|
||||
{
|
||||
options.DefaultScheme = Constants.AuthenticationScheme;
|
||||
})
|
||||
.AddCookie(Constants.AuthenticationScheme)
|
||||
.AddOpenIdConnect(AuthenticationProviderTypes.OpenIDConnect, options => { })
|
||||
.AddOAuth(AuthenticationProviderTypes.OAuth2, options => { });
|
||||
|
||||
services.ConfigureOqtaneCookieOptions();
|
||||
services.ConfigureOqtaneAuthenticationOptions(configuration);
|
||||
|
||||
services.AddOqtaneSiteOptions()
|
||||
.WithSiteIdentity()
|
||||
.WithSiteAuthentication();
|
||||
|
||||
services.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy(Constants.MauiCorsPolicy,
|
||||
policy =>
|
||||
{
|
||||
// allow .NET MAUI client cross origin calls
|
||||
policy.WithOrigins("https://0.0.0.1", "http://0.0.0.1", "app://0.0.0.1")
|
||||
.AllowAnyHeader().AllowAnyMethod().AllowCredentials();
|
||||
});
|
||||
});
|
||||
|
||||
services.AddOutputCache();
|
||||
|
||||
services.AddMvc(options =>
|
||||
{
|
||||
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
|
||||
})
|
||||
.AddOqtaneApplicationParts() // register any Controllers from custom modules
|
||||
.ConfigureOqtaneMvc(); // any additional configuration from IStartup classes
|
||||
|
||||
services.AddRazorPages();
|
||||
|
||||
services.AddRazorComponents()
|
||||
.AddInteractiveServerComponents(options =>
|
||||
{
|
||||
if (environment.IsDevelopment())
|
||||
{
|
||||
options.DetailedErrors = true;
|
||||
}
|
||||
}).AddHubOptions(options =>
|
||||
{
|
||||
options.MaximumReceiveMessageSize = null; // no limit (for large amounts of data ie. textarea components)
|
||||
})
|
||||
.AddInteractiveWebAssemblyComponents();
|
||||
|
||||
services.AddSwaggerGen(options =>
|
||||
{
|
||||
options.CustomSchemaIds(type => type.ToString()); // Handle SchemaId already used for different type
|
||||
});
|
||||
services.TryAddSwagger(configuration);
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddOqtaneAssemblies(this IServiceCollection services)
|
||||
{
|
||||
LoadAssemblies();
|
||||
LoadSatelliteAssemblies(installedCultures);
|
||||
LoadSatelliteAssemblies();
|
||||
services.AddOqtaneServices();
|
||||
|
||||
return services;
|
||||
@@ -53,7 +175,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||
return new OqtaneSiteOptionsBuilder(services);
|
||||
}
|
||||
|
||||
internal static IServiceCollection AddOqtaneSingletonServices(this IServiceCollection services)
|
||||
public static IServiceCollection AddOqtaneSingletonServices(this IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<IInstallationManager, InstallationManager>();
|
||||
services.AddSingleton<ISyncManager, SyncManager>();
|
||||
@@ -66,7 +188,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||
return services;
|
||||
}
|
||||
|
||||
internal static IServiceCollection AddOqtaneServerScopedServices(this IServiceCollection services)
|
||||
public static IServiceCollection AddOqtaneServerScopedServices(this IServiceCollection services)
|
||||
{
|
||||
services.AddScoped<Oqtane.Shared.SiteState>();
|
||||
services.AddScoped<IInstallationService, InstallationService>();
|
||||
@@ -112,7 +234,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||
return services;
|
||||
}
|
||||
|
||||
internal static IServiceCollection AddOqtaneTransientServices(this IServiceCollection services)
|
||||
public static IServiceCollection AddOqtaneTransientServices(this IServiceCollection services)
|
||||
{
|
||||
// services
|
||||
services.AddTransient<ISiteService, ServerSiteService>();
|
||||
@@ -242,7 +364,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||
return services;
|
||||
}
|
||||
|
||||
internal static IServiceCollection AddHttpClients(this IServiceCollection services)
|
||||
public static IServiceCollection AddHttpClients(this IServiceCollection services)
|
||||
{
|
||||
if (!services.Any(x => x.ServiceType == typeof(HttpClient)))
|
||||
{
|
||||
@@ -285,9 +407,9 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||
return services;
|
||||
}
|
||||
|
||||
internal static IServiceCollection TryAddSwagger(this IServiceCollection services, bool useSwagger)
|
||||
public static IServiceCollection TryAddSwagger(this IServiceCollection services, IConfigurationRoot configuration)
|
||||
{
|
||||
if (useSwagger)
|
||||
if (configuration.GetSection("UseSwagger").Value != "false")
|
||||
{
|
||||
services.AddSwaggerGen(c =>
|
||||
{
|
||||
@@ -386,10 +508,11 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||
}
|
||||
}
|
||||
|
||||
private static void LoadSatelliteAssemblies(string[] installedCultures)
|
||||
private static void LoadSatelliteAssemblies()
|
||||
{
|
||||
AssemblyLoadContext.Default.Resolving += ResolveDependencies;
|
||||
|
||||
var installedCultures = LocalizationManager.GetSatelliteAssemblyCultures();
|
||||
foreach (var file in Directory.EnumerateFiles(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), $"*{Constants.SatelliteAssemblyExtension}", SearchOption.AllDirectories))
|
||||
{
|
||||
var code = Path.GetFileName(Path.GetDirectoryName(file));
|
||||
|
||||
@@ -31,6 +31,10 @@ namespace Oqtane.Extensions
|
||||
builder.AddSiteNamedOptions<CookieAuthenticationOptions>(Constants.AuthenticationScheme, (options, alias, sitesettings) =>
|
||||
{
|
||||
options.Cookie.Name = sitesettings.GetValue("LoginOptions:CookieName", ".AspNetCore.Identity.Application");
|
||||
if (!string.IsNullOrEmpty(sitesettings.GetValue("LoginOptions:CookieDomain", "")))
|
||||
{
|
||||
options.Cookie.Domain = sitesettings.GetValue("LoginOptions:CookieDomain", "");
|
||||
}
|
||||
string cookieExpStr = sitesettings.GetValue("LoginOptions:CookieExpiration", "");
|
||||
if (!string.IsNullOrEmpty(cookieExpStr) && TimeSpan.TryParse(cookieExpStr, out TimeSpan cookieExpTS))
|
||||
{
|
||||
@@ -61,6 +65,7 @@ namespace Oqtane.Extensions
|
||||
options.ClientId = sitesettings.GetValue("ExternalLogin:ClientId", "");
|
||||
options.ClientSecret = sitesettings.GetValue("ExternalLogin:ClientSecret", "");
|
||||
options.ResponseType = sitesettings.GetValue("ExternalLogin:AuthResponseType", "code"); // default is authorization code flow
|
||||
options.ProtocolValidator.RequireNonce = bool.Parse(sitesettings.GetValue("ExternalLogin:RequireNonce", "true"));
|
||||
options.UsePkce = bool.Parse(sitesettings.GetValue("ExternalLogin:PKCE", "false"));
|
||||
options.SaveTokens = bool.Parse(sitesettings.GetValue("ExternalLogin:SaveTokens", "false"));
|
||||
if (!string.IsNullOrEmpty(sitesettings.GetValue("ExternalLogin:RoleClaimType", "")))
|
||||
@@ -476,8 +481,26 @@ namespace Oqtane.Extensions
|
||||
else
|
||||
{
|
||||
var logins = await _identityUserManager.GetLoginsAsync(identityuser);
|
||||
var login = logins.FirstOrDefault(item => item.LoginProvider == (providerType + ":" + alias.SiteId.ToString()));
|
||||
if (login == null)
|
||||
// check if any logins exist for this user and provider type for any site
|
||||
var login = logins.FirstOrDefault(item => item.LoginProvider.StartsWith(providerType));
|
||||
if (login != null || !bool.Parse(httpContext.GetSiteSettings().GetValue("ExternalLogin:VerifyUsers", "true")))
|
||||
{
|
||||
// external login using existing user account - link automatically
|
||||
user = _users.GetUser(identityuser.UserName);
|
||||
user.SiteId = alias.SiteId;
|
||||
|
||||
var _notifications = httpContext.RequestServices.GetRequiredService<INotificationRepository>();
|
||||
string url = httpContext.Request.Scheme + "://" + alias.Name;
|
||||
string body = "You Recently Used An External Account To Sign In To Our Site.\n\n" + url + "\n\nThank You!";
|
||||
var notification = new Notification(user.SiteId, user, "User Account Notification", body);
|
||||
_notifications.AddNotification(notification);
|
||||
|
||||
// add user login
|
||||
await _identityUserManager.AddLoginAsync(identityuser, new UserLoginInfo(providerType + ":" + user.SiteId.ToString(), id, providerName));
|
||||
|
||||
_logger.Log(user.SiteId, LogLevel.Information, "ExternalLogin", Enums.LogFunction.Create, "External Login Linkage Created For User {Username} And Provider {Provider}", user.Username, providerName);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bool.Parse(httpContext.GetSiteSettings().GetValue("ExternalLogin:VerifyUsers", "true")))
|
||||
{
|
||||
@@ -496,28 +519,11 @@ namespace Oqtane.Extensions
|
||||
}
|
||||
else
|
||||
{
|
||||
// external login using existing user account - link automatically
|
||||
user = _users.GetUser(identityuser.UserName);
|
||||
user.SiteId = alias.SiteId;
|
||||
|
||||
var _notifications = httpContext.RequestServices.GetRequiredService<INotificationRepository>();
|
||||
string url = httpContext.Request.Scheme + "://" + alias.Name;
|
||||
string body = "You Recently Used An External Account To Sign In To Our Site.\n\n" + url + "\n\nThank You!";
|
||||
var notification = new Notification(user.SiteId, user, "User Account Notification", body);
|
||||
_notifications.AddNotification(notification);
|
||||
|
||||
// add user login
|
||||
await _identityUserManager.AddLoginAsync(identityuser, new UserLoginInfo(providerType + ":" + user.SiteId.ToString(), id, providerName));
|
||||
|
||||
_logger.Log(user.SiteId, LogLevel.Information, "ExternalLogin", Enums.LogFunction.Create, "External Login Linkage Created For User {Username} And Provider {Provider}", user.Username, providerName);
|
||||
// provider keys do not match
|
||||
identity.Label = ExternalLoginStatus.ProviderKeyMismatch;
|
||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Provider Key Does Not Match For User {Username}. Login Denied.", identityuser.UserName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// provider keys do not match
|
||||
identity.Label = ExternalLoginStatus.ProviderKeyMismatch;
|
||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Provider Key Does Not Match For User {Username}. Login Denied.", identityuser.UserName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -525,14 +531,34 @@ namespace Oqtane.Extensions
|
||||
if (user != null)
|
||||
{
|
||||
// manage roles
|
||||
var _roles = httpContext.RequestServices.GetRequiredService<IRoleRepository>();
|
||||
var _userRoles = httpContext.RequestServices.GetRequiredService<IUserRoleRepository>();
|
||||
var userRoles = _userRoles.GetUserRoles(user.UserId, user.SiteId).ToList();
|
||||
|
||||
// if user is signing in to a new site
|
||||
if (userRoles.Count == 0)
|
||||
{
|
||||
// add auto assigned roles to user for site
|
||||
var roles = _roles.GetRoles(user.SiteId).Where(item => item.IsAutoAssigned).ToList();
|
||||
foreach (var role in roles)
|
||||
{
|
||||
var userrole = new UserRole();
|
||||
userrole.UserId = user.UserId;
|
||||
userrole.RoleId = role.RoleId;
|
||||
userrole.EffectiveDate = null;
|
||||
userrole.ExpiryDate = null;
|
||||
userrole.IgnoreSecurityStamp = true;
|
||||
_userRoles.AddUserRole(userrole);
|
||||
}
|
||||
userRoles = _userRoles.GetUserRoles(user.UserId, user.SiteId).ToList();
|
||||
}
|
||||
|
||||
// process any role claims
|
||||
if (!string.IsNullOrEmpty(httpContext.GetSiteSettings().GetValue("ExternalLogin:RoleClaimType", "")))
|
||||
{
|
||||
// external roles
|
||||
if (claimsPrincipal.Claims.Any(item => item.Type == httpContext.GetSiteSettings().GetValue("ExternalLogin:RoleClaimType", "")))
|
||||
{
|
||||
var _roles = httpContext.RequestServices.GetRequiredService<IRoleRepository>();
|
||||
var allowhostrole = bool.Parse(httpContext.GetSiteSettings().GetValue("ExternalLogin:AllowHostRole", "false"));
|
||||
var roles = _roles.GetRoles(user.SiteId, allowhostrole).ToList();
|
||||
|
||||
|
||||
@@ -93,12 +93,21 @@ namespace Oqtane.Infrastructure
|
||||
{
|
||||
id = node.InnerText;
|
||||
}
|
||||
// get framework dependency
|
||||
node = doc.SelectSingleNode("/package/metadata/dependencies/dependency[@id='Oqtane.Framework']");
|
||||
// get minimum framework version using packageType
|
||||
node = doc.SelectSingleNode("/package/metadata/packageTypes/packageType[@name='Oqtane.Framework']");
|
||||
if (node != null)
|
||||
{
|
||||
frameworkversion = node.Attributes["version"].Value;
|
||||
}
|
||||
if (string.IsNullOrEmpty(frameworkversion))
|
||||
{
|
||||
// legacy packages used the dependency metadata
|
||||
node = doc.SelectSingleNode("/package/metadata/dependencies/dependency[@id='Oqtane.Framework']");
|
||||
if (node != null)
|
||||
{
|
||||
frameworkversion = node.Attributes["version"].Value;
|
||||
}
|
||||
}
|
||||
reader.Close();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -45,6 +45,12 @@ namespace Oqtane.Infrastructure
|
||||
}
|
||||
|
||||
public string[] GetInstalledCultures()
|
||||
{
|
||||
return GetSatelliteAssemblyCultures();
|
||||
}
|
||||
|
||||
// method is static as it is called during startup
|
||||
public static string[] GetSatelliteAssemblyCultures()
|
||||
{
|
||||
var cultures = new List<string>();
|
||||
foreach (var file in Directory.EnumerateFiles(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), $"*{Constants.SatelliteAssemblyExtension}", SearchOption.AllDirectories))
|
||||
|
||||
@@ -1,260 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Oqtane.Extensions;
|
||||
using Oqtane.Infrastructure;
|
||||
using Oqtane.Repository;
|
||||
using Oqtane.Security;
|
||||
using Oqtane.Shared;
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Oqtane.Components;
|
||||
using Oqtane.UI;
|
||||
using OqtaneSSR.Extensions;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using Oqtane.Providers;
|
||||
using Microsoft.AspNetCore.Cors.Infrastructure;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
||||
namespace Oqtane
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
private readonly bool _useSwagger;
|
||||
private readonly IWebHostEnvironment _env;
|
||||
private readonly string[] _installedCultures;
|
||||
private string _configureServicesErrors;
|
||||
private readonly IConfigurationRoot _configuration;
|
||||
private readonly IWebHostEnvironment _environment;
|
||||
|
||||
public IConfigurationRoot Configuration { get; }
|
||||
|
||||
public Startup(IWebHostEnvironment env, ILocalizationManager localizationManager)
|
||||
public Startup(IWebHostEnvironment environment)
|
||||
{
|
||||
AppDomain.CurrentDomain.SetData(Constants.DataDirectory, Path.Combine(environment.ContentRootPath, "Data"));
|
||||
|
||||
var builder = new ConfigurationBuilder()
|
||||
.SetBasePath(env.ContentRootPath)
|
||||
.SetBasePath(environment.ContentRootPath)
|
||||
.AddJsonFile("appsettings.json", false, true)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", true, true)
|
||||
.AddJsonFile($"appsettings.{environment.EnvironmentName}.json", true, true)
|
||||
.AddEnvironmentVariables();
|
||||
|
||||
Configuration = builder.Build();
|
||||
|
||||
_installedCultures = localizationManager.GetInstalledCultures();
|
||||
|
||||
//add possibility to switch off swagger on production.
|
||||
_useSwagger = Configuration.GetSection("UseSwagger").Value != "false";
|
||||
|
||||
AppDomain.CurrentDomain.SetData(Constants.DataDirectory, Path.Combine(env.ContentRootPath, "Data"));
|
||||
|
||||
_env = env;
|
||||
_configuration = builder.Build();
|
||||
_environment = environment;
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
// process forwarded headers on load balancers and proxy servers
|
||||
services.Configure<ForwardedHeadersOptions>(options =>
|
||||
{
|
||||
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
|
||||
});
|
||||
|
||||
// register localization services
|
||||
services.AddLocalization(options => options.ResourcesPath = "Resources");
|
||||
|
||||
services.AddOptions<List<Models.Database>>().Bind(Configuration.GetSection(SettingKeys.AvailableDatabasesSection));
|
||||
|
||||
// register scoped core services
|
||||
services.AddScoped<IAuthorizationHandler, PermissionHandler>()
|
||||
.AddOqtaneServerScopedServices();
|
||||
|
||||
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||
|
||||
// setup HttpClient for server side in a client side compatible fashion ( with auth cookie )
|
||||
services.AddHttpClients();
|
||||
|
||||
// register singleton scoped core services
|
||||
services.AddSingleton(Configuration)
|
||||
.AddOqtaneSingletonServices();
|
||||
|
||||
// install any modules or themes ( this needs to occur BEFORE the assemblies are loaded into the app domain )
|
||||
_configureServicesErrors += InstallationManager.InstallPackages(_env.WebRootPath, _env.ContentRootPath);
|
||||
|
||||
// register transient scoped core services
|
||||
services.AddOqtaneTransientServices();
|
||||
|
||||
// load the external assemblies into the app domain, install services
|
||||
services.AddOqtane(_installedCultures);
|
||||
services.AddOqtaneDbContext();
|
||||
|
||||
services.AddAntiforgery(options =>
|
||||
{
|
||||
options.HeaderName = Constants.AntiForgeryTokenHeaderName;
|
||||
options.Cookie.Name = Constants.AntiForgeryTokenCookieName;
|
||||
options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Strict;
|
||||
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
|
||||
options.Cookie.HttpOnly = true;
|
||||
});
|
||||
|
||||
services.AddIdentityCore<IdentityUser>(options => { })
|
||||
.AddEntityFrameworkStores<TenantDBContext>()
|
||||
.AddSignInManager()
|
||||
.AddDefaultTokenProviders()
|
||||
.AddClaimsPrincipalFactory<ClaimsPrincipalFactory<IdentityUser>>(); // role claims
|
||||
|
||||
services.ConfigureOqtaneIdentityOptions(Configuration);
|
||||
|
||||
services.AddCascadingAuthenticationState();
|
||||
services.AddScoped<AuthenticationStateProvider, IdentityRevalidatingAuthenticationStateProvider>();
|
||||
services.AddAuthorization();
|
||||
|
||||
services.AddAuthentication(options =>
|
||||
{
|
||||
options.DefaultScheme = Constants.AuthenticationScheme;
|
||||
})
|
||||
.AddCookie(Constants.AuthenticationScheme)
|
||||
.AddOpenIdConnect(AuthenticationProviderTypes.OpenIDConnect, options => { })
|
||||
.AddOAuth(AuthenticationProviderTypes.OAuth2, options => { });
|
||||
|
||||
services.ConfigureOqtaneCookieOptions();
|
||||
services.ConfigureOqtaneAuthenticationOptions(Configuration);
|
||||
|
||||
services.AddOqtaneSiteOptions()
|
||||
.WithSiteIdentity()
|
||||
.WithSiteAuthentication();
|
||||
|
||||
services.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy(Constants.MauiCorsPolicy,
|
||||
policy =>
|
||||
{
|
||||
// allow .NET MAUI client cross origin calls
|
||||
policy.WithOrigins("https://0.0.0.1", "http://0.0.0.1", "app://0.0.0.1")
|
||||
.AllowAnyHeader().AllowAnyMethod().AllowCredentials();
|
||||
});
|
||||
});
|
||||
|
||||
services.AddOutputCache();
|
||||
|
||||
services.AddMvc(options =>
|
||||
{
|
||||
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
|
||||
})
|
||||
.AddOqtaneApplicationParts() // register any Controllers from custom modules
|
||||
.ConfigureOqtaneMvc(); // any additional configuration from IStartup classes
|
||||
|
||||
services.AddRazorPages();
|
||||
|
||||
services.AddRazorComponents()
|
||||
.AddInteractiveServerComponents(options =>
|
||||
{
|
||||
if (_env.IsDevelopment())
|
||||
{
|
||||
options.DetailedErrors = true;
|
||||
}
|
||||
}).AddHubOptions(options =>
|
||||
{
|
||||
options.MaximumReceiveMessageSize = null; // no limit (for large amounts of data ie. textarea components)
|
||||
})
|
||||
.AddInteractiveWebAssemblyComponents();
|
||||
|
||||
services.AddSwaggerGen(options =>
|
||||
{
|
||||
options.CustomSchemaIds(type => type.ToString()); // Handle SchemaId already used for different type
|
||||
});
|
||||
services.TryAddSwagger(_useSwagger);
|
||||
services.AddOqtane(_configuration, _environment);
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ISyncManager sync, ICorsService corsService, ICorsPolicyProvider corsPolicyProvider, ILogger<Startup> logger)
|
||||
public void Configure(IApplicationBuilder app, IConfigurationRoot configuration, IWebHostEnvironment environment, ICorsService corsService, ICorsPolicyProvider corsPolicyProvider, ISyncManager sync)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_configureServicesErrors))
|
||||
{
|
||||
logger.LogError(_configureServicesErrors);
|
||||
}
|
||||
|
||||
ServiceActivator.Configure(app.ApplicationServices);
|
||||
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseWebAssemblyDebugging();
|
||||
app.UseForwardedHeaders();
|
||||
}
|
||||
else
|
||||
{
|
||||
app.UseForwardedHeaders();
|
||||
app.UseExceptionHandler("/Error", createScopeForErrors: true);
|
||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
||||
app.UseHsts();
|
||||
}
|
||||
|
||||
// allow oqtane localization middleware
|
||||
app.UseOqtaneLocalization();
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
app.UseStaticFiles(new StaticFileOptions
|
||||
{
|
||||
OnPrepareResponse = (ctx) =>
|
||||
{
|
||||
// static asset caching
|
||||
var cachecontrol = Configuration.GetSection("CacheControl");
|
||||
if (!string.IsNullOrEmpty(cachecontrol.Value))
|
||||
{
|
||||
ctx.Context.Response.Headers.Append(HeaderNames.CacheControl, cachecontrol.Value);
|
||||
}
|
||||
// CORS headers for .NET MAUI clients
|
||||
var policy = corsPolicyProvider.GetPolicyAsync(ctx.Context, Constants.MauiCorsPolicy)
|
||||
.ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
corsService.ApplyResult(corsService.EvaluatePolicy(ctx.Context, policy), ctx.Context.Response);
|
||||
}
|
||||
});
|
||||
app.UseExceptionMiddleWare();
|
||||
app.UseTenantResolution();
|
||||
app.UseJwtAuthorization();
|
||||
app.UseRouting();
|
||||
app.UseCors();
|
||||
app.UseOutputCache();
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
app.UseAntiforgery();
|
||||
|
||||
// execute any IServerStartup logic
|
||||
app.ConfigureOqtaneAssemblies(env);
|
||||
|
||||
if (_useSwagger)
|
||||
{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/" + Constants.Version + "/swagger.json", Constants.PackageId + " " + Constants.Version); });
|
||||
}
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapControllers();
|
||||
endpoints.MapRazorPages();
|
||||
});
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapRazorComponents<App>()
|
||||
.AddInteractiveServerRenderMode()
|
||||
.AddInteractiveWebAssemblyRenderMode()
|
||||
.AddAdditionalAssemblies(typeof(SiteRouter).Assembly);
|
||||
});
|
||||
|
||||
// simulate the fallback routing approach of traditional Blazor - allowing the custom SiteRouter to handle all routing concerns
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapFallback();
|
||||
});
|
||||
|
||||
// create a global sync event to identify server application startup
|
||||
sync.AddSyncEvent(-1, -1, EntityNames.Host, -1, SyncEventActions.Reload);
|
||||
app.UseOqtane(configuration, environment, corsService, corsPolicyProvider, sync);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"RenderMode": "Interactive",
|
||||
"RenderMode": "Static",
|
||||
"Runtime": "Server",
|
||||
"Database": {
|
||||
"DefaultDBType": ""
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"RenderMode": "Interactive",
|
||||
"RenderMode": "Static",
|
||||
"Runtime": "Server",
|
||||
"Database": {
|
||||
"DefaultDBType": ""
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
public override List<Resource> Resources => new List<Resource>()
|
||||
{
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" }
|
||||
new Stylesheet("_content/[Owner].Module.[Module]/Module.css")
|
||||
};
|
||||
|
||||
private ElementReference form;
|
||||
|
||||
@@ -42,8 +42,8 @@ else
|
||||
|
||||
public override List<Resource> Resources => new List<Resource>()
|
||||
{
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" },
|
||||
new Resource { ResourceType = ResourceType.Script, Url = ModulePath() + "Module.js" }
|
||||
new Stylesheet("_content/[Owner].Module.[Module]/Module.css"),
|
||||
new Script("_content/[Owner].Module.[Module]/Module.js")
|
||||
};
|
||||
|
||||
List<[Module]> _[Module]s;
|
||||
|
||||
@@ -8,7 +8,10 @@ namespace [Owner].Module.[Module].Startup
|
||||
{
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddScoped<I[Module]Service, [Module]Service>();
|
||||
if (!services.Any(s => s.ServiceType == typeof(I[Module]Service)))
|
||||
{
|
||||
services.AddScoped<I[Module]Service, [Module]Service>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
<tags>oqtane module</tags>
|
||||
<releaseNotes></releaseNotes>
|
||||
<summary></summary>
|
||||
<dependencies>
|
||||
<dependency id="Oqtane.Framework" version="[FrameworkVersion]" />
|
||||
</dependencies>
|
||||
<packageTypes>
|
||||
<packageType name="Dependency" />
|
||||
<packageType name="Oqtane.Framework" version="[FrameworkVersion]" />
|
||||
</packageTypes>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="..\Client\bin\Release\$targetframework$\$ProjectName$.Client.Oqtane.dll" target="lib\$targetframework$" />
|
||||
@@ -26,7 +27,12 @@
|
||||
<file src="..\Server\bin\Release\$targetframework$\$ProjectName$.Server.Oqtane.pdb" target="lib\$targetframework$" />
|
||||
<file src="..\Shared\bin\Release\$targetframework$\$ProjectName$.Shared.Oqtane.dll" target="lib\$targetframework$" />
|
||||
<file src="..\Shared\bin\Release\$targetframework$\$ProjectName$.Shared.Oqtane.pdb" target="lib\$targetframework$" />
|
||||
<file src="..\Server\wwwroot\**\*.*" target="wwwroot" />
|
||||
<file src="..\Server\obj\Release\net9.0\staticwebassets\msbuild.$ProjectName$.Microsoft.AspNetCore.StaticWebAssetEndpoints.props" target="build\Microsoft.AspNetCore.StaticWebAssetEndpoints.props" />
|
||||
<file src="..\Server\obj\Release\net9.0\staticwebassets\msbuild.$ProjectName$.Microsoft.AspNetCore.StaticWebAssets.props" target="build\Microsoft.AspNetCore.StaticWebAssets.props" />
|
||||
<file src="..\Server\obj\Release\net9.0\staticwebassets\msbuild.build.$ProjectName$.props" target="build\$ProjectName$.props" />
|
||||
<file src="..\Server\obj\Release\net9.0\staticwebassets\msbuild.buildMultiTargeting.$ProjectName$.props" target="buildMultiTargeting\$ProjectName$.props" />
|
||||
<file src="..\Server\obj\Release\net9.0\staticwebassets\msbuild.buildTransitive.$ProjectName$.props" target="buildTransitive\$ProjectName$.props" />
|
||||
<file src="..\Server\wwwroot\**\*.*" target="staticwebassets" />
|
||||
<file src="icon.png" target="" />
|
||||
</files>
|
||||
</package>
|
||||
@@ -8,4 +8,4 @@ XCOPY "..\Server\bin\Debug\%TargetFramework%\%ProjectName%.Server.Oqtane.dll" ".
|
||||
XCOPY "..\Server\bin\Debug\%TargetFramework%\%ProjectName%.Server.Oqtane.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\%TargetFramework%\" /Y
|
||||
XCOPY "..\Shared\bin\Debug\%TargetFramework%\%ProjectName%.Shared.Oqtane.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\%TargetFramework%\" /Y
|
||||
XCOPY "..\Shared\bin\Debug\%TargetFramework%\%ProjectName%.Shared.Oqtane.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\%TargetFramework%\" /Y
|
||||
XCOPY "..\Server\wwwroot\*" "..\..\[RootFolder]\Oqtane.Server\wwwroot\" /Y /S /I
|
||||
XCOPY "..\Server\wwwroot\*" "..\..\[RootFolder]\Oqtane.Server\wwwroot\_content\%ProjectName%\" /Y /S /I
|
||||
@@ -9,4 +9,4 @@ cp -f "../Server/bin/Debug/$TargetFramework/$ProjectName$.Server.Oqtane.dll" "..
|
||||
cp -f "../Server/bin/Debug/$TargetFramework/$ProjectName$.Server.Oqtane.pdb" "../../[RootFolder]/Oqtane.Server/bin/Debug/$TargetFramework/"
|
||||
cp -f "../Shared/bin/Debug/$TargetFramework/$ProjectName$.Shared.Oqtane.dll" "../../[RootFolder]/Oqtane.Server/bin/Debug/$TargetFramework/"
|
||||
cp -f "../Shared/bin/Debug/$TargetFramework/$ProjectName$.Shared.Oqtane.pdb" "../../[RootFolder]/Oqtane.Server/bin/Debug/$TargetFramework/"
|
||||
cp -rf "../Server/wwwroot/"* "../../[RootFolder]/Oqtane.Server/wwwroot/"
|
||||
cp -rf "../Server/wwwroot/"* "../../[RootFolder]/Oqtane.Server/wwwroot/_content/%ProjectName%/"
|
||||
|
||||
@@ -3,5 +3,6 @@ set TargetFramework=%1
|
||||
set ProjectName=%2
|
||||
|
||||
del "*.nupkg"
|
||||
"..\..\oqtane.framework\oqtane.package\FixProps.exe"
|
||||
"..\..\[RootFolder]\oqtane.package\nuget.exe" pack %ProjectName%.nuspec -Properties targetframework=%TargetFramework%;projectname=%ProjectName%
|
||||
XCOPY "*.nupkg" "..\..\[RootFolder]\Oqtane.Server\Packages\" /Y
|
||||
@@ -1,5 +1,7 @@
|
||||
TargetFramework=$1
|
||||
ProjectName=$2
|
||||
|
||||
find . -name "*.nupkg" -delete
|
||||
"..\..\oqtane.framework\oqtane.package\FixProps.exe"
|
||||
"..\..\[RootFolder]\oqtane.package\nuget.exe" pack %ProjectName%.nuspec -Properties targetframework=%TargetFramework%;projectname=%ProjectName%
|
||||
cp -f "*.nupkg" "..\..\[RootFolder]\Oqtane.Server\Packages\"
|
||||
@@ -1,11 +0,0 @@
|
||||
The _content folder should only contain static resources from shared razor component libraries (RCLs). Static resources can be extracted from shared RCL Nuget packages by executing a Publish task on the module's Server project to a local folder and copying the files from the _content folder which is created. Each shared RCL would have its own appropriately named subfolder within the module's _content folder.
|
||||
|
||||
ie.
|
||||
|
||||
/_content
|
||||
/Radzen.Blazor
|
||||
/css
|
||||
/fonts
|
||||
/syncfusion.blazor
|
||||
/scripts
|
||||
/styles
|
||||
@@ -16,11 +16,10 @@ namespace [Owner].Theme.[Theme]
|
||||
ContainerSettingsType = "[Owner].Theme.[Theme].ContainerSettings, [Owner].Theme.[Theme].Client.Oqtane",
|
||||
Resources = new List<Resource>()
|
||||
{
|
||||
// obtained from https://cdnjs.com/libraries
|
||||
new StyleSheet(Constants.BootstrapStylesheetUrl, Constants.BootstrapStylesheetIntegrity, "anonymous"),
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "~/Theme.css" },
|
||||
// obtained from https://cdnjs.com/libraries
|
||||
new Stylesheet(Constants.BootstrapStylesheetUrl, Constants.BootstrapStylesheetIntegrity, "anonymous"),
|
||||
new Stylesheet("_content/[Owner].Theme.[Theme]/Theme.css"),
|
||||
new Script(Constants.BootstrapScriptUrl, Constants.BootstrapScriptIntegrity, "anonymous")
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
/* Oqtane Styles */
|
||||
|
||||
body {
|
||||
padding-top: 7rem;
|
||||
}
|
||||
|
||||
/* App Logo */
|
||||
.app-logo .img-fluid {
|
||||
max-height: 90px;
|
||||
padding: 0 5px 0 5px;
|
||||
}
|
||||
|
||||
.table > :not(caption) > * > * {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.table .form-control {
|
||||
background-color: #ffffff !important;
|
||||
border-width: 0.5px !important;
|
||||
border-bottom-color: #ccc !important;
|
||||
}
|
||||
|
||||
.table .form-select {
|
||||
background-color: #ffffff !important;
|
||||
border-width: 0.5px !important;
|
||||
border-bottom-color: #ccc !important;
|
||||
}
|
||||
|
||||
.table .btn-primary {
|
||||
background-color: var(--bs-primary);
|
||||
}
|
||||
|
||||
.table .btn-secondary {
|
||||
background-color: var(--bs-secondary);
|
||||
}
|
||||
|
||||
.alert-dismissible .btn-close {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.controls {
|
||||
z-index: 2000;
|
||||
padding-top: 15px;
|
||||
padding-bottom: 15px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.app-menu .nav-item {
|
||||
font-size: 0.9rem;
|
||||
padding-bottom: 0.5rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.app-menu .nav-item a {
|
||||
border-radius: 4px;
|
||||
height: 3rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
line-height: 3rem;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
.app-menu .nav-item a.active {
|
||||
background-color: rgba(255,255,255,0.25);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.app-menu .nav-item a:hover {
|
||||
background-color: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.app-menu .nav-link .oi {
|
||||
width: 1.5rem;
|
||||
font-size: 1.1rem;
|
||||
vertical-align: text-top;
|
||||
top: -2px;
|
||||
}
|
||||
|
||||
.navbar-toggler {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
margin: .5rem;
|
||||
}
|
||||
|
||||
div.app-moduleactions a.dropdown-toggle, div.app-moduleactions div.dropdown-menu {
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.dropdown-menu span {
|
||||
mix-blend-mode: difference;
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
|
||||
.app-menu {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
position: fixed;
|
||||
top: 60px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.controls {
|
||||
height: 60px;
|
||||
top: 15px;
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
width: 100%;
|
||||
background-color: rgb(0, 0, 0);
|
||||
}
|
||||
|
||||
.controls-group {
|
||||
float: right;
|
||||
margin-right: 25px;
|
||||
}
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
top: 60px;
|
||||
}
|
||||
}
|
||||
@@ -15,14 +15,20 @@
|
||||
<tags>oqtane theme</tags>
|
||||
<releaseNotes></releaseNotes>
|
||||
<summary></summary>
|
||||
<dependencies>
|
||||
<dependency id="Oqtane.Framework" version="[FrameworkVersion]" />
|
||||
</dependencies>
|
||||
<packageTypes>
|
||||
<packageType name="Dependency" />
|
||||
<packageType name="Oqtane.Framework" version="[FrameworkVersion]" />
|
||||
</packageTypes>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="..\Client\bin\Release\$targetframework$\$projectname$.Client.Oqtane.dll" target="lib\$targetframework$" />
|
||||
<file src="..\Client\bin\Release\$targetframework$\$projectname$.Client.Oqtane.pdb" target="lib\$targetframework$" />
|
||||
<file src="..\Client\wwwroot\**\*.*" target="wwwroot" />
|
||||
<file src="..\Client\obj\Release\net9.0\staticwebassets\msbuild.$ProjectName$.Microsoft.AspNetCore.StaticWebAssetEndpoints.props" target="build\Microsoft.AspNetCore.StaticWebAssetEndpoints.props" />
|
||||
<file src="..\Client\obj\Release\net9.0\staticwebassets\msbuild.$ProjectName$.Microsoft.AspNetCore.StaticWebAssets.props" target="build\Microsoft.AspNetCore.StaticWebAssets.props" />
|
||||
<file src="..\Client\obj\Release\net9.0\staticwebassets\msbuild.build.$ProjectName$.props" target="build\$ProjectName$.props" />
|
||||
<file src="..\Client\obj\Release\net9.0\staticwebassets\msbuild.buildMultiTargeting.$ProjectName$.props" target="buildMultiTargeting\$ProjectName$.props" />
|
||||
<file src="..\Client\obj\Release\net9.0\staticwebassets\msbuild.buildTransitive.$ProjectName$.props" target="buildTransitive\$ProjectName$.props" />
|
||||
<file src="..\Client\wwwroot\**\*.*" target="staticwebassets" />
|
||||
<file src="icon.png" target="" />
|
||||
</files>
|
||||
</package>
|
||||
@@ -4,4 +4,4 @@ set ProjectName=%2
|
||||
|
||||
XCOPY "..\Client\bin\Debug\%TargetFramework%\%ProjectName%.Client.Oqtane.dll" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\%TargetFramework%\" /Y
|
||||
XCOPY "..\Client\bin\Debug\%TargetFramework%\%ProjectName%.Client.Oqtane.pdb" "..\..\[RootFolder]\Oqtane.Server\bin\Debug\%TargetFramework%\" /Y
|
||||
XCOPY "..\Client\wwwroot\*" "..\..\[RootFolder]\Oqtane.Server\wwwroot\" /Y /S /I
|
||||
XCOPY "..\Client\wwwroot\*" "..\..\oqtane.framework\Oqtane.Server\wwwroot\_content\%ProjectName%\" /Y /S /I
|
||||
@@ -5,4 +5,4 @@ ProjectName=$2
|
||||
|
||||
cp -f "../Client/bin/Debug/$TargetFramework/$ProjectName$.Client.Oqtane.dll" "../../[RootFolder]/Oqtane.Server/bin/Debug/$TargetFramework/"
|
||||
cp -f "../Client/bin/Debug/$TargetFramework/$ProjectName$.Client.Oqtane.pdb" "../../[RootFolder]/Oqtane.Server/bin/Debug/$TargetFramework/"
|
||||
cp -rf "../Server/wwwroot/"* "../../[RootFolder]/Oqtane.Server/wwwroot/"
|
||||
cp -rf "../Client/wwwroot/"* "../../[RootFolder]/Oqtane.Server/wwwroot/_content/%ProjectName%/"
|
||||