Merge remote-tracking branch 'upstream/dev' into dev

This commit is contained in:
Leigh Pointer
2025-07-22 07:40:38 +02:00
50 changed files with 341 additions and 283 deletions

View File

@@ -54,7 +54,6 @@ namespace Microsoft.Extensions.DependencyInjection
services.AddScoped<ILocalizationCookieService, LocalizationCookieService>();
services.AddScoped<ICookieConsentService, CookieConsentService>();
services.AddScoped<IOutputCacheService, OutputCacheService>();
services.AddScoped<ITimeZoneService, TimeZoneService>();
// providers
services.AddScoped<ITextEditor, Oqtane.Modules.Controls.QuillJSTextEditor>();

View File

@@ -3,7 +3,6 @@
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IUserService UserService
@inject ITimeZoneService TimeZoneService
@inject IStringLocalizer<Index> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
@inject ISettingService SettingService
@@ -115,7 +114,7 @@
{
_passwordrequirements = await UserService.GetPasswordRequirementsAsync(PageState.Site.SiteId);
_allowsitelogin = bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:AllowSiteLogin", "true"));
_timezones = await TimeZoneService.GetTimeZonesAsync();
_timezones = Utilities.GetTimeZones();
_timezoneid = PageState.Site.TimeZoneId;
_initialized = true;
}

View File

@@ -46,7 +46,7 @@
<Row>
<div class="search-item mb-2">
<h4 class="mb-1"><a href="@context.Url">@context.Title</a></h4>
<p class="mb-0 text-muted">@((MarkupString)context.Snippet)</p>
<p class="mb-0 text-body-secondary">@((MarkupString)context.Snippet)</p>
</div>
</Row>
</Pager>

View File

