diff --git a/Directory.Build.props b/Directory.Build.props index d979cd70..72d3c1d5 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,7 +2,7 @@ net10.0 Debug;Release - 10.0.3 + 10.0.4 Oqtane Shaun Walker .NET Foundation @@ -10,7 +10,7 @@ .NET Foundation https://www.oqtane.org https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE - https://github.com/oqtane/oqtane.framework/releases/tag/v10.0.3 + https://github.com/oqtane/oqtane.framework/releases/tag/v10.0.4 https://github.com/oqtane/oqtane.framework Git diff --git a/LICENSE b/LICENSE index a8125f20..b6c6f1c8 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018-2025 .NET Foundation +Copyright (c) 2018-2026 .NET Foundation Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Oqtane.Application/Client/Oqtane.Application.Client.csproj b/Oqtane.Application/Client/Oqtane.Application.Client.csproj index f37eac86..69df010a 100644 --- a/Oqtane.Application/Client/Oqtane.Application.Client.csproj +++ b/Oqtane.Application/Client/Oqtane.Application.Client.csproj @@ -12,10 +12,10 @@ - - - - + + + + @@ -23,7 +23,7 @@ - + diff --git a/Oqtane.Application/Oqtane.Application.Template.nuspec b/Oqtane.Application/Oqtane.Application.Template.nuspec index 3a66ba87..e6c2bdb6 100644 --- a/Oqtane.Application/Oqtane.Application.Template.nuspec +++ b/Oqtane.Application/Oqtane.Application.Template.nuspec @@ -2,7 +2,7 @@ Oqtane.Application.Template - 10.0.3 + 10.0.4 Oqtane Application Template For Blazor Shaun Walker false diff --git a/Oqtane.Application/Server/Oqtane.Application.Server.csproj b/Oqtane.Application/Server/Oqtane.Application.Server.csproj index 5719d905..9aed8cfc 100644 --- a/Oqtane.Application/Server/Oqtane.Application.Server.csproj +++ b/Oqtane.Application/Server/Oqtane.Application.Server.csproj @@ -22,9 +22,9 @@ - - - + + + @@ -33,7 +33,7 @@ - + diff --git a/Oqtane.Application/Shared/Oqtane.Application.Shared.csproj b/Oqtane.Application/Shared/Oqtane.Application.Shared.csproj index 0d94a35a..fb8f617b 100644 --- a/Oqtane.Application/Shared/Oqtane.Application.Shared.csproj +++ b/Oqtane.Application/Shared/Oqtane.Application.Shared.csproj @@ -11,7 +11,7 @@ - + diff --git a/Oqtane.Client/Modules/Admin/Jobs/Edit.razor b/Oqtane.Client/Modules/Admin/Jobs/Edit.razor index 26d96688..85020c33 100644 --- a/Oqtane.Client/Modules/Admin/Jobs/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Jobs/Edit.razor @@ -5,100 +5,109 @@ @inject IStringLocalizer Localizer @inject IStringLocalizer SharedLocalizer -
-
-
- -
- +@if (_initialized) +{ + +
+ @if (!_editable) + { + + } +
+ +
+ +
-
-
- -
- +
+ +
+ +
-
-
- -
- +
+ +
+ +
-
-
- -
- - +
+ +
+ + +
-
-
- -
- +
+ +
+ +
-
-
- -
-
-
- -
-
- +
+ +
+
+
+ +
+
+ +
-
-
- -
-
-
- -
-
- +
+ +
+
+
+ +
+
+ +
-
-
- -
-
-
- -
-
- -
+
+ +
+
-
-
- - @SharedLocalizer["Cancel"] -
-
- - +
+ @if (_editable) + { + + } + else + { + + } + @SharedLocalizer["Cancel"] +
+
+ + +} @code { private ElementReference form; private bool validated = false; + private bool _initialized = false; + private bool _editable = true; private int _jobId; private string _name = string.Empty; private string _jobType = string.Empty; @@ -113,26 +122,32 @@ private DateTime? _nextDate = null; private DateTime? _nextTime = null; private string createdby; - private DateTime createdon; - private string modifiedby; - private DateTime modifiedon; + private DateTime createdon; + private string modifiedby; + private DateTime modifiedon; - public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; - protected override async Task OnInitializedAsync() - { - try - { - _jobId = Int32.Parse(PageState.QueryString["id"]); - Job job = await JobService.GetJobAsync(_jobId); - if (job != null) - { - _name = job.Name; - _jobType = job.JobType; - _isEnabled = job.IsEnabled.ToString(); - _interval = job.Interval.ToString(); - _frequency = job.Frequency; - _startDate = UtcToLocal(job.StartDate); + protected override async Task OnInitializedAsync() + { + await LoadJob(); + } + + protected async Task LoadJob() + { + try + { + _jobId = Int32.Parse(PageState.QueryString["id"]); + Job job = await JobService.GetJobAsync(_jobId); + if (job != null) + { + _editable = !job.IsEnabled && !job.IsExecuting; + _name = job.Name; + _jobType = job.JobType; + _isEnabled = job.IsEnabled.ToString(); + _interval = job.Interval.ToString(); + _frequency = job.Frequency; + _startDate = UtcToLocal(job.StartDate); _startTime = UtcToLocal(job.StartDate); _endDate = UtcToLocal(job.EndDate); _endTime = UtcToLocal(job.EndDate); @@ -140,70 +155,107 @@ _nextDate = UtcToLocal(job.NextExecution); _nextTime = UtcToLocal(job.NextExecution); createdby = job.CreatedBy; - createdon = job.CreatedOn; - modifiedby = job.ModifiedBy; - modifiedon = job.ModifiedOn; - } - } - catch (Exception ex) - { - await logger.LogError(ex, "Error Loading Job {JobId} {Error}", _jobId, ex.Message); - AddModuleMessage(Localizer["Error.Job.Load"], MessageType.Error); - } - } + createdon = job.CreatedOn; + modifiedby = job.ModifiedBy; + modifiedon = job.ModifiedOn; + } - private async Task SaveJob() - { - if (!Utilities.ValidateEffectiveExpiryDates(_startDate, _endDate)) - { - AddModuleMessage(Localizer["Message.StartEndDateError"], MessageType.Warning); - return; + _initialized = true; } - validated = true; - var interop = new Interop(JSRuntime); - if (await interop.FormValid(form)) - { - var job = await JobService.GetJobAsync(_jobId); - job.Name = _name; - job.JobType = _jobType; - job.IsEnabled = Boolean.Parse(_isEnabled); - job.Frequency = _frequency; - if (job.Frequency == "O") // once - { - job.Interval = 1; - } - else - { - job.Interval = int.Parse(_interval); - } - job.StartDate = _startDate.HasValue && _startTime.HasValue - ? LocalToUtc(_startDate.GetValueOrDefault().Date.Add(_startTime.GetValueOrDefault().TimeOfDay)) - : null; + catch (Exception ex) + { + await logger.LogError(ex, "Error Loading Job {JobId} {Error}", _jobId, ex.Message); + AddModuleMessage(Localizer["Error.Job.Load"], MessageType.Error); + } + } - job.EndDate = _endDate.HasValue && _endTime.HasValue - ? LocalToUtc(_endDate.GetValueOrDefault().Date.Add(_endTime.GetValueOrDefault().TimeOfDay)) - : null; + private async Task SaveJob() + { + try + { + var interop = new Interop(JSRuntime); + if (await interop.FormValid(form)) + { + var job = await JobService.GetJobAsync(_jobId); + job.Name = _name; + job.IsEnabled = bool.Parse(_isEnabled); - job.NextExecution = _nextDate.HasValue && _nextTime.HasValue - ? LocalToUtc(_nextDate.GetValueOrDefault().Date.Add(_nextTime.GetValueOrDefault().TimeOfDay)) - : null; - job.RetentionHistory = int.Parse(_retentionHistory); + job.Frequency = _frequency; + if (job.Frequency == "O") // once + { + job.Interval = 1; + } + else + { + job.Interval = int.Parse(_interval); + } - try - { - job = await JobService.UpdateJobAsync(job); - await logger.LogInformation("Job Updated {Job}", job); - NavigationManager.NavigateTo(NavigateUrl()); - } - catch (Exception ex) - { - await logger.LogError(ex, "Error Udate Job {Job} {Error}", job, ex.Message); - AddModuleMessage(Localizer["Error.Job.Update"], MessageType.Error); - } - } - else - { - AddModuleMessage(Localizer["Message.Required.JobInfo"], MessageType.Warning); - } - } + job.StartDate = _startDate.HasValue && _startTime.HasValue + ? LocalToUtc(_startDate.GetValueOrDefault().Date.Add(_startTime.GetValueOrDefault().TimeOfDay)) + : null; + + job.EndDate = _endDate.HasValue && _endTime.HasValue + ? LocalToUtc(_endDate.GetValueOrDefault().Date.Add(_endTime.GetValueOrDefault().TimeOfDay)) + : null; + + job.RetentionHistory = int.Parse(_retentionHistory); + + if (!job.IsEnabled || Utilities.ValidateEffectiveExpiryDates(job.StartDate, job.EndDate)) + { + if (!job.IsEnabled || (job.StartDate >= DateTime.UtcNow || job.StartDate == null)) + { + job.NextExecution = null; + job = await JobService.UpdateJobAsync(job); + await logger.LogInformation("Job Updated {Job}", job); + NavigationManager.NavigateTo(NavigateUrl()); + } + else + { + AddModuleMessage(Localizer["Message.StartDateError"], MessageType.Warning); + } + } + else + { + AddModuleMessage(Localizer["Message.StartEndDateError"], MessageType.Warning); + } + } + else + { + AddModuleMessage(Localizer["Message.Required.JobInfo"], MessageType.Warning); + } + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Updating Job {JobId} {Error}", _jobId, ex.Message); + AddModuleMessage(Localizer["Error.Job.Update"], MessageType.Error); + } + } + + private async Task DisableJob() + { + try + { + var job = await JobService.GetJobAsync(_jobId); + if (job != null) + { + if (job.IsExecuting) + { + AddModuleMessage(Localizer["Message.ExecutingError"], MessageType.Warning); + } + else + { + job.IsEnabled = false; + job.NextExecution = null; + job = await JobService.UpdateJobAsync(job); + await logger.LogInformation("Job Updated {Job}", job); + NavigationManager.NavigateTo(NavigateUrl()); + } + } + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Updating Job {JobId} {Error}", _jobId, ex.Message); + AddModuleMessage(Localizer["Error.Job.Update"], MessageType.Error); + } + } } diff --git a/Oqtane.Client/Modules/Admin/Login/Index.razor b/Oqtane.Client/Modules/Admin/Login/Index.razor index 773f52a8..15882e39 100644 --- a/Oqtane.Client/Modules/Admin/Login/Index.razor +++ b/Oqtane.Client/Modules/Admin/Login/Index.razor @@ -144,7 +144,6 @@ else private string _code = string.Empty; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous; - public override bool? Prerender => true; public override List Resources => new List() { @@ -222,6 +221,14 @@ else { AddModuleMessage(Localizer["ExternalLoginStatus." + PageState.QueryString["status"]], MessageType.Warning); } + else + { + if (_allowexternallogin && !_allowsitelogin) + { + // external login + ExternalLogin(); + } + } } } catch (Exception ex) diff --git a/Oqtane.Client/Modules/Admin/Site/Index.razor b/Oqtane.Client/Modules/Admin/Site/Index.razor index 0ceec1fd..d49d90ed 100644 --- a/Oqtane.Client/Modules/Admin/Site/Index.razor +++ b/Oqtane.Client/Modules/Admin/Site/Index.razor @@ -14,6 +14,7 @@ @inject IServiceProvider ServiceProvider @inject IStringLocalizer Localizer @inject INotificationService NotificationService +@inject IJobService JobService @inject IStringLocalizer SharedLocalizer @inject IOutputCacheService CacheService @@ -207,13 +208,6 @@
@if (_smtpenabled == "True" && UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) { -
-
-
-
- @Localizer["Smtp.Required.EnableNotificationJob"]
-
-
@@ -264,15 +258,6 @@
-
- -
- -
-
} else { @@ -534,7 +519,6 @@ private string _togglesmtpclientsecret = string.Empty; private string _smtpscopes = string.Empty; private string _smtpsender = string.Empty; - private string _smtprelay = "False"; private int _retention = 30; private string _pwaisenabled; @@ -653,7 +637,6 @@ _togglesmtpclientsecret = SharedLocalizer["ShowPassword"]; _smtpscopes = SettingService.GetSetting(settings, "SMTPScopes", string.Empty); _smtpsender = SettingService.GetSetting(settings, "SMTPSender", string.Empty); - _smtprelay = SettingService.GetSetting(settings, "SMTPRelay", "False"); _retention = int.Parse(SettingService.GetSetting(settings, "NotificationRetention", "30")); } @@ -846,8 +829,18 @@ settings = SettingService.SetSetting(settings, "SMTPClientSecret", _smtpclientsecret, true); settings = SettingService.SetSetting(settings, "SMTPScopes", _smtpscopes, true); settings = SettingService.SetSetting(settings, "SMTPSender", _smtpsender, true); - settings = SettingService.SetSetting(settings, "SMTPRelay", _smtprelay, true); settings = SettingService.SetSetting(settings, "NotificationRetention", _retention.ToString(), true); + + if (_smtpenabled == "True") + { + var jobs = await JobService.GetJobsAsync(); + var job = jobs.FirstOrDefault(item => item.JobType == "Oqtane.Infrastructure.NotificationJob, Oqtane.Server"); + if (job != null && !job.IsEnabled) + { + job.IsEnabled = true; + await JobService.UpdateJobAsync(job); + } + } } //cookie consent @@ -957,7 +950,10 @@ { try { + _smtpenabled = "True"; + var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId); + settings = SettingService.SetSetting(settings, "SMTPEnabled", _smtpenabled, true); settings = SettingService.SetSetting(settings, "SMTPHost", _smtphost, true); settings = SettingService.SetSetting(settings, "SMTPPort", _smtpport, true); settings = SettingService.SetSetting(settings, "SMTPSSL", _smtpssl, true); @@ -972,6 +968,14 @@ await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId); await logger.LogInformation("Site SMTP Settings Saved"); + var jobs = await JobService.GetJobsAsync(); + var job = jobs.FirstOrDefault(item => item.JobType == "Oqtane.Infrastructure.NotificationJob, Oqtane.Server"); + if (job != null && !job.IsEnabled) + { + job.IsEnabled = true; + await JobService.UpdateJobAsync(job); + } + await NotificationService.AddNotificationAsync(new Notification(PageState.Site.SiteId, PageState.User, PageState.Site.Name + " SMTP Configuration Test", "SMTP Server Is Configured Correctly.")); AddModuleMessage(Localizer["Info.Smtp.SaveSettings"], MessageType.Info); await ScrollToPageTop(); diff --git a/Oqtane.Client/Modules/Admin/Sql/Index.razor b/Oqtane.Client/Modules/Admin/Sql/Index.razor index bf2eefec..dc7ff28e 100644 --- a/Oqtane.Client/Modules/Admin/Sql/Index.razor +++ b/Oqtane.Client/Modules/Admin/Sql/Index.razor @@ -83,14 +83,14 @@ else { @if (_connection != "-") { - @if (!string.IsNullOrEmpty(_tenant)) - { -
- -
- -
+
+ +
+
+
+ @if (!string.IsNullOrEmpty(_tenant)) + {
@@ -150,130 +150,149 @@ else } @code { - private string _connection = "-"; - private Dictionary _connections; - private List _tenants; - private List _databases; + private string _connection = "-"; + private Dictionary _connections; + private List _tenants; + private List _databases; - private string _name = string.Empty; - private string _databasetype = string.Empty; - private Type _databaseConfigType; - private object _databaseConfig; - private RenderFragment DatabaseConfigComponent { get; set; } - private bool _showConnectionString = false; - private string _tenant = string.Empty; - private string _connectionstring = string.Empty; - private string _connectionstringtype = "password"; - private string _connectionstringtoggle = string.Empty; - private string _sql = string.Empty; - private List> _results; + private string _name = string.Empty; + private string _databasetype = string.Empty; + private Type _databaseConfigType; + private object _databaseConfig; + private RenderFragment DatabaseConfigComponent { get; set; } + private bool _showConnectionString = false; + private string _tenant = string.Empty; + private string _connectionstring = string.Empty; + private string _connectionstringtype = "password"; + private string _connectionstringtoggle = string.Empty; + private string _sql = string.Empty; + private List> _results; - public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; - protected override async Task OnInitializedAsync() - { - try - { - _connections = await SystemService.GetSystemInfoAsync("connectionstrings"); - _tenants = await TenantService.GetTenantsAsync(); - _databases = await DatabaseService.GetDatabasesAsync(); - _connectionstringtoggle = SharedLocalizer["ShowPassword"]; - } - catch (Exception ex) - { - await logger.LogError(ex, "Error Loading Tenants {Error}", ex.Message); - AddModuleMessage(ex.Message, MessageType.Error); - } - } + protected override async Task OnInitializedAsync() + { + try + { + _connections = await SystemService.GetSystemInfoAsync("connectionstrings"); + _tenants = await TenantService.GetTenantsAsync(); + _databases = await DatabaseService.GetDatabasesAsync(); + _connectionstringtoggle = SharedLocalizer["ShowPassword"]; + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Loading Tenants {Error}", ex.Message); + AddModuleMessage(ex.Message, MessageType.Error); + } + } - private async void ConnectionChanged(ChangeEventArgs e) - { - try - { - _connection = (string)e.Value; - if (_connection != "-" && _connection != "+") - { - _connectionstring = _connections[_connection].ToString(); - _tenant = ""; - _databasetype = ""; - var tenant = _tenants.FirstOrDefault(item => item.DBConnectionString == _connection); - if (tenant != null) - { - _tenant = tenant.Name; + private async void ConnectionChanged(ChangeEventArgs e) + { + try + { + _connection = (string)e.Value; + if (_connection != "-" && _connection != "+") + { + _connectionstring = _connections[_connection].ToString(); + _tenant = ""; + _databasetype = ""; + var tenant = _tenants.FirstOrDefault(item => item.DBConnectionString == _connection); + if (tenant != null) + { + _tenant = tenant.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 - { - if (_databases.Exists(item => item.IsDefault)) - { - _databasetype = _databases.Find(item => item.IsDefault).Name; - } - else - { + } + else + { + if (_connection.Contains(" (")) + { + _databasetype = _connection.Substring(_connection.LastIndexOf(" (") + 2).Replace(")", ""); + } + else + { + if (_databases.Exists(item => item.IsDefault)) + { + _databasetype = _databases.Find(item => item.IsDefault).Name; + } + else + { + _databasetype = Constants.DefaultDBName; + } + } + } + } + else + { + if (_databases.Exists(item => item.IsDefault)) + { + _databasetype = _databases.Find(item => item.IsDefault).Name; + } + else + { _databasetype = Constants.DefaultDBName; - } - _showConnectionString = false; - LoadDatabaseConfigComponent(); - } - StateHasChanged(); - } - catch (Exception ex) - { - await logger.LogError(ex, "Error Loading Connection {Connection} {Error}", _connection, ex.Message); - AddModuleMessage(ex.Message, MessageType.Error); - } - } + } + _showConnectionString = false; + LoadDatabaseConfigComponent(); + } + StateHasChanged(); + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Loading Connection {Connection} {Error}", _connection, ex.Message); + AddModuleMessage(ex.Message, MessageType.Error); + } + } - private void DatabaseTypeChanged(ChangeEventArgs eventArgs) - { - try - { - _databasetype = (string)eventArgs.Value; - _showConnectionString = false; - LoadDatabaseConfigComponent(); - } - catch - { - AddModuleMessage(Localizer["Error.Database.LoadConfig"], MessageType.Error); - } - } + private void DatabaseTypeChanged(ChangeEventArgs eventArgs) + { + try + { + _databasetype = (string)eventArgs.Value; + _showConnectionString = false; + LoadDatabaseConfigComponent(); + } + catch + { + AddModuleMessage(Localizer["Error.Database.LoadConfig"], MessageType.Error); + } + } - private void LoadDatabaseConfigComponent() - { - var database = _databases.SingleOrDefault(d => d.Name == _databasetype); - if (database != null) - { - _databaseConfigType = Type.GetType(database.ControlType); - DatabaseConfigComponent = builder => - { - builder.OpenComponent(0, _databaseConfigType); - builder.AddComponentReferenceCapture(1, inst => { _databaseConfig = Convert.ChangeType(inst, _databaseConfigType); }); - builder.CloseComponent(); - }; - } - } + private void LoadDatabaseConfigComponent() + { + var database = _databases.SingleOrDefault(d => d.Name == _databasetype); + if (database != null) + { + _databaseConfigType = Type.GetType(database.ControlType); + DatabaseConfigComponent = builder => + { + builder.OpenComponent(0, _databaseConfigType); + builder.AddComponentReferenceCapture(1, inst => { _databaseConfig = Convert.ChangeType(inst, _databaseConfigType); }); + builder.CloseComponent(); + }; + } + } - private void ShowConnectionString() - { - if (_databaseConfig is IDatabaseConfigControl databaseConfigControl) - { - _connectionstring = databaseConfigControl.GetConnectionString(); - } - _showConnectionString = !_showConnectionString; - } + private void ShowConnectionString() + { + if (_databaseConfig is IDatabaseConfigControl databaseConfigControl) + { + _connectionstring = databaseConfigControl.GetConnectionString(); + } + _showConnectionString = !_showConnectionString; + } - private async Task Add() - { - var connectionstring = _connectionstring; - if (!_showConnectionString && _databaseConfig is IDatabaseConfigControl databaseConfigControl) - { - connectionstring = databaseConfigControl.GetConnectionString(); - } - if (!string.IsNullOrEmpty(_name) && !string.IsNullOrEmpty(connectionstring)) - { - var settings = new Dictionary(); + private async Task Add() + { + var connectionstring = _connectionstring; + if (!_showConnectionString && _databaseConfig is IDatabaseConfigControl databaseConfigControl) + { + connectionstring = databaseConfigControl.GetConnectionString(); + } + if (!string.IsNullOrEmpty(_name) && !string.IsNullOrEmpty(connectionstring)) + { + _name = _name + " (" + _databasetype +")"; + var settings = new Dictionary(); settings.Add($"{SettingKeys.ConnectionStringsSection}:{_name}", connectionstring); await SystemService.UpdateSystemInfoAsync(settings); _connections = await SystemService.GetSystemInfoAsync("connectionstrings"); diff --git a/Oqtane.Client/Modules/Admin/Upgrade/Index.razor b/Oqtane.Client/Modules/Admin/Upgrade/Index.razor index aeda1366..1c9e322b 100644 --- a/Oqtane.Client/Modules/Admin/Upgrade/Index.razor +++ b/Oqtane.Client/Modules/Admin/Upgrade/Index.razor @@ -11,11 +11,22 @@ { - @if (_package != null && _upgradeavailable) + @if (_versions.Count > 0 && _upgradeable) {
- + +
+ +
+
+
+