Merge remote-tracking branch 'oqtane/dev' into dev
This commit is contained in:
commit
97026746f8
@ -119,7 +119,13 @@
|
||||
<div class="row">
|
||||
<div class="mx-auto text-center">
|
||||
<button type="button" class="btn btn-success" @onclick="Install">@Localizer["InstallNow"]</button><br /><br />
|
||||
<ModuleMessage Message="@_message" Type="MessageType.Error"></ModuleMessage>
|
||||
@if (!string.IsNullOrEmpty(_message))
|
||||
{
|
||||
<div class="alert alert-danger alert-dismissible fade show mb-3" role="alert">
|
||||
@((MarkupString)_message)
|
||||
<button type="button" class="btn-close" aria-label="Close" @onclick="DismissModal"></button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="app-progress-indicator" style="@_loadingDisplay"></div>
|
||||
</div>
|
||||
@ -316,4 +322,9 @@
|
||||
_showConnectionString = !_showConnectionString;
|
||||
}
|
||||
|
||||
private void DismissModal()
|
||||
{
|
||||
_message = "";
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
@ -19,12 +19,7 @@ namespace Oqtane.Themes.Controls
|
||||
{
|
||||
if (page.IsClickable)
|
||||
{
|
||||
var url = string.IsNullOrEmpty(page.Url) ? NavigateUrl(page.Path) : page.Url;
|
||||
if (PageState.QueryString.ContainsKey("method"))
|
||||
{
|
||||
url += ((url.Contains("?")) ? "&" : "?") + "method=" + PageState.QueryString["method"];
|
||||
}
|
||||
return url;
|
||||
return string.IsNullOrEmpty(page.Url) ? NavigateUrl(page.Path) : page.Url;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -42,16 +42,6 @@
|
||||
}
|
||||
headcontent += $"<link id=\"app-favicon\" rel=\"shortcut icon\" type=\"image/{favicontype}\" href=\"{favicon}\" />\n";
|
||||
|
||||
if (PageState.QueryString.ContainsKey("method") && PageState.QueryString["method"] == "new")
|
||||
{
|
||||
// stylesheets
|
||||
foreach (Resource resource in PageState.Page.Resources.Where(item => item.ResourceType == ResourceType.Stylesheet))
|
||||
{
|
||||
var url = (resource.Url.Contains("://")) ? resource.Url : PageState.Alias.BaseUrl + resource.Url;
|
||||
headcontent += "<link rel=\"stylesheet\" href=\"" + url + "\"" + (!string.IsNullOrEmpty(resource.Integrity) ? " integrity=\"" + resource.Integrity + "\"" : "") + (!string.IsNullOrEmpty(resource.CrossOrigin) ? " crossorigin=\"" + resource.CrossOrigin + "\"" : "") + " type=\"text/css\"/>" + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// head content
|
||||
AddHeadContent(headcontent, PageState.Site.HeadContent);
|
||||
if (!string.IsNullOrEmpty(PageState.Site.HeadContent))
|
||||
@ -106,27 +96,24 @@
|
||||
}
|
||||
}
|
||||
|
||||
if (!PageState.QueryString.ContainsKey("method") || (PageState.QueryString.ContainsKey("method") && PageState.QueryString["method"] == "old"))
|
||||
// style sheets
|
||||
if (PageState.Page.Resources != null && PageState.Page.Resources.Exists(item => item.ResourceType == ResourceType.Stylesheet))
|
||||
{
|
||||
// style sheets
|
||||
if (PageState.Page.Resources != null && PageState.Page.Resources.Exists(item => item.ResourceType == ResourceType.Stylesheet))
|
||||
var interop = new Interop(JSRuntime);
|
||||
string batch = DateTime.UtcNow.ToString("yyyyMMddHHmmssfff");
|
||||
var links = new List<object>();
|
||||
foreach (Resource resource in PageState.Page.Resources.Where(item => item.ResourceType == ResourceType.Stylesheet))
|
||||
{
|
||||
var interop = new Interop(JSRuntime);
|
||||
string batch = DateTime.UtcNow.ToString("yyyyMMddHHmmssfff");
|
||||
var links = new List<object>();
|
||||
foreach (Resource resource in PageState.Page.Resources.Where(item => item.ResourceType == ResourceType.Stylesheet))
|
||||
{
|
||||
var prefix = "app-stylesheet-" + resource.Level.ToString().ToLower();
|
||||
var url = (resource.Url.Contains("://")) ? resource.Url : PageState.Alias.BaseUrl + resource.Url;
|
||||
links.Add(new { id = prefix + "-" + batch + "-" + (links.Count + 1).ToString("00"), rel = "stylesheet", href = url, type = "text/css", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", insertbefore = prefix });
|
||||
}
|
||||
if (links.Any())
|
||||
{
|
||||
await interop.IncludeLinks(links.ToArray());
|
||||
}
|
||||
await interop.RemoveElementsById("app-stylesheet-page-", "", "app-stylesheet-page-" + batch + "-00");
|
||||
await interop.RemoveElementsById("app-stylesheet-module-", "", "app-stylesheet-module-" + batch + "-00");
|
||||
var prefix = "app-stylesheet-" + resource.Level.ToString().ToLower();
|
||||
var url = (resource.Url.Contains("://")) ? resource.Url : PageState.Alias.BaseUrl + resource.Url;
|
||||
links.Add(new { id = prefix + "-" + batch + "-" + (links.Count + 1).ToString("00"), rel = "stylesheet", href = url, type = "text/css", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", insertbefore = prefix });
|
||||
}
|
||||
if (links.Any())
|
||||
{
|
||||
await interop.IncludeLinks(links.ToArray());
|
||||
}
|
||||
await interop.RemoveElementsById("app-stylesheet-page-", "", "app-stylesheet-page-" + batch + "-00");
|
||||
await interop.RemoveElementsById("app-stylesheet-module-", "", "app-stylesheet-module-" + batch + "-00");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<Resource> Resources { get; } // identifies global resources for an application
|
||||
}
|
||||
}
|
||||
|
@ -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<HostedServiceBase> _filelogger = scope.ServiceProvider.GetRequiredService<ILogger<HostedServiceBase>>();
|
||||
|
||||
try
|
||||
{
|
||||
var jobs = scope.ServiceProvider.GetRequiredService<IJobRepository>();
|
||||
var jobLogs = scope.ServiceProvider.GetRequiredService<IJobLogRepository>();
|
||||
var tenantRepository = scope.ServiceProvider.GetRequiredService<ITenantRepository>();
|
||||
var tenantManager = scope.ServiceProvider.GetRequiredService<ITenantManager>();
|
||||
|
||||
// get name of job
|
||||
string jobType = Utilities.GetFullTypeName(GetType().AssemblyQualifiedName);
|
||||
|
||||
// load jobs and find current job
|
||||
IJobRepository jobs = scope.ServiceProvider.GetRequiredService<IJobRepository>();
|
||||
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<IJobLogRepository>();
|
||||
// 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<ITenantRepository>();
|
||||
var tenantManager = scope.ServiceProvider.GetRequiredService<ITenantManager>();
|
||||
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<HostedServiceBase> _filelogger = scope.ServiceProvider.GetRequiredService<ILogger<HostedServiceBase>>();
|
||||
|
||||
try
|
||||
{
|
||||
string jobTypeName = Utilities.GetFullTypeName(GetType().AssemblyQualifiedName);
|
||||
IJobRepository jobs = scope.ServiceProvider.GetRequiredService<IJobRepository>();
|
||||
@ -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<HostedServiceBase> _filelogger = scope.ServiceProvider.GetRequiredService<ILogger<HostedServiceBase>>();
|
||||
|
||||
try
|
||||
{
|
||||
string jobTypeName = Utilities.GetFullTypeName(GetType().AssemblyQualifiedName);
|
||||
IJobRepository jobs = scope.ServiceProvider.GetRequiredService<IJobRepository>();
|
||||
@ -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
|
||||
|
@ -57,10 +57,10 @@ namespace Oqtane.SiteTemplates
|
||||
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
||||
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
||||
},
|
||||
Content = "<p><a href=\"https://www.oqtane.org\" target=\"_new\">Oqtane</a> is an open source <b>CMS</b> and <b>Application Framework</b> that provides advanced functionality for developing web, mobile, and desktop applications on .NET Core. It leverages the Blazor component model to compose a <b>fully dynamic</b> web development experience which can be hosted either client-side or server-side. Whether you are looking for a platform to <b>accelerate your web development</b> 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.</p>" +
|
||||
"<p align=\"center\"><a href=\"https://www.oqtane.org\" target=\"_new\"><img class=\"img-fluid\" src=\"oqtane-glow.png\"></a></p><p align=\"center\"><a class=\"btn btn-primary\" href=\"https://www.oqtane.org/Community\" target=\"_new\">Join Our Community</a> <a class=\"btn btn-primary\" href=\"https://github.com/oqtane/oqtane.framework\" target=\"_new\">Clone Our Repo</a></p>" +
|
||||
"<p><a href=\"https://dotnet.microsoft.com/apps/aspnet/web-apps/blazor\" target=\"_new\">Blazor</a> 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.</p>" +
|
||||
"<p>Blazor is a feature of <a href=\"https://dotnet.microsoft.com/apps/aspnet\" target=\"_new\">.NET Core</a>, the popular cross platform web development framework from Microsoft that extends the <a href=\"https://dotnet.microsoft.com/learn/dotnet/what-is-dotnet\" target=\"_new\" >.NET developer platform</a> with tools and libraries for building web apps.</p>"
|
||||
Content = "<p><a href=\"https://www.oqtane.org\" target=\"_new\">Oqtane</a> is an open source <b>CMS</b> and <b>Application Framework</b> that provides advanced functionality for developing web, mobile, and desktop applications on .NET. It leverages Blazor to compose a <b>fully dynamic</b> digital experience. Whether you are looking for a platform to <b>accelerate your web development</b> 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.</p>" +
|
||||
"<p align=\"center\"><a href=\"https://www.oqtane.org\" target=\"_new\"><img class=\"img-fluid\" src=\"oqtane-glow.png\"></a></p><p align=\"center\"><a class=\"btn btn-primary\" href=\"https://www.oqtane.org\" target=\"_new\">Join Our Community</a> <a class=\"btn btn-primary\" href=\"https://github.com/oqtane/oqtane.framework\" target=\"_new\">Clone Our Repo</a></p>" +
|
||||
"<p><a href=\"https://dotnet.microsoft.com/apps/aspnet/web-apps/blazor\" target=\"_new\">Blazor</a> 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.</p>" +
|
||||
"<p>Blazor is a feature of <a href=\"https://dotnet.microsoft.com/apps/aspnet\" target=\"_new\">ASP.NET</a>, the popular cross platform development framework from Microsoft that provides powerful tools and libraries for building modern software applications.</p>"
|
||||
},
|
||||
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "MIT License", Pane = PaneNames.Default,
|
||||
PermissionList = new List<Permission> {
|
||||
|
@ -167,29 +167,26 @@ namespace Oqtane.Pages
|
||||
}
|
||||
|
||||
// stylesheets
|
||||
if (!HttpContext.Request.Query.ContainsKey("method") || (HttpContext.Request.Query.ContainsKey("method") && HttpContext.Request.Query["method"] == "old"))
|
||||
var resources = new List<Resource>();
|
||||
if (string.IsNullOrEmpty(page.ThemeType))
|
||||
{
|
||||
var resources = new List<Resource>();
|
||||
if (string.IsNullOrEmpty(page.ThemeType))
|
||||
{
|
||||
page.ThemeType = site.DefaultThemeType;
|
||||
}
|
||||
var theme = site.Themes.FirstOrDefault(item => item.Themes.Any(item => item.TypeName == page.ThemeType));
|
||||
if (theme?.Resources != null)
|
||||
{
|
||||
resources.AddRange(theme.Resources.Where(item => item.ResourceType == ResourceType.Stylesheet).ToList());
|
||||
}
|
||||
var type = Type.GetType(page.ThemeType);
|
||||
if (type != null)
|
||||
{
|
||||
var obj = Activator.CreateInstance(type) as IThemeControl;
|
||||
if (obj?.Resources != null)
|
||||
{
|
||||
resources.AddRange(obj.Resources.Where(item => item.ResourceType == ResourceType.Stylesheet).ToList());
|
||||
}
|
||||
}
|
||||
ManageStyleSheets(resources, alias, theme.ThemeName);
|
||||
page.ThemeType = site.DefaultThemeType;
|
||||
}
|
||||
var theme = site.Themes.FirstOrDefault(item => item.Themes.Any(item => item.TypeName == page.ThemeType));
|
||||
if (theme?.Resources != null)
|
||||
{
|
||||
resources.AddRange(theme.Resources.Where(item => item.ResourceType == ResourceType.Stylesheet).ToList());
|
||||
}
|
||||
var type = Type.GetType(page.ThemeType);
|
||||
if (type != null)
|
||||
{
|
||||
var obj = Activator.CreateInstance(type) as IThemeControl;
|
||||
if (obj?.Resources != null)
|
||||
{
|
||||
resources.AddRange(obj.Resources.Where(item => item.ResourceType == ResourceType.Stylesheet).ToList());
|
||||
}
|
||||
}
|
||||
ManageStyleSheets(resources, alias, theme.ThemeName);
|
||||
|
||||
// scripts
|
||||
if (Runtime == "Server")
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||

|
||||
|
||||
Oqtane is a CMS and Application Framework. It leverages Blazor, an open source and cross-platform web UI framework for building single-page apps using .NET and C# instead of JavaScript. Blazor apps are composed of reusable web UI components implemented using C#, HTML, and CSS. Both client and server code is written in C#, allowing you to share code and libraries.
|
||||
Oqtane is a CMS and Application Framework. It leverages Blazor, an open source and cross-platform web UI framework for building modern apps using .NET and C# instead of JavaScript. Blazor apps are composed of reusable web UI components implemented using C#, HTML, and CSS. Both client and server code is written in C#, allowing you to share code and libraries.
|
||||
|
||||
Oqtane is being developed based on some fundamental principles which are outlined in the [Oqtane Philosophy](https://www.oqtane.org/blog/!/20/oqtane-philosophy).
|
||||
|
||||
|
Reference in New Issue
Block a user