@@ -10,7 +10,6 @@
@inject IAliasService AliasService
@inject IThemeService ThemeService
@inject ISettingService SettingService
@inject ITimeZoneService TimeZoneService
@inject IServiceProvider ServiceProvider
@inject IStringLocalizer<Index> Localizer
@inject INotificationService NotificationService
@@ -508,7 +507,7 @@
Site site = await SiteService.GetSiteAsync(PageState.Site.SiteId);
if (site != null)
{
_timezones = await TimeZoneService.GetTimeZonesAsync();
_timezones = Utilities.GetTimeZones();
var settings = await SettingService.GetSiteSettingsAsync(site.SiteId);
_pages = await PageService.GetPagesAsync(PageState.Site.SiteId);

View File

@@ -9,7 +9,6 @@
@inject INotificationService NotificationService
@inject IFileService FileService
@inject IFolderService FolderService
@inject ITimeZoneService TimeZoneService
@inject IJSRuntime jsRuntime
@inject IServiceProvider ServiceProvider
@inject IStringLocalizer<Index> Localizer
@@ -404,7 +403,7 @@
_togglepassword = SharedLocalizer["ShowPassword"];
_allowtwofactor = (SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:TwoFactor", "false") == "true");
_profiles = await ProfileService.GetProfilesAsync(ModuleState.SiteId);
_timezones = await TimeZoneService.GetTimeZonesAsync();
_timezones = Utilities.GetTimeZones();
if (PageState.User != null)
{
@@ -414,11 +413,6 @@
_displayname = PageState.User.DisplayName;
_timezoneid = PageState.User.TimeZoneId;
if (string.IsNullOrEmpty(_email))
{
AddModuleMessage(Localizer["Message.User.NoEmail"], MessageType.Warning);
}
// get user folder
var folder = await FolderService.GetFolderAsync(ModuleState.SiteId, PageState.User.FolderPath);
if (folder != null)

View File

@@ -5,7 +5,6 @@
@inject IUserService UserService
@inject IProfileService ProfileService
@inject ISettingService SettingService
@inject ITimeZoneService TimeZoneService
@inject IStringLocalizer<Add> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
@@ -133,7 +132,7 @@
{
try
{
_timezones = await TimeZoneService.GetTimeZonesAsync();
_timezones = Utilities.GetTimeZones();
_profiles = await ProfileService.GetProfilesAsync(ModuleState.SiteId);
_settings = new Dictionary<string, string>();
_timezoneid = PageState.Site.TimeZoneId;

View File

@@ -6,7 +6,6 @@
@inject IProfileService ProfileService
@inject ISettingService SettingService
@inject IFileService FileService
@inject ITimeZoneService TimeZoneService
@inject IServiceProvider ServiceProvider
@inject IStringLocalizer<Edit> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
@@ -204,7 +203,7 @@
_passwordrequirements = await UserService.GetPasswordRequirementsAsync(PageState.Site.SiteId);
_togglepassword = SharedLocalizer["ShowPassword"];
_profiles = await ProfileService.GetProfilesAsync(PageState.Site.SiteId);
_timezones = await TimeZoneService.GetTimeZonesAsync();
_timezones = Utilities.GetTimeZones();
if (PageState.QueryString.ContainsKey("id") && int.TryParse(PageState.QueryString["id"], out int UserId))
{

View File

@@ -157,6 +157,9 @@
[Parameter]
public bool UploadMultiple { get; set; } = false; // optional - enable multiple file uploads - default false
[Parameter]
public bool AnonymizeUploadFilenames { get; set; } = false; // optional - indicate if file names should be anonymized on upload - default false
[Parameter]
public int ChunkSize { get; set; } = 1; // optional - size of file chunks to upload in MB
@@ -408,7 +411,7 @@
}
// upload files
var success = await interop.UploadFiles(posturl, folder, _guid, SiteState.AntiForgeryToken, jwt, chunksize, tokenSource.Token);
var success = await interop.UploadFiles(posturl, folder, _guid, SiteState.AntiForgeryToken, jwt, chunksize, AnonymizeUploadFilenames, tokenSource.Token);
// reset progress indicators
if (ShowProgress)

View File

@@ -507,24 +507,18 @@ namespace Oqtane.Modules
if (datetime == null)
return null;
TimeZoneInfo timezone = null;
try
string timezoneId = null;
if (PageState.User != null && !string.IsNullOrEmpty(PageState.User.TimeZoneId))
{
if (PageState.User != null && !string.IsNullOrEmpty(PageState.User.TimeZoneId))
{
timezone = TimeZoneInfo.FindSystemTimeZoneById(PageState.User.TimeZoneId);
}
else if (!string.IsNullOrEmpty(PageState.Site.TimeZoneId))
{
timezone = TimeZoneInfo.FindSystemTimeZoneById(PageState.Site.TimeZoneId);
}
timezoneId = PageState.User.TimeZoneId;
}
catch
else if (!string.IsNullOrEmpty(PageState.Site.TimeZoneId))
{
// The time zone ID was not found on the local computer
timezoneId = PageState.Site.TimeZoneId;
}
return Utilities.UtcAsLocalDateTime(datetime, timezone);
return Utilities.UtcAsLocalDateTime(datetime, timezoneId);
}
public DateTime? LocalToUtc(DateTime? datetime)
@@ -533,24 +527,18 @@ namespace Oqtane.Modules
if (datetime == null)
return null;
TimeZoneInfo timezone = null;
try
string timezoneId = null;
if (PageState.User != null && !string.IsNullOrEmpty(PageState.User.TimeZoneId))
{
if (PageState.User != null && !string.IsNullOrEmpty(PageState.User.TimeZoneId))
{
timezone = TimeZoneInfo.FindSystemTimeZoneById(PageState.User.TimeZoneId);
}
else if (!string.IsNullOrEmpty(PageState.Site.TimeZoneId))
{
timezone = TimeZoneInfo.FindSystemTimeZoneById(PageState.Site.TimeZoneId);
}
timezoneId = PageState.User.TimeZoneId;
}
catch
else if (!string.IsNullOrEmpty(PageState.Site.TimeZoneId))
{
// The time zone ID was not found on the local computer
timezoneId = PageState.Site.TimeZoneId;
}
return Utilities.LocalDateAndTimeAsUtc(datetime, timezone);
return Utilities.LocalDateAndTimeAsUtc(datetime, timezoneId);
}
// logging methods

View File

@@ -4,7 +4,7 @@
<TargetFramework>net9.0</TargetFramework>
<OutputType>Exe</OutputType>
<Configurations>Debug;Release</Configurations>
<Version>6.1.3</Version>
<Version>6.1.4</Version>
<Product>Oqtane</Product>
<Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company>
@@ -12,7 +12,7 @@
<Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.3</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.4</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<RootNamespace>Oqtane</RootNamespace>
@@ -22,10 +22,10 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.5" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.5" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.7" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.7" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.7" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.7" />
</ItemGroup>
<ItemGroup>

View File

@@ -147,9 +147,6 @@
<data name="Message.User.NoLogIn" xml:space="preserve">
<value>Current User Is Not Logged In</value>
</data>
<data name="Message.User.NoEmail" xml:space="preserve">
<value>You Must Provide An Email Address For Your User Account</value>
</data>
<data name="Error.Profile.Load" xml:space="preserve">
<value>Error Loading User Profile</value>
</data>

View File

@@ -256,7 +256,7 @@ namespace Oqtane.Services
Dictionary<string, string> SetSetting(Dictionary<string, string> settings, string settingName, string settingValue, bool isPrivate);
Dictionary<string, string> MergeSettings(Dictionary<string, string> settings1, Dictionary<string, string> settings2);
Dictionary<string, string> MergeSettings(Dictionary<string, string> baseSettings, Dictionary<string, string> overwriteSettings);
[Obsolete("GetSettingAsync(int settingId) is deprecated. Use GetSettingAsync(string entityName, int settingId) instead.", false)]

View File

@@ -1,18 +0,0 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Oqtane.Models;
namespace Oqtane.Services
{
/// <summary>
/// Service to store and retrieve <see cref="TimeZone"/> entries
/// </summary>
public interface ITimeZoneService
{
/// <summary>
/// Get the list of time zones
/// </summary>
/// <returns></returns>
Task<List<TimeZone>> GetTimeZonesAsync();
}
}

View File

@@ -266,27 +266,25 @@ namespace Oqtane.Services
return settings;
}
public Dictionary<string, string> MergeSettings(Dictionary<string, string> settings1, Dictionary<string, string> settings2)
public Dictionary<string, string> MergeSettings(Dictionary<string, string> baseSettings, Dictionary<string, string> overwriteSettings)
{
if (settings1 == null)
var settings = baseSettings != null ? new Dictionary<string, string>(baseSettings) : new Dictionary<string, string>();
if (overwriteSettings != null)
{
settings1 = new Dictionary<string, string>();
}
if (settings2 != null)
{
foreach (var setting in settings2)
foreach (var setting in overwriteSettings)
{
if (settings1.ContainsKey(setting.Key))
if (settings.ContainsKey(setting.Key))
{
settings1[setting.Key] = setting.Value;
settings[setting.Key] = setting.Value;
}
else
{
settings1.Add(setting.Key, setting.Value);
settings.Add(setting.Key, setting.Value);
}
}
}
return settings1;
return settings;
}
[Obsolete("GetSettingAsync(int settingId) is deprecated. Use GetSettingAsync(string entityName, int settingId) instead.", false)]

View File

@@ -1,22 +0,0 @@
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Oqtane.Documentation;
using Oqtane.Models;
using Oqtane.Shared;
namespace Oqtane.Services
{
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
public class TimeZoneService : ServiceBase, ITimeZoneService
{
public TimeZoneService(HttpClient http, SiteState siteState) : base(http, siteState) { }
private string Apiurl => CreateApiUrl("TimeZone");
public async Task<List<TimeZone>> GetTimeZonesAsync()
{
return await GetJsonAsync<List<TimeZone>>($"{Apiurl}");
}
}
}

View File

@@ -353,7 +353,7 @@
module.PageId = PageState.Page.PageId;
module.ModuleDefinitionName = _moduleDefinitionName;
module.AllPages = false;
module.PermissionList = GenerateDefaultPermissions(module.SiteId);
module.PermissionList = GenerateDefaultPermissions(module.SiteId, module.ModuleDefinitionName);
module = await ModuleService.AddModuleAsync(module);
newModuleId = module.ModuleId;
@@ -365,7 +365,7 @@
module.SiteId = PageState.Page.SiteId;
module.PageId = PageState.Page.PageId;
module.AllPages = false;
module.PermissionList = GenerateDefaultPermissions(module.SiteId);
module.PermissionList = GenerateDefaultPermissions(module.SiteId, module.ModuleDefinitionName);
module = await ModuleService.AddModuleAsync(module);
var moduleContent = await ModuleService.ExportModuleAsync(int.Parse(_moduleId), PageState.Page.PageId);
@@ -430,7 +430,7 @@
}
}
private List<Permission> GenerateDefaultPermissions(int siteId)
private List<Permission> GenerateDefaultPermissions(int siteId, string moduleDefinitionName)
{
var permissions = new List<Permission>();
if (_visibility == "view")
@@ -443,8 +443,22 @@
// set module view permissions to page edit permissions
permissions = SetPermissions(permissions, siteId, PermissionNames.View, PermissionNames.Edit);
}
// set module edit permissions to page edit permissions
permissions = SetPermissions(permissions, siteId, PermissionNames.Edit, PermissionNames.Edit);
// get module permissions
var permissionNames = $"{PermissionNames.View},{PermissionNames.Edit}";
var moduleDefinition = _allModuleDefinitions.FirstOrDefault(item => item.ModuleDefinitionName == moduleDefinitionName);
if (moduleDefinition != null && !string.IsNullOrEmpty(moduleDefinition.PermissionNames))
{
permissionNames = moduleDefinition.PermissionNames;
}
foreach (var permission in permissionNames.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
if (permission != PermissionNames.View)
{
// set remaining module permissions to page edit permissions
permissions = SetPermissions(permissions, siteId, permission, PermissionNames.Edit);
}
}
return permissions;
}

View File

@@ -16,8 +16,8 @@ namespace Oqtane.Themes.OqtaneTheme
ContainerSettingsType = "Oqtane.Themes.OqtaneTheme.ContainerSettings, Oqtane.Client",
Resources = new List<Resource>()
{
// obtained from https://cdnjs.com/libraries
new Stylesheet("https://cdnjs.cloudflare.com/ajax/libs/bootswatch/5.3.3/cyborg/bootstrap.min.css", "sha512-M+Wrv9LTvQe81gFD2ZE3xxPTN5V2n1iLCXsldIxXvfs6tP+6VihBCwCMBkkjkQUZVmEHBsowb9Vqsq1et1teEg==", "anonymous"),
// obtained from https://www.jsdelivr.com/package/npm/bootswatch
new Stylesheet("https://cdn.jsdelivr.net/npm/bootswatch@5.3.5/dist/cyborg/bootstrap.min.css"),
new Stylesheet("~/Theme.css"),
new Script(Constants.BootstrapScriptUrl, Constants.BootstrapScriptIntegrity, "anonymous")
}

View File

@@ -224,17 +224,17 @@ namespace Oqtane.UI
public Task UploadFiles(string posturl, string folder, string id, string antiforgerytoken, string jwt)
{
UploadFiles(posturl, folder, id, antiforgerytoken, jwt, 1);
UploadFiles(posturl, folder, id, antiforgerytoken, jwt, 1, false);
return Task.CompletedTask;
}
public ValueTask<bool> UploadFiles(string posturl, string folder, string id, string antiforgerytoken, string jwt, int chunksize, CancellationToken cancellationToken = default)
public ValueTask<bool> UploadFiles(string posturl, string folder, string id, string antiforgerytoken, string jwt, int chunksize, bool anonymizeuploadfilenames, CancellationToken cancellationToken = default)
{
try
{
return _jsRuntime.InvokeAsync<bool>(
"Oqtane.Interop.uploadFiles", cancellationToken,
posturl, folder, id, antiforgerytoken, jwt, chunksize);
posturl, folder, id, antiforgerytoken, jwt, chunksize, anonymizeuploadfilenames);
}
catch
{

View File

@@ -20,13 +20,6 @@
return;
}
// force authenticated user to provide email address (email may be missing if using external login)
if (PageState.User != null && PageState.User.IsAuthenticated && string.IsNullOrEmpty(PageState.User.Email) && PageState.Route.PagePath != "profile")
{
NavigationManager.NavigateTo(Utilities.NavigateUrl(PageState.Alias.Path, "profile", "returnurl=" + WebUtility.UrlEncode(PageState.Route.PathAndQuery)));
return;
}
// set page title
if (!string.IsNullOrEmpty(PageState.Page.Title))
{

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Version>6.1.3</Version>
<Version>6.1.4</Version>
<Product>Oqtane</Product>
<Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company>
@@ -10,7 +10,7 @@
<Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.3</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.4</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Version>6.1.3</Version>
<Version>6.1.4</Version>
<Product>Oqtane</Product>
<Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company>
@@ -10,7 +10,7 @@
<Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.3</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.4</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
@@ -34,7 +34,7 @@
<ItemGroup>
<PackageReference Include="EFCore.NamingConventions" Version="9.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.7" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
</ItemGroup>

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Version>6.1.3</Version>
<Version>6.1.4</Version>
<Product>Oqtane</Product>
<Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company>
@@ -10,7 +10,7 @@
<Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.3</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.4</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
@@ -33,7 +33,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.7" />
</ItemGroup>
<ItemGroup>

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Version>6.1.3</Version>
<Version>6.1.4</Version>
<Product>Oqtane</Product>
<Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company>
@@ -10,7 +10,7 @@
<Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.3</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.4</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
@@ -33,7 +33,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.7" />
</ItemGroup>
<ItemGroup>

View File

@@ -6,7 +6,7 @@
<!-- <TargetFrameworks>net9.0-android;net9.0-ios;net9.0-maccatalyst</TargetFrameworks> -->
<!-- <TargetFrameworks>$(TargetFrameworks);net9.0-tizen</TargetFrameworks> -->
<OutputType>Exe</OutputType>
<Version>6.1.3</Version>
<Version>6.1.4</Version>
<Product>Oqtane</Product>
<Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company>
@@ -14,7 +14,7 @@
<Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.3</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.4</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<RootNamespace>Oqtane.Maui</RootNamespace>
@@ -30,7 +30,7 @@
<ApplicationId>com.oqtane.maui</ApplicationId>
<!-- Versions -->
<ApplicationDisplayVersion>6.1.3</ApplicationDisplayVersion>
<ApplicationDisplayVersion>6.1.4</ApplicationDisplayVersion>
<ApplicationVersion>1</ApplicationVersion>
<!-- To develop, package, and publish an app to the Microsoft Store, see: https://aka.ms/MauiTemplateUnpackaged -->
@@ -67,14 +67,14 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="9.0.5" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.5" />
<PackageReference Include="System.Net.Http.Json" Version="9.0.5" />
<PackageReference Include="Microsoft.Maui.Controls" Version="9.0.61" />
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="9.0.61" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="9.0.61" />
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="9.0.7" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.7" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.7" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.7" />
<PackageReference Include="System.Net.Http.Json" Version="9.0.7" />
<PackageReference Include="Microsoft.Maui.Controls" Version="9.0.90" />
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="9.0.90" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="9.0.90" />
</ItemGroup>
<ItemGroup>

View File

@@ -75,6 +75,10 @@ app {
color: gray;
}
.app-moduleactions .dropdown-menu {
z-index: 9999;
}
.app-moduleactions .dropdown-submenu {
position: relative;
}
@@ -270,4 +274,4 @@ app {
.app-logo .navbar-brand {
padding: 5px 20px 5px 20px;
}
}

View File

@@ -2,7 +2,7 @@
<package>
<metadata>
<id>Oqtane.Client</id>
<version>6.1.3</version>
<version>6.1.4</version>
<authors>Shaun Walker</authors>
<owners>.NET Foundation</owners>
<title>Oqtane Framework</title>
@@ -12,7 +12,7 @@
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license>
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.3</releaseNotes>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.4</releaseNotes>
<readme>readme.md</readme>
<icon>icon.png</icon>
<tags>oqtane</tags>

View File

@@ -2,7 +2,7 @@
<package>
<metadata>
<id>Oqtane.Framework</id>
<version>6.1.3</version>
<version>6.1.4</version>
<authors>Shaun Walker</authors>
<owners>.NET Foundation</owners>
<title>Oqtane Framework</title>
@@ -11,8 +11,8 @@
<copyright>.NET Foundation</copyright>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license>
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v6.1.3/Oqtane.Framework.6.1.3.Upgrade.zip</projectUrl>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.3</releaseNotes>
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v6.1.4/Oqtane.Framework.6.1.4.Upgrade.zip</projectUrl>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.4</releaseNotes>
<readme>readme.md</readme>
<icon>icon.png</icon>
<tags>oqtane framework</tags>

View File

@@ -2,7 +2,7 @@
<package>
<metadata>
<id>Oqtane.Server</id>
<version>6.1.3</version>
<version>6.1.4</version>
<authors>Shaun Walker</authors>
<owners>.NET Foundation</owners>
<title>Oqtane Framework</title>
@@ -12,7 +12,7 @@
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license>
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.3</releaseNotes>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.4</releaseNotes>
<readme>readme.md</readme>
<icon>icon.png</icon>
<tags>oqtane</tags>

View File

@@ -2,7 +2,7 @@
<package>
<metadata>
<id>Oqtane.Shared</id>
<version>6.1.3</version>
<version>6.1.4</version>
<authors>Shaun Walker</authors>
<owners>.NET Foundation</owners>
<title>Oqtane Framework</title>
@@ -12,7 +12,7 @@
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license>
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.3</releaseNotes>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.4</releaseNotes>
<readme>readme.md</readme>
<icon>icon.png</icon>
<tags>oqtane</tags>

View File

@@ -2,7 +2,7 @@
<package>
<metadata>
<id>Oqtane.Updater</id>
<version>6.1.3</version>
<version>6.1.4</version>
<authors>Shaun Walker</authors>
<owners>.NET Foundation</owners>
<title>Oqtane Framework</title>
@@ -12,7 +12,7 @@
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license>
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.3</releaseNotes>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.4</releaseNotes>
<readme>readme.md</readme>
<icon>icon.png</icon>
<tags>oqtane</tags>

View File

@@ -1 +1 @@
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net9.0\publish\*" -DestinationPath "Oqtane.Framework.6.1.3.Install.zip" -Force
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net9.0\publish\*" -DestinationPath "Oqtane.Framework.6.1.4.Install.zip" -Force

View File

@@ -1 +1 @@
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net9.0\publish\*" -DestinationPath "Oqtane.Framework.6.1.3.Upgrade.zip" -Force
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net9.0\publish\*" -DestinationPath "Oqtane.Framework.6.1.4.Upgrade.zip" -Force

View File

@@ -444,9 +444,14 @@ namespace Oqtane.Controllers
}
// ensure filename is valid
if (!formfile.FileName.IsPathOrFileValid() || !HasValidFileExtension(formfile.FileName))
string fileName = formfile.FileName;
if (Path.GetExtension(fileName).Contains(':'))
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "File Upload File Name Is Invalid Or Contains Invalid Extension {File}", formfile.FileName);
fileName = fileName.Substring(0, fileName.LastIndexOf(':')); // remove invalid suffix from extension
}
if (!fileName.IsPathOrFileValid() || !HasValidFileExtension(fileName))
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "File Upload File Name Is Invalid Or Contains Invalid Extension {File}", fileName);
return StatusCode((int)HttpStatusCode.Forbidden);
}
@@ -458,8 +463,8 @@ namespace Oqtane.Controllers
return StatusCode((int)HttpStatusCode.Forbidden);
}
// create file name using header values
string fileName = formfile.FileName + ".part_" + partCount.ToString("000") + "_" + totalParts.ToString("000");
// create file name using header part values
fileName += ".part_" + partCount.ToString("000") + "_" + totalParts.ToString("000");
string folderPath = "";
try
@@ -532,13 +537,13 @@ namespace Oqtane.Controllers
string parts = Path.GetExtension(filename)?.Replace(token, ""); // returns "001_999"
int totalparts = int.Parse(parts?.Substring(parts.IndexOf("_") + 1));
filename = Path.GetFileNameWithoutExtension(filename); // base filename
filename = Path.GetFileNameWithoutExtension(filename); // base filename including original file extension
string[] fileparts = Directory.GetFiles(folder, filename + token + "*"); // list of all file parts
// if all of the file parts exist (note that file parts can arrive out of order)
if (fileparts.Length == totalparts && CanAccessFiles(fileparts))
{
// merge file parts into temp file (in case another user is trying to get the file)
// merge file parts into temp file (in case another user is trying to read the file)
bool success = true;
using (var stream = new FileStream(Path.Combine(folder, filename + ".tmp"), FileMode.Create))
{
@@ -559,25 +564,22 @@ namespace Oqtane.Controllers
}
// clean up file parts
foreach (var file in Directory.GetFiles(folder, "*" + token + "*"))
foreach (var file in fileparts)
{
if (fileparts.Contains(file))
try
{
try
{
System.IO.File.Delete(file);
}
catch
{
// unable to delete part - ignore
}
System.IO.File.Delete(file);
}
catch
{
// unable to delete part - ignore
}
}
// rename temp file
if (success)
{
// remove file if it already exists (as well as any thumbnails which may exist)
// remove existing file (as well as any thumbnails)
foreach (var file in Directory.GetFiles(folder, Path.GetFileNameWithoutExtension(filename) + ".*"))
{
if (Path.GetExtension(file) != ".tmp")

View File

@@ -43,7 +43,6 @@ namespace Oqtane.Controllers
private readonly ILogManager _logger;
private readonly Alias _alias;
private readonly string _visitorCookie;
public SettingController(ISettingRepository settings, IPageModuleRepository pageModules, IUserPermissions userPermissions, ITenantManager tenantManager, ISyncManager syncManager,
IOptions<CookieAuthenticationOptions> cookieOptions, IOptionsSnapshot<CookieAuthenticationOptions> cookieOptionsSnapshot, IOptionsMonitorCache<CookieAuthenticationOptions> cookieOptionsMonitorCache,

View File

@@ -1,29 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Oqtane.Models;
using Oqtane.Shared;
namespace Oqtane.Controllers
{
[Route(ControllerRoutes.ApiRoute)]
public class TimeZoneController : Controller
{
public TimeZoneController() {}
// GET: api/<controller>
[HttpGet]
public IEnumerable<Models.TimeZone> Get()
{
return TimeZoneInfo.GetSystemTimeZones()
.Select(item => new Models.TimeZone
{
Id = item.Id,
DisplayName = item.DisplayName
})
.OrderBy(item => item.DisplayName);
}
}
}

View File

@@ -104,7 +104,6 @@ namespace Microsoft.Extensions.DependencyInjection
services.AddScoped<ISearchProvider, DatabaseSearchProvider>();
services.AddScoped<IImageService, ImageService>();
services.AddScoped<ICookieConsentService, ServerCookieConsentService>();
services.AddScoped<ITimeZoneService, TimeZoneService>();
// providers
services.AddScoped<ITextEditor, Oqtane.Modules.Controls.QuillJSTextEditor>();

View File

@@ -404,13 +404,13 @@ namespace Oqtane.Extensions
else if (!string.IsNullOrEmpty(name)) // name claim provided
{
username = name.ToLower().Replace(" ", "") + DateTime.UtcNow.ToString("mmss");
emailaddress = ""; // unknown - will need to be requested from user later
emailaddress = username + "@unknown.com";
displayname = name;
}
else // neither email nor name provided
{
username = Guid.NewGuid().ToString("N");
emailaddress = ""; // unknown - will need to be requested from user later
emailaddress = username + "@unknown.com";
displayname = username;
}

View File

@@ -1,9 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Mail;
using MailKit.Net.Smtp;
using Microsoft.Extensions.DependencyInjection;
using MimeKit;
using Oqtane.Models;
using Oqtane.Repository;
using Oqtane.Shared;
@@ -48,18 +52,17 @@ namespace Oqtane.Infrastructure
settingRepository.GetSettingValue(settings, "SMTPPort", "") != "" &&
settingRepository.GetSettingValue(settings, "SMTPSender", "") != "")
{
// construct SMTP Client
var client = new SmtpClient()
{
DeliveryMethod = SmtpDeliveryMethod.Network,
UseDefaultCredentials = false,
Host = settingRepository.GetSettingValue(settings, "SMTPHost", ""),
Port = int.Parse(settingRepository.GetSettingValue(settings, "SMTPPort", "")),
EnableSsl = bool.Parse(settingRepository.GetSettingValue(settings, "SMTPSSL", "False"))
};
// construct SMTP Client
using var client = new SmtpClient();
client.Connect(host: settingRepository.GetSettingValue(settings, "SMTPHost", ""),
port: int.Parse(settingRepository.GetSettingValue(settings, "SMTPPort", "")),
options: bool.Parse(settingRepository.GetSettingValue(settings, "SMTPSSL", "False")) ? MailKit.Security.SecureSocketOptions.StartTls : MailKit.Security.SecureSocketOptions.None);
if (settingRepository.GetSettingValue(settings, "SMTPUsername", "") != "" && settingRepository.GetSettingValue(settings, "SMTPPassword", "") != "")
{
client.Credentials = new NetworkCredential(settingRepository.GetSettingValue(settings, "SMTPUsername", ""), settingRepository.GetSettingValue(settings, "SMTPPassword", ""));
client.Authenticate(settingRepository.GetSettingValue(settings, "SMTPUsername", ""),
settingRepository.GetSettingValue(settings, "SMTPPassword", ""));
}
// iterate through undelivered notifications
@@ -88,7 +91,7 @@ namespace Oqtane.Infrastructure
}
// validate recipient
if (string.IsNullOrEmpty(notification.ToEmail) || !MailAddress.TryCreate(notification.ToEmail, out _))
if (string.IsNullOrEmpty(notification.ToEmail) || !MailboxAddress.TryParse(notification.ToEmail, out _))
{
log += $"NotificationId: {notification.NotificationId} - Has Missing Or Invalid Recipient {notification.ToEmail}<br />";
notification.IsDeleted = true;
@@ -96,50 +99,52 @@ namespace Oqtane.Infrastructure
}
else
{
MailMessage mailMessage = new MailMessage();
MimeMessage mailMessage = new MimeMessage();
// sender
if (settingRepository.GetSettingValue(settings, "SMTPRelay", "False") == "True" && !string.IsNullOrEmpty(notification.FromEmail))
{
if (!string.IsNullOrEmpty(notification.FromDisplayName))
{
mailMessage.From = new MailAddress(notification.FromEmail, notification.FromDisplayName);
mailMessage.From.Add(new MailboxAddress(notification.FromDisplayName, notification.FromEmail));
}
else
{
mailMessage.From = new MailAddress(notification.FromEmail);
mailMessage.From.Add(new MailboxAddress("", notification.FromEmail));
}
}
else
{
mailMessage.From = new MailAddress(settingRepository.GetSettingValue(settings, "SMTPSender", ""), (!string.IsNullOrEmpty(notification.FromDisplayName)) ? notification.FromDisplayName : site.Name);
mailMessage.From.Add(new MailboxAddress((!string.IsNullOrEmpty(notification.FromDisplayName)) ? notification.FromDisplayName : site.Name,
settingRepository.GetSettingValue(settings, "SMTPSender", "")));
}
// recipient
if (!string.IsNullOrEmpty(notification.ToDisplayName))
{
mailMessage.To.Add(new MailAddress(notification.ToEmail, notification.ToDisplayName));
mailMessage.To.Add(new MailboxAddress(notification.ToDisplayName, notification.ToEmail));
}
else
{
mailMessage.To.Add(new MailAddress(notification.ToEmail));
mailMessage.To.Add(new MailboxAddress("", notification.ToEmail));
}
// subject
mailMessage.Subject = notification.Subject;
//body
mailMessage.Body = notification.Body;
if (!mailMessage.Body.Contains("<") || !mailMessage.Body.Contains(">"))
var bodyText = notification.Body;
if (!bodyText.Contains('<') || !bodyText.Contains('>'))
{
// plain text messages should convert line breaks to HTML tags to preserve formatting
mailMessage.Body = mailMessage.Body.Replace("\n", "<br />");
bodyText = bodyText.Replace("\n", "<br />");
}
// encoding
mailMessage.SubjectEncoding = System.Text.Encoding.UTF8;
mailMessage.BodyEncoding = System.Text.Encoding.UTF8;
mailMessage.IsBodyHtml = true;
mailMessage.Body = new TextPart("html", System.Text.Encoding.UTF8)
{
Text = bodyText
};
// send mail
try
@@ -157,6 +162,7 @@ namespace Oqtane.Infrastructure
}
}
}
client.Disconnect(true);
log += "Notifications Delivered: " + sent + "<br />";
}
else

View File

@@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Configurations>Debug;Release</Configurations>
<Version>6.1.3</Version>
<Version>6.1.4</Version>
<Product>Oqtane</Product>
<Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company>
@@ -11,7 +11,7 @@
<Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.3</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.4</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<RootNamespace>Oqtane</RootNamespace>
@@ -34,21 +34,22 @@
<EmbeddedResource Include="Scripts\MigrateTenant.sql" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.5" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.5" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.7" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.7" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.5">
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.7" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.7">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.5" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="9.0.5" />
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.7" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="9.0.7" />
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="9.0.7" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.11" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.9" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.10" />
<PackageReference Include="HtmlAgilityPack" Version="1.12.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.4" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.3" />
<PackageReference Include="MailKit" Version="4.13.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Oqtane.Client\Oqtane.Client.csproj" />

View File

@@ -13,11 +13,11 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.5" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.5" />
<PackageReference Include="System.Net.Http.Json" Version="9.0.5" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.7" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.7" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.7" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.7" />
<PackageReference Include="System.Net.Http.Json" Version="9.0.7" />
</ItemGroup>
<ItemGroup>

View File

@@ -19,10 +19,10 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.5" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.5" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.7" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.7" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.7" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.7" />
</ItemGroup>
<ItemGroup>

View File

@@ -16,10 +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 Resource { ResourceType = ResourceType.Stylesheet, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css", Integrity = "sha512-jnSuA4Ss2PkkikSOLtYs8BlYIeeIK1h99ty4YfvRPAlzr377vr3CXDb7sb7eEEBYjDtcYj+AjBH3FLv5uSJuXg==", CrossOrigin = "anonymous" },
// obtained from https://www.jsdelivr.com/
new Script(Constants.BootstrapStylesheetUrl, Constants.BootstrapStylesheetIntegrity, "anonymous"),
new Resource { ResourceType = ResourceType.Stylesheet, Url = "~/Theme.css" },
new Resource { ResourceType = ResourceType.Script, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/js/bootstrap.bundle.min.js", Integrity = "sha512-7Pi/otdlbbCR+LnW+F7PwFcSDJOuUJB3OxtEHbg4vSMvzvJjde4Po1v4BR9Gdc9aXNUNFVUY+SK51wWT8WF0Gg==", CrossOrigin = "anonymous" }
new Script(Constants.BootstrapScriptUrl, Constants.BootstrapScriptIntegrity, "anonymous")
}
};

View File

@@ -13,9 +13,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.5" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.5" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.7" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.7" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.7" />
</ItemGroup>
<ItemGroup>

View File

@@ -75,6 +75,10 @@ app {
color: gray;
}
.app-moduleactions .dropdown-menu {
z-index: 9999;
}
.app-moduleactions .dropdown-submenu {
position: relative;
}
@@ -281,4 +285,4 @@ app {
.gdpr-consent-bar .btn-hide{
top: 0;
right: 5px;
}
}

View File

@@ -311,7 +311,7 @@ Oqtane.Interop = {
}
return files;
},
uploadFiles: async function (posturl, folder, id, antiforgerytoken, jwt, chunksize) {
uploadFiles: async function (posturl, folder, id, antiforgerytoken, jwt, chunksize, anonymizeuploadfilenames) {
var success = true;
var fileinput = document.getElementById('FileInput_' + id);
var progressinfo = document.getElementById('ProgressInfo_' + id);
@@ -344,16 +344,22 @@ Oqtane.Interop = {
const totalParts = Math.ceil(file.size / chunkSize);
let partCount = 0;
let filename = file.name;
if (anonymizeuploadfilenames) {
filename = crypto.randomUUID() + '.' + filename.split('.').pop();
}
const uploadPart = () => {
const start = partCount * chunkSize;
const end = Math.min(start + chunkSize, file.size);
const chunk = file.slice(start, end);
return new Promise((resolve, reject) => {
let formdata = new FormData();
formdata.append('__RequestVerificationToken', antiforgerytoken);
formdata.append('folder', folder);
formdata.append('formfile', chunk, file.name);
formdata.append('formfile', chunk, filename);
var credentials = 'same-origin';
var headers = new Headers();

View File

@@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Configurations>Debug;Release</Configurations>
<Version>6.1.3</Version>
<Version>6.1.4</Version>
<Product>Oqtane</Product>
<Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company>
@@ -11,7 +11,7 @@
<Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.3</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.4</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<RootNamespace>Oqtane</RootNamespace>
@@ -19,11 +19,12 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.7" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.7" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.7" />
<PackageReference Include="NodaTime" Version="3.2.2" />
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
<PackageReference Include="System.Text.Json" Version="9.0.5" />
<PackageReference Include="System.Text.Json" Version="9.0.7" />
</ItemGroup>
</Project>

View File

@@ -4,8 +4,8 @@ namespace Oqtane.Shared
{
public class Constants
{
public static readonly string Version = "6.1.3";
public const string ReleaseVersions = "1.0.0,1.0.1,1.0.2,1.0.3,1.0.4,2.0.0,2.0.1,2.0.2,2.1.0,2.2.0,2.3.0,2.3.1,3.0.0,3.0.1,3.0.2,3.0.3,3.1.0,3.1.1,3.1.2,3.1.3,3.1.4,3.2.0,3.2.1,3.3.0,3.3.1,3.4.0,3.4.1,3.4.2,3.4.3,4.0.0,4.0.1,4.0.2,4.0.3,4.0.4,4.0.5,4.0.6,5.0.0,5.0.1,5.0.2,5.0.3,5.1.0,5.1.1,5.1.2,5.2.0,5.2.1,5.2.2,5.2.3,5.2.4,6.0.0,6.0.1,6.1.0,6.1.1,6.1.2,6.1.3";
public static readonly string Version = "6.1.4";
public const string ReleaseVersions = "1.0.0,1.0.1,1.0.2,1.0.3,1.0.4,2.0.0,2.0.1,2.0.2,2.1.0,2.2.0,2.3.0,2.3.1,3.0.0,3.0.1,3.0.2,3.0.3,3.1.0,3.1.1,3.1.2,3.1.3,3.1.4,3.2.0,3.2.1,3.3.0,3.3.1,3.4.0,3.4.1,3.4.2,3.4.3,4.0.0,4.0.1,4.0.2,4.0.3,4.0.4,4.0.5,4.0.6,5.0.0,5.0.1,5.0.2,5.0.3,5.1.0,5.1.1,5.1.2,5.2.0,5.2.1,5.2.2,5.2.3,5.2.4,6.0.0,6.0.1,6.1.0,6.1.1,6.1.2,6.1.3,6.1.4";
public const string PackageId = "Oqtane.Framework";
public const string ClientId = "Oqtane.Client";
public const string UpdaterPackageId = "Oqtane.Updater";
@@ -86,10 +86,10 @@ namespace Oqtane.Shared
public static readonly string[] InternalPagePaths = { "login", "register", "reset", "404" };
public const string DefaultTextEditor = "Oqtane.Modules.Controls.QuillJSTextEditor, Oqtane.Client";
public const string BootstrapScriptUrl = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/js/bootstrap.bundle.min.js";
public const string BootstrapScriptIntegrity = "sha512-7Pi/otdlbbCR+LnW+F7PwFcSDJOuUJB3OxtEHbg4vSMvzvJjde4Po1v4BR9Gdc9aXNUNFVUY+SK51wWT8WF0Gg==";
public const string BootstrapStylesheetUrl = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css";
public const string BootstrapStylesheetIntegrity = "sha512-jnSuA4Ss2PkkikSOLtYs8BlYIeeIK1h99ty4YfvRPAlzr377vr3CXDb7sb7eEEBYjDtcYj+AjBH3FLv5uSJuXg==";
public const string BootstrapScriptUrl = "https://cdn.jsdelivr.net/npm/bootstrap@5.3.5/dist/js/bootstrap.bundle.min.js";
public const string BootstrapScriptIntegrity = "sha384-k6d4wzSIapyDyv1kpU366/PK5hCdSbCRGRCMv+eplOQJWyd1fbcAu9OCUj5zNLiq";
public const string BootstrapStylesheetUrl = "https://cdn.jsdelivr.net/npm/bootstrap@5.3.5/dist/css/bootstrap.min.css";
public const string BootstrapStylesheetIntegrity = "sha384-SgOJa3DmI69IUzQ2PVdRZhwQ+dy64/BUtbMJw1MZ8t5HZApcHrRKUc4W0kG879m7";
public const string CookieConsentCookieName = "Oqtane.CookieConsent";
public const string CookieConsentCookieValue = "yes";

View File

@@ -71,13 +71,15 @@ namespace Oqtane.Shared
{ "ExternalLogin:ProviderUrl", "https://developers.facebook.com" },
{ "ExternalLogin:ProviderType", "oauth2" },
{ "ExternalLogin:ProviderName", "Facebook" },
{ "ExternalLogin:AuthorizationUrl", "https://www.facebook.com/v18.0/dialog/oauth" },
{ "ExternalLogin:TokenUrl", "https://graph.facebook.com/v18.0/oauth/access_token" },
{ "ExternalLogin:UserInfoUrl", "https://graph.facebook.com/v18.0/me" },
{ "ExternalLogin:AuthorizationUrl", "https://www.facebook.com/v23.0/dialog/oauth" },
{ "ExternalLogin:TokenUrl", "https://graph.facebook.com/v23.0/oauth/access_token" },
{ "ExternalLogin:UserInfoUrl", "https://graph.facebook.com/v23.0/me?fields=id,name,email" },
{ "ExternalLogin:ClientId", "YOUR CLIENT ID" },
{ "ExternalLogin:ClientSecret", "YOUR CLIENT SECRET" },
{ "ExternalLogin:Scopes", "public_profile" },
{ "ExternalLogin:IdentifierClaimType", "id" }
{ "ExternalLogin:Scopes", "public_profile,email" },
{ "ExternalLogin:IdentifierClaimType", "id" },
{ "ExternalLogin:NameClaimType", "name" },
{ "ExternalLogin:EmailClaimType", "email" }
}
}
};

View File

@@ -1,4 +1,3 @@
using Oqtane.Models;
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -7,7 +6,14 @@ using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using NodaTime;
using NodaTime.Extensions;
using Oqtane.Models;
using File = Oqtane.Models.File;
using TimeZone = Oqtane.Models.TimeZone;
namespace Oqtane.Shared
{
@@ -505,6 +511,7 @@ namespace Oqtane.Shared
return $"[{@class.GetType()}] {message}";
}
//Time conversions with TimeZoneInfo
public static DateTime? LocalDateAndTimeAsUtc(DateTime? date, string time, TimeZoneInfo localTimeZone = null)
{
if (date != null && !string.IsNullOrEmpty(time) && TimeSpan.TryParse(time, out TimeSpan timespan))
@@ -581,6 +588,120 @@ namespace Oqtane.Shared
return (localDateTime?.Date, localTime);
}
//Time conversions with NodaTime (IANA) timezoneId
public static DateTime? LocalDateAndTimeAsUtc(DateTime? date, string time, string localTimeZoneId)
{
if (date != null && !string.IsNullOrEmpty(time) && TimeSpan.TryParse(time, out TimeSpan timespan))
{
return LocalDateAndTimeAsUtc(date.Value.Date.Add(timespan), localTimeZoneId);
}
return null;
}
public static DateTime? LocalDateAndTimeAsUtc(DateTime? date, DateTime? time, string localTimeZoneId)
{
if (date != null)
{
if (time != null)
{
return LocalDateAndTimeAsUtc(date.Value.Date.Add(time.Value.TimeOfDay), localTimeZoneId);
}
return LocalDateAndTimeAsUtc(date.Value.Date, localTimeZoneId);
}
return null;
}
public static DateTime? LocalDateAndTimeAsUtc(DateTime? date, string localTimeZoneId)
{
if (date != null)
{
DateTimeZone localTimeZone;
if (!string.IsNullOrEmpty(localTimeZoneId))
{
localTimeZone = DateTimeZoneProviders.Tzdb.GetZoneOrNull(localTimeZoneId) ?? DateTimeZoneProviders.Tzdb.GetSystemDefault();
}
else
{
localTimeZone = DateTimeZoneProviders.Tzdb.GetSystemDefault();
}
var localDateTime = LocalDateTime.FromDateTime(date.Value);
return localTimeZone.AtLeniently(localDateTime).ToDateTimeUtc();
}
return null;
}
public static DateTime? UtcAsLocalDate(DateTime? dateTime, string timeZoneId)
{
return UtcAsLocalDateAndTime(dateTime, timeZoneId).date;
}
public static DateTime? UtcAsLocalDateTime(DateTime? dateTime, string timeZoneId)
{
var result = UtcAsLocalDateAndTime(dateTime, timeZoneId);
if (result.date != null && !string.IsNullOrEmpty(result.time) && TimeSpan.TryParse(result.time, out TimeSpan timespan))
{
result.date = result.date.Value.Add(timespan);
}
return result.date;
}
public static (DateTime? date, string time) UtcAsLocalDateAndTime(DateTime? dateTime, string timeZoneId)
{
DateTimeZone localTimeZone;
if (!string.IsNullOrEmpty(timeZoneId))
{
localTimeZone = DateTimeZoneProviders.Tzdb.GetZoneOrNull(timeZoneId) ?? DateTimeZoneProviders.Tzdb.GetSystemDefault();
}
else
{
localTimeZone = DateTimeZoneProviders.Tzdb.GetSystemDefault();
}
DateTime? localDateTime = null;
string localTime = string.Empty;
if (dateTime.HasValue && dateTime?.Kind != DateTimeKind.Local)
{
Instant instant;
if (dateTime?.Kind == DateTimeKind.Unspecified)
{
// Treat Unspecified as Utc not Local. This is due to EF Core, on some databases, after retrieval will have DateTimeKind as Unspecified.
// All values in database should be UTC.
// Normal .net conversion treats Unspecified as local.
// https://docs.microsoft.com/en-us/dotnet/api/system.timezoneinfo.converttime?view=net-6.0
instant = Instant.FromDateTimeUtc(new DateTime(dateTime.Value.Ticks, DateTimeKind.Utc));
}
else
{
instant = Instant.FromDateTimeUtc(dateTime.Value);
}
localDateTime = instant.InZone(localTimeZone).ToDateTimeOffset().DateTime;
}
if (localDateTime != null && localDateTime.Value.TimeOfDay.TotalSeconds != 0)
{
localTime = localDateTime.Value.ToString("HH:mm");
}
return (localDateTime?.Date, localTime);
}
public static List<TimeZone> GetTimeZones()
{
return [.. DateTimeZoneProviders.Tzdb.GetAllZones()
.Select(tz => new TimeZone()
{
Id = tz.Id,
DisplayName = tz.ToString()
})];
}
public static bool IsEffectiveAndNotExpired(DateTime? effectiveDate, DateTime? expiryDate)
{
DateTime currentUtcTime = DateTime.UtcNow;

View File

@@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<OutputType>Exe</OutputType>
<Version>6.1.3</Version>
<Version>6.1.4</Version>
<Product>Oqtane</Product>
<Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company>
@@ -11,7 +11,7 @@
<Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.3</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.4</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<RootNamespace>Oqtane</RootNamespace>