_packages;
private string _price = "free";
+ private string _sort = "popularity";
private string _search = "";
private string _productname = "";
private string _packageid = "";
@@ -131,6 +178,7 @@
try
{
await LoadModuleDefinitions();
+ _initialized = true;
}
catch (Exception ex)
{
@@ -141,8 +189,10 @@
private async Task LoadModuleDefinitions()
{
+ ShowProgressIndicator();
+
var moduledefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId);
- _packages = await PackageService.GetPackagesAsync("module", _search, _price, "");
+ _packages = await PackageService.GetPackagesAsync("module", _search, _price, "", _sort);
if (_packages != null)
{
@@ -154,21 +204,22 @@
}
}
}
+
+ HideProgressIndicator();
}
- private async void PriceChanged(ChangeEventArgs e)
+ private string GetLogo(int fileid)
{
- try
- {
- _price = (string)e.Value;
- _search = "";
- await LoadModuleDefinitions();
- StateHasChanged();
- }
- catch (Exception ex)
- {
- await logger.LogError(ex, "Error On PriceChanged");
- }
+ var url = ImageUrl(fileid, 100, 100);
+ url = (!string.IsNullOrEmpty(PageState.Alias.Path)) ? url.Substring(PageState.Alias.Path.Length + 1) : url;
+ return Constants.PackageRegistryUrl + url;
+ }
+
+ private async void PriceChanged(string price)
+ {
+ _price = price;
+ await LoadModuleDefinitions();
+ StateHasChanged();
}
private async Task Search()
@@ -196,6 +247,12 @@
}
}
+ private async void SortChanged(ChangeEventArgs e)
+ {
+ _sort = (string)e.Value;
+ await LoadModuleDefinitions();
+ }
+
private void HideModal()
{
_productname = "";
diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor
index 57380333..751a9312 100644
--- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor
+++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Index.razor
@@ -80,7 +80,7 @@ else
}
- @((MarkupString)SupportLink(context.PackageName))
+ @((MarkupString)SupportLink(context.PackageName, context.Version))
|
@((MarkupString)PurchaseLink(context.PackageName))
@@ -145,7 +145,7 @@ else
return link;
}
- private string SupportLink(string packagename)
+ private string SupportLink(string packagename, string version)
{
string link = "";
if (!string.IsNullOrEmpty(packagename) && _packages != null)
@@ -153,7 +153,7 @@ else
var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault();
if (package != null && !string.IsNullOrEmpty(package.SupportUrl))
{
- link += "" + SharedLocalizer["Help"] + "";
+ link += "" + SharedLocalizer["Help"] + "";
}
}
return link;
diff --git a/Oqtane.Client/Modules/Admin/Modules/Export.razor b/Oqtane.Client/Modules/Admin/Modules/Export.razor
index 6afb875b..fc1ca548 100644
--- a/Oqtane.Client/Modules/Admin/Modules/Export.razor
+++ b/Oqtane.Client/Modules/Admin/Modules/Export.razor
@@ -22,6 +22,11 @@
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
public override string Title => "Export Content";
+ protected override void OnParametersSet()
+ {
+ base.OnParametersSet();
+ base.SetModuleTitle(Localizer["ModuleTitle.Text"]);
+ }
private async Task ExportModule()
{
diff --git a/Oqtane.Client/Modules/Admin/Modules/Import.razor b/Oqtane.Client/Modules/Admin/Modules/Import.razor
index 961157eb..83468d1e 100644
--- a/Oqtane.Client/Modules/Admin/Modules/Import.razor
+++ b/Oqtane.Client/Modules/Admin/Modules/Import.razor
@@ -28,6 +28,11 @@
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
public override string Title => "Import Content";
+ protected override void OnParametersSet()
+ {
+ base.OnParametersSet();
+ base.SetModuleTitle(Localizer["ModuleTitle.Text"]);
+ }
private async Task ImportModule()
{
validated = true;
diff --git a/Oqtane.Client/Modules/Admin/Modules/Settings.razor b/Oqtane.Client/Modules/Admin/Modules/Settings.razor
index 9798c2f9..fe01bca9 100644
--- a/Oqtane.Client/Modules/Admin/Modules/Settings.razor
+++ b/Oqtane.Client/Modules/Admin/Modules/Settings.razor
@@ -130,6 +130,11 @@
private string modifiedby;
private DateTime modifiedon;
+ protected override void OnParametersSet()
+ {
+ base.OnParametersSet();
+ base.SetModuleTitle(Localizer["ModuleTitle.Text"]);
+ }
protected override void OnInitialized()
{
_module = ModuleState.ModuleDefinition.Name;
diff --git a/Oqtane.Client/Modules/Admin/Pages/Add.razor b/Oqtane.Client/Modules/Admin/Pages/Add.razor
index c91be528..647077d1 100644
--- a/Oqtane.Client/Modules/Admin/Pages/Add.razor
+++ b/Oqtane.Client/Modules/Admin/Pages/Add.razor
@@ -10,7 +10,7 @@
{
|
- @((MarkupString)SupportLink(context.PackageName))
+ @((MarkupString)SupportLink(context.PackageName, context.Version))
|
@((MarkupString)PurchaseLink(context.PackageName))
@@ -112,7 +112,7 @@ else
return link;
}
- private string SupportLink(string packagename)
+ private string SupportLink(string packagename, string version)
{
string link = "";
if (!string.IsNullOrEmpty(packagename) && _packages != null)
@@ -120,7 +120,7 @@ else
var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault();
if (package != null && !string.IsNullOrEmpty(package.SupportUrl))
{
- link += "" + SharedLocalizer["Help"] + "";
+ link += "" + SharedLocalizer["Help"] + "";
}
}
return link;
diff --git a/Oqtane.Client/Modules/Admin/Upgrade/Index.razor b/Oqtane.Client/Modules/Admin/Upgrade/Index.razor
index dbc733e0..5bf8d48e 100644
--- a/Oqtane.Client/Modules/Admin/Upgrade/Index.razor
+++ b/Oqtane.Client/Modules/Admin/Upgrade/Index.razor
@@ -17,11 +17,11 @@
}
else
{
-
+
}
-
+
diff --git a/Oqtane.Client/Modules/Admin/UserProfile/Add.razor b/Oqtane.Client/Modules/Admin/UserProfile/Add.razor
index ceeb2713..fb3c8209 100644
--- a/Oqtane.Client/Modules/Admin/UserProfile/Add.razor
+++ b/Oqtane.Client/Modules/Admin/UserProfile/Add.razor
@@ -42,6 +42,12 @@
public override string Title => "Send Notification";
+ protected override void OnParametersSet()
+ {
+ base.OnParametersSet();
+ base.SetModuleTitle(Localizer["ModuleTitle.Text"]);
+ }
+
private async Task Send()
{
try
diff --git a/Oqtane.Client/Modules/Admin/UserProfile/View.razor b/Oqtane.Client/Modules/Admin/UserProfile/View.razor
index 58a3d211..719b8813 100644
--- a/Oqtane.Client/Modules/Admin/UserProfile/View.razor
+++ b/Oqtane.Client/Modules/Admin/UserProfile/View.razor
@@ -110,6 +110,11 @@
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.View;
public override string Title => "View Notification";
+ protected override void OnParametersSet()
+ {
+ base.OnParametersSet();
+ base.SetModuleTitle(Localizer["ModuleTitle.Text"]);
+ }
protected override async Task OnInitializedAsync()
{
try
diff --git a/Oqtane.Client/Modules/Admin/Users/Add.razor b/Oqtane.Client/Modules/Admin/Users/Add.razor
index 54c396ee..f36e78b0 100644
--- a/Oqtane.Client/Modules/Admin/Users/Add.razor
+++ b/Oqtane.Client/Modules/Admin/Users/Add.razor
@@ -140,34 +140,26 @@
{
if (_password == confirm)
{
- var user = await UserService.GetUserAsync(username, PageState.Site.SiteId);
- if (user == null)
+ var user = new User();
+ user.SiteId = PageState.Site.SiteId;
+ user.Username = username;
+ user.Password = _password;
+ user.Email = email;
+ user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname;
+ user.PhotoFileId = null;
+
+ user = await UserService.AddUserAsync(user);
+
+ if (user != null)
{
- user = new User();
- user.SiteId = PageState.Site.SiteId;
- user.Username = username;
- user.Password = _password;
- user.Email = email;
- user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname;
- user.PhotoFileId = null;
-
- user = await UserService.AddUserAsync(user);
-
- if (user != null)
- {
- await SettingService.UpdateUserSettingsAsync(settings, user.UserId);
- await logger.LogInformation("User Created {User}", user);
- NavigationManager.NavigateTo(NavigateUrl());
- }
- else
- {
- await logger.LogError("Error Adding User {Username} {Email}", username, email);
- AddModuleMessage(Localizer["Error.User.AddCheckPass"], MessageType.Error);
- }
+ await SettingService.UpdateUserSettingsAsync(settings, user.UserId);
+ await logger.LogInformation("User Created {User}", user);
+ NavigationManager.NavigateTo(NavigateUrl());
}
else
{
- AddModuleMessage(Localizer["Message.Username.Exists"], MessageType.Warning);
+ await logger.LogError("Error Adding User {Username} {Email}", username, email);
+ AddModuleMessage(Localizer["Error.User.AddCheckPass"], MessageType.Error);
}
}
else
diff --git a/Oqtane.Client/Modules/Controls/AutoComplete.razor b/Oqtane.Client/Modules/Controls/AutoComplete.razor
index b464fd20..89e74124 100644
--- a/Oqtane.Client/Modules/Controls/AutoComplete.razor
+++ b/Oqtane.Client/Modules/Controls/AutoComplete.razor
@@ -2,7 +2,7 @@
@inherits LocalizableComponent
-
+
@if (_results != null)
{
@code {
- Dictionary _results;
+ Dictionary _results;
+ Dictionary InputAttributes { get; set; } = new();
- [Parameter]
- public Func>> OnSearch { get; set; } // required - an async delegate method which accepts a filter string parameter and returns a dictionary
+ [Parameter]
+ public Func>> OnSearch { get; set; } // required - an async delegate method which accepts a filter string parameter and returns a dictionary
- [Parameter]
- public int Characters { get; set; } = 3; // optional - number of characters before search is initiated
+ [Parameter]
+ public int Characters { get; set; } = 3; // optional - number of characters before search is initiated
- [Parameter]
- public int Rows { get; set; } = 3; // optional - number of result rows to display
+ [Parameter]
+ public int Rows { get; set; } = 3; // optional - number of result rows to display
- [Parameter]
- public string Placeholder { get; set; } // optional - placeholder input text
+ [Parameter]
+ public string Placeholder { get; set; } // optional - placeholder input text
- [Parameter]
- public string Value { get; set; } // value of item selected
+ [Parameter]
+ public string Value { get; set; } // value of item selected
- [Parameter]
- public string Key { get; set; } // key of item selected
+ [Parameter]
+ public string Key { get; set; } // key of item selected
- private async Task OnInput(ChangeEventArgs e)
+ [Parameter]
+ public bool Required { get; set; } // optional - if the item is required
+
+ protected override void OnParametersSet()
+ {
+ if (Required)
+ {
+ if (!InputAttributes.ContainsKey(nameof(Required)))
+ {
+ InputAttributes.Add(nameof(Required), true);
+ }
+ }
+ else
+ {
+ if (InputAttributes.ContainsKey(nameof(Required)))
+ {
+ InputAttributes.Remove(nameof(Required));
+ }
+ }
+ }
+ private async Task OnInput(ChangeEventArgs e)
{
Value = e.Value?.ToString();
if (Value?.Length >= Characters)
diff --git a/Oqtane.Client/Modules/Controls/FileManager.razor b/Oqtane.Client/Modules/Controls/FileManager.razor
index c37cef54..17ed8f81 100644
--- a/Oqtane.Client/Modules/Controls/FileManager.razor
+++ b/Oqtane.Client/Modules/Controls/FileManager.razor
@@ -168,8 +168,6 @@
ShowSuccess = true;
}
- _folders = await FolderService.GetFoldersAsync(ModuleState.SiteId);
-
if (!string.IsNullOrEmpty(Folder) && Folder != Constants.PackagesFolder)
{
Folder folder = await FolderService.GetFolderAsync(ModuleState.SiteId, Folder);
@@ -185,6 +183,22 @@
}
}
+ if (ShowFolders)
+ {
+ _folders = await FolderService.GetFoldersAsync(ModuleState.SiteId);
+ }
+ else
+ {
+ if (FolderId != -1)
+ {
+ var folder = await FolderService.GetFolderAsync(FolderId);
+ if (folder != null)
+ {
+ _folders = new List { folder };
+ }
+ }
+ }
+
if (FileId != -1)
{
File file = await FileService.GetFileAsync(FileId);
@@ -358,10 +372,21 @@
attempts += 1;
Thread.Sleep(1000 * attempts); // progressive retry
- var file = await FileService.GetFileAsync(int.Parse(folder), uploads[upload]);
- if (file != null)
+ if (Folder == Constants.PackagesFolder)
{
- success = true;
+ var files = await FileService.GetFilesAsync(folder);
+ if (files != null && files.Any(item => item.Name == uploads[upload]))
+ {
+ success = true;
+ }
+ }
+ else
+ {
+ var file = await FileService.GetFileAsync(int.Parse(folder), uploads[upload]);
+ if (file != null)
+ {
+ success = true;
+ }
}
}
if (success)
diff --git a/Oqtane.Client/Modules/Controls/Section.razor b/Oqtane.Client/Modules/Controls/Section.razor
index 22737d6c..91f91724 100644
--- a/Oqtane.Client/Modules/Controls/Section.razor
+++ b/Oqtane.Client/Modules/Controls/Section.razor
@@ -42,6 +42,8 @@
protected override void OnParametersSet()
{
+ base.OnParametersSet(); // must be included to call method in LocalizableComponent
+
_heading = !string.IsNullOrEmpty(Heading) ? Localize(nameof(Heading), Heading) : Localize(nameof(Name), Name);
_expanded = (!string.IsNullOrEmpty(Expanded)) ? Expanded.ToLower() : "false";
if (_expanded == "true") { _show = "show"; }
diff --git a/Oqtane.Client/Modules/HtmlText/Edit.razor b/Oqtane.Client/Modules/HtmlText/Edit.razor
index 72a9662c..6c1b59a4 100644
--- a/Oqtane.Client/Modules/HtmlText/Edit.razor
+++ b/Oqtane.Client/Modules/HtmlText/Edit.razor
@@ -68,6 +68,12 @@
private List _htmltexts;
private string _view = "";
+ protected override void OnParametersSet()
+ {
+ base.OnParametersSet();
+ base.SetModuleTitle(Localizer["ModuleTitle.Text"]);
+ }
+
protected override async Task OnInitializedAsync()
{
try
diff --git a/Oqtane.Client/Oqtane.Client.csproj b/Oqtane.Client/Oqtane.Client.csproj
index ae3e2589..f2f659e1 100644
--- a/Oqtane.Client/Oqtane.Client.csproj
+++ b/Oqtane.Client/Oqtane.Client.csproj
@@ -4,7 +4,7 @@
net7.0
Exe
Debug;Release
- 4.0.1
+ 4.0.2
Oqtane
Shaun Walker
.NET Foundation
@@ -12,7 +12,7 @@
.NET Foundation
https://www.oqtane.org
https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE
- https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1
+ https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2
https://github.com/oqtane/oqtane.framework
Git
Oqtane
diff --git a/Oqtane.Client/Program.cs b/Oqtane.Client/Program.cs
index e6927c1b..0c5f80d7 100644
--- a/Oqtane.Client/Program.cs
+++ b/Oqtane.Client/Program.cs
@@ -1,19 +1,23 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
+using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Runtime.Loader;
using System.Text.Json;
using System.Threading.Tasks;
+using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.AspNetCore.Localization;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.JSInterop;
using Oqtane.Documentation;
+using Oqtane.Models;
using Oqtane.Modules;
using Oqtane.Services;
using Oqtane.UI;
@@ -65,6 +69,14 @@ namespace Oqtane.Client
private static async Task LoadClientAssemblies(HttpClient http, IServiceProvider serviceProvider)
{
+ // get alias
+ var navigationManager = serviceProvider.GetRequiredService();
+ var urlpath = GetUrlPath(navigationManager.Uri);
+ var json = await http.GetStringAsync($"api/Installation/installed/?path={WebUtility.UrlEncode(urlpath)}");
+ var installation = JsonSerializer.Deserialize(json, new JsonSerializerOptions(JsonSerializerDefaults.Web));
+ urlpath = installation.Alias.Path;
+ urlpath = (!string.IsNullOrEmpty(urlpath)) ? urlpath + "/" : urlpath;
+
var dlls = new Dictionary();
var pdbs = new Dictionary();
var list = new List();
@@ -76,7 +88,7 @@ namespace Oqtane.Client
if (files.Count() != 0)
{
// get list of assemblies from server
- var json = await http.GetStringAsync("/api/Installation/list");
+ json = await http.GetStringAsync($"{urlpath}api/Installation/list");
var assemblies = JsonSerializer.Deserialize>(json);
// determine which assemblies need to be downloaded
@@ -138,7 +150,7 @@ namespace Oqtane.Client
if (list.Count != 0)
{
// get assemblies from server and load into client app domain
- var zip = await http.GetByteArrayAsync($"/api/Installation/load?list=" + string.Join(",", list));
+ var zip = await http.GetByteArrayAsync($"{urlpath}api/Installation/load?list=" + string.Join(",", list));
// asemblies and debug symbols are packaged in a zip file
using (ZipArchive archive = new ZipArchive(new MemoryStream(zip)))
@@ -254,5 +266,10 @@ namespace Oqtane.Client
CultureInfo.DefaultThreadCurrentCulture = cultureInfo;
CultureInfo.DefaultThreadCurrentUICulture = cultureInfo;
}
+
+ private static string GetUrlPath(string url)
+ {
+ return new Uri(url).AbsolutePath.Substring(1);
+ }
}
}
diff --git a/Oqtane.Client/Resources/Modules/Admin/Files/Add.resx b/Oqtane.Client/Resources/Modules/Admin/Files/Add.resx
index 67845d7b..235c5130 100644
--- a/Oqtane.Client/Resources/Modules/Admin/Files/Add.resx
+++ b/Oqtane.Client/Resources/Modules/Admin/Files/Add.resx
@@ -162,4 +162,7 @@
Name:
+
+ Download Files
+
\ No newline at end of file
diff --git a/Oqtane.Client/Resources/Modules/Admin/Files/Details.resx b/Oqtane.Client/Resources/Modules/Admin/Files/Details.resx
index 84ddc423..65be9d58 100644
--- a/Oqtane.Client/Resources/Modules/Admin/Files/Details.resx
+++ b/Oqtane.Client/Resources/Modules/Admin/Files/Details.resx
@@ -150,4 +150,7 @@
Description:
+
+ File Management
+
\ No newline at end of file
diff --git a/Oqtane.Client/Resources/Modules/Admin/Files/Edit.resx b/Oqtane.Client/Resources/Modules/Admin/Files/Edit.resx
index 81d6a5a6..643c66ae 100644
--- a/Oqtane.Client/Resources/Modules/Admin/Files/Edit.resx
+++ b/Oqtane.Client/Resources/Modules/Admin/Files/Edit.resx
@@ -183,4 +183,16 @@
Image Sizes:
+
+ Folder Management!
+
+
+ Private
+
+
+ Public
+
+
+ Folder Management
+
\ No newline at end of file
diff --git a/Oqtane.Client/Resources/Modules/Admin/Jobs/Index.resx b/Oqtane.Client/Resources/Modules/Admin/Jobs/Index.resx
index a4d0f7f2..381bae6d 100644
--- a/Oqtane.Client/Resources/Modules/Admin/Jobs/Index.resx
+++ b/Oqtane.Client/Resources/Modules/Admin/Jobs/Index.resx
@@ -195,4 +195,7 @@
Please Note That After An Initial Installation You Must <a href={0}>Restart</a> The Application In Order To Activate The Default Scheduled Jobs.
+
+ Refresh
+
\ No newline at end of file
diff --git a/Oqtane.Client/Resources/Modules/Admin/ModuleDefinitions/Add.resx b/Oqtane.Client/Resources/Modules/Admin/ModuleDefinitions/Add.resx
index 9db1c83a..037e700c 100644
--- a/Oqtane.Client/Resources/Modules/Admin/ModuleDefinitions/Add.resx
+++ b/Oqtane.Client/Resources/Modules/Admin/ModuleDefinitions/Add.resx
@@ -136,9 +136,12 @@
No Modules Match The Criteria Provided Or Package Service Is Disabled
- Download
+ Marketplace
Upload
+
+ Product
+
\ No newline at end of file
diff --git a/Oqtane.Client/Resources/Modules/Admin/ModuleDefinitions/Index.resx b/Oqtane.Client/Resources/Modules/Admin/ModuleDefinitions/Index.resx
index 9e3b9cc2..0ff2e74d 100644
--- a/Oqtane.Client/Resources/Modules/Admin/ModuleDefinitions/Index.resx
+++ b/Oqtane.Client/Resources/Modules/Admin/ModuleDefinitions/Index.resx
@@ -144,6 +144,9 @@
Delete Module
+
+ Delete
+
In Use?
diff --git a/Oqtane.Client/Resources/Modules/Admin/Modules/Export.resx b/Oqtane.Client/Resources/Modules/Admin/Modules/Export.resx
index 2e6a713f..80a69d3b 100644
--- a/Oqtane.Client/Resources/Modules/Admin/Modules/Export.resx
+++ b/Oqtane.Client/Resources/Modules/Admin/Modules/Export.resx
@@ -132,4 +132,7 @@
Content Exported Successfully
+
+ Export Content
+
\ No newline at end of file
diff --git a/Oqtane.Client/Resources/Modules/Admin/Modules/Import.resx b/Oqtane.Client/Resources/Modules/Admin/Modules/Import.resx
index acbfc2ab..d91d9e49 100644
--- a/Oqtane.Client/Resources/Modules/Admin/Modules/Import.resx
+++ b/Oqtane.Client/Resources/Modules/Admin/Modules/Import.resx
@@ -138,4 +138,7 @@
You Must Enter Some Content To Import
+
+ Import Content
+
\ No newline at end of file
diff --git a/Oqtane.Client/Resources/Modules/Admin/Modules/Settings.resx b/Oqtane.Client/Resources/Modules/Admin/Modules/Settings.resx
index db947070..03299510 100644
--- a/Oqtane.Client/Resources/Modules/Admin/Modules/Settings.resx
+++ b/Oqtane.Client/Resources/Modules/Admin/Modules/Settings.resx
@@ -156,4 +156,7 @@
Module:
+
+ Module Settings
+
\ No newline at end of file
diff --git a/Oqtane.Client/Resources/Modules/Admin/Pages/Add.resx b/Oqtane.Client/Resources/Modules/Admin/Pages/Add.resx
index 20b0c41f..62972b72 100644
--- a/Oqtane.Client/Resources/Modules/Admin/Pages/Add.resx
+++ b/Oqtane.Client/Resources/Modules/Admin/Pages/Add.resx
@@ -249,4 +249,10 @@
Please Note That Overriding The Default Site Theme With An Unrelated Page Theme May Result In Compatibility Issues For Your Site
+
+ Permissions
+
+
+ Theme Settings
+
\ No newline at end of file
diff --git a/Oqtane.Client/Resources/Modules/Admin/RecycleBin/Index.resx b/Oqtane.Client/Resources/Modules/Admin/RecycleBin/Index.resx
index 25184660..c45ee304 100644
--- a/Oqtane.Client/Resources/Modules/Admin/RecycleBin/Index.resx
+++ b/Oqtane.Client/Resources/Modules/Admin/RecycleBin/Index.resx
@@ -120,9 +120,15 @@
Delete Module
+
+ Delete
+
Delete Page
+
+ Delete
+
No Deleted Pages
diff --git a/Oqtane.Client/Resources/Modules/Admin/Themes/Add.resx b/Oqtane.Client/Resources/Modules/Admin/Themes/Add.resx
index b7a18332..9ceeae29 100644
--- a/Oqtane.Client/Resources/Modules/Admin/Themes/Add.resx
+++ b/Oqtane.Client/Resources/Modules/Admin/Themes/Add.resx
@@ -135,4 +135,10 @@
No Themes Match The Criteria Provided Or Package Service Is Disabled
+
+ Marketplace
+
+
+ Upload
+
\ No newline at end of file
diff --git a/Oqtane.Client/Resources/Modules/Admin/Themes/Index.resx b/Oqtane.Client/Resources/Modules/Admin/Themes/Index.resx
index 73d2a5ad..3c5074b4 100644
--- a/Oqtane.Client/Resources/Modules/Admin/Themes/Index.resx
+++ b/Oqtane.Client/Resources/Modules/Admin/Themes/Index.resx
@@ -141,6 +141,9 @@
Create Theme
+
+ Install Theme
+
View
diff --git a/Oqtane.Client/Resources/Modules/Admin/Upgrade/Index.resx b/Oqtane.Client/Resources/Modules/Admin/Upgrade/Index.resx
index a6f7fa40..5f1fa146 100644
--- a/Oqtane.Client/Resources/Modules/Admin/Upgrade/Index.resx
+++ b/Oqtane.Client/Resources/Modules/Admin/Upgrade/Index.resx
@@ -141,4 +141,10 @@
Upload
+
+ Framework Is Already Up To Date
+
+
+ Upload A Framework Package (Oqtane.Framework.version.nupkg) And Then Select Upgrade
+
\ No newline at end of file
diff --git a/Oqtane.Client/Resources/Modules/Admin/UserProfile/Add.resx b/Oqtane.Client/Resources/Modules/Admin/UserProfile/Add.resx
index 3666102c..39d179fe 100644
--- a/Oqtane.Client/Resources/Modules/Admin/UserProfile/Add.resx
+++ b/Oqtane.Client/Resources/Modules/Admin/UserProfile/Add.resx
@@ -141,4 +141,7 @@
Subject:
+
+ Send Notification
+
\ No newline at end of file
diff --git a/Oqtane.Client/Resources/Modules/Admin/UserProfile/View.resx b/Oqtane.Client/Resources/Modules/Admin/UserProfile/View.resx
index 47a59876..528b6689 100644
--- a/Oqtane.Client/Resources/Modules/Admin/UserProfile/View.resx
+++ b/Oqtane.Client/Resources/Modules/Admin/UserProfile/View.resx
@@ -144,4 +144,7 @@
Original Message
+
+ View Notification
+
\ No newline at end of file
diff --git a/Oqtane.Client/Resources/Modules/HtmlText/Edit.resx b/Oqtane.Client/Resources/Modules/HtmlText/Edit.resx
index 5e1d2160..c0e62bdc 100644
--- a/Oqtane.Client/Resources/Modules/HtmlText/Edit.resx
+++ b/Oqtane.Client/Resources/Modules/HtmlText/Edit.resx
@@ -156,6 +156,9 @@
Version Restored
+
+ Edit Html/Text
+
Restore Version
diff --git a/Oqtane.Client/Resources/SharedResources.resx b/Oqtane.Client/Resources/SharedResources.resx
index 89823584..3c1426b2 100644
--- a/Oqtane.Client/Resources/SharedResources.resx
+++ b/Oqtane.Client/Resources/SharedResources.resx
@@ -223,13 +223,13 @@
by
- downloads
+ Downloads
- released
+ Released
- version
+ Version
Edit
@@ -277,19 +277,19 @@
Installed Version
- source
+ Source
Please Provide All Required Information
- Free
+ Open Source
- Paid
+ Commercial
- price
+ Price
Accept
@@ -390,4 +390,19 @@
Support
+
+ Alphabetical
+
+
+ Buy Now
+
+
+ Popularity
+
+
+ Results
+
+
+ Recently Released
+
\ No newline at end of file
diff --git a/Oqtane.Client/Services/AliasService.cs b/Oqtane.Client/Services/AliasService.cs
index c2e59799..714f1061 100644
--- a/Oqtane.Client/Services/AliasService.cs
+++ b/Oqtane.Client/Services/AliasService.cs
@@ -22,7 +22,7 @@ namespace Oqtane.Services
///
public async Task> GetAliasesAsync()
{
- List aliases = await GetJsonAsync>(ApiUrl);
+ List aliases = await GetJsonAsync>(ApiUrl, Enumerable.Empty().ToList());
return aliases.OrderBy(item => item.Name).ToList();
}
diff --git a/Oqtane.Client/Services/InstallationService.cs b/Oqtane.Client/Services/InstallationService.cs
index 4659d9f6..a9e4d3d6 100644
--- a/Oqtane.Client/Services/InstallationService.cs
+++ b/Oqtane.Client/Services/InstallationService.cs
@@ -6,6 +6,7 @@ using Oqtane.Shared;
using Microsoft.AspNetCore.Components;
using System;
using System.Net;
+using System.Linq;
namespace Oqtane.Services
{
@@ -14,11 +15,13 @@ namespace Oqtane.Services
{
private readonly NavigationManager _navigationManager;
private readonly SiteState _siteState;
+ private readonly HttpClient _http;
public InstallationService(HttpClient http, SiteState siteState, NavigationManager navigationManager) : base(http, siteState)
{
_navigationManager = navigationManager;
_siteState = siteState;
+ _http = http;
}
private string ApiUrl => (_siteState.Alias == null)
@@ -27,7 +30,15 @@ namespace Oqtane.Services
public async Task IsInstalled()
{
- var path = new Uri(_navigationManager.Uri).LocalPath.Substring(1);
+ var path = "";
+ if (_http.DefaultRequestHeaders.UserAgent.ToString().Contains(Constants.MauiUserAgent))
+ {
+ path = _http.DefaultRequestHeaders.GetValues(Constants.MauiAliasPath).First();
+ }
+ else
+ {
+ path = new Uri(_navigationManager.Uri).LocalPath.Substring(1);
+ }
return await GetJsonAsync($"{ApiUrl}/installed/?path={WebUtility.UrlEncode(path)}");
}
diff --git a/Oqtane.Client/Services/Interfaces/ISyncService.cs b/Oqtane.Client/Services/Interfaces/ISyncService.cs
index 65b5c226..12af4f2e 100644
--- a/Oqtane.Client/Services/Interfaces/ISyncService.cs
+++ b/Oqtane.Client/Services/Interfaces/ISyncService.cs
@@ -14,6 +14,6 @@ namespace Oqtane.Services
///
///
///
- Task GetSyncAsync(DateTime lastSyncDate);
+ Task GetSyncEventsAsync(DateTime lastSyncDate);
}
}
diff --git a/Oqtane.Client/Services/ServiceBase.cs b/Oqtane.Client/Services/ServiceBase.cs
index f12c0278..d5a65dea 100644
--- a/Oqtane.Client/Services/ServiceBase.cs
+++ b/Oqtane.Client/Services/ServiceBase.cs
@@ -145,10 +145,19 @@ namespace Oqtane.Services
{
return await response.Content.ReadFromJsonAsync();
}
-
return default;
}
+ protected async Task GetJsonAsync(string uri, T defaultResult)
+ {
+ var response = await GetHttpClient().GetAsync(uri, HttpCompletionOption.ResponseHeadersRead, CancellationToken.None);
+ if (await CheckResponse(response, uri) && ValidateJsonContent(response.Content))
+ {
+ return await response.Content.ReadFromJsonAsync();
+ }
+ return defaultResult;
+ }
+
protected async Task PutAsync(string uri)
{
var response = await GetHttpClient().PutAsync(uri, null);
@@ -202,17 +211,27 @@ namespace Oqtane.Services
private async Task CheckResponse(HttpResponseMessage response, string uri)
{
- if (response.IsSuccessStatusCode && uri.Contains("/api/") && !response.RequestMessage.RequestUri.AbsolutePath.Contains("/api/"))
+ if (response.IsSuccessStatusCode)
{
- await Log(uri, response.RequestMessage.Method.ToString(), response.StatusCode.ToString(), "Request {Uri} Not Mapped To An API Controller Method", uri);
+ // if response from api call is not from an api url then the route was not mapped correctly
+ if (uri.Contains("/api/") && !response.RequestMessage.RequestUri.AbsolutePath.Contains("/api/"))
+ {
+ await Log(uri, response.RequestMessage.Method.ToString(), response.StatusCode.ToString(), "Request {Uri} Not Mapped To An API Controller Method", uri);
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+ }
+ else
+ {
+ if (response.StatusCode != HttpStatusCode.NoContent && response.StatusCode != HttpStatusCode.NotFound)
+ {
+ await Log(uri, response.RequestMessage.Method.ToString(), response.StatusCode.ToString(), "Request {Uri} Failed With Status {StatusCode} - {ReasonPhrase}", uri, response.StatusCode, response.ReasonPhrase);
+ }
return false;
}
- if (response.IsSuccessStatusCode) return true;
- if (response.StatusCode != HttpStatusCode.NoContent && response.StatusCode != HttpStatusCode.NotFound)
- {
- await Log(uri, response.RequestMessage.Method.ToString(), response.StatusCode.ToString(), "Request {Uri} Failed With Status {StatusCode} - {ReasonPhrase}", uri, response.StatusCode, response.ReasonPhrase);
- }
- return false;
}
private static bool ValidateJsonContent(HttpContent content)
diff --git a/Oqtane.Client/Services/SyncService.cs b/Oqtane.Client/Services/SyncService.cs
index 6f60d176..3885706c 100644
--- a/Oqtane.Client/Services/SyncService.cs
+++ b/Oqtane.Client/Services/SyncService.cs
@@ -16,7 +16,7 @@ namespace Oqtane.Services
private string ApiUrl => CreateApiUrl("Sync");
///
- public async Task GetSyncAsync(DateTime lastSyncDate)
+ public async Task GetSyncEventsAsync(DateTime lastSyncDate)
{
return await GetJsonAsync($"{ApiUrl}/{lastSyncDate.ToString("yyyyMMddHHmmssfff")}");
}
diff --git a/Oqtane.Client/Themes/Controls/Container/ModuleTitle.razor b/Oqtane.Client/Themes/Controls/Container/ModuleTitle.razor
index ff35f013..007f2d41 100644
--- a/Oqtane.Client/Themes/Controls/Container/ModuleTitle.razor
+++ b/Oqtane.Client/Themes/Controls/Container/ModuleTitle.razor
@@ -2,6 +2,7 @@
@namespace Oqtane.Themes.Controls
@inherits ContainerBase
@attribute [OqtaneIgnore]
+@inject IStringLocalizer SharedLocalizer
@((MarkupString)title)
@@ -23,7 +24,7 @@
}
else
{
- title = ModuleState.Title;
+ title = SharedLocalizer[ModuleState.Title];
}
}
diff --git a/Oqtane.Client/UI/SiteRouter.razor b/Oqtane.Client/UI/SiteRouter.razor
index e1224b16..560deacf 100644
--- a/Oqtane.Client/UI/SiteRouter.razor
+++ b/Oqtane.Client/UI/SiteRouter.razor
@@ -93,12 +93,12 @@
[SuppressMessage("ReSharper", "StringIndexOfIsCultureSpecific.1")]
private async Task Refresh()
{
- Site site;
- Page page;
+ Site site = null;
+ Page page = null;
User user = null;
var editmode = false;
var refresh = false;
- var lastsyncdate = DateTime.UtcNow.AddHours(-1);
+ var lastsyncdate = DateTime.MinValue;
var runtime = (Shared.Runtime)Enum.Parse(typeof(Shared.Runtime), Runtime);
_error = "";
@@ -112,8 +112,8 @@
returnurl = WebUtility.UrlDecode(querystring["returnurl"]);
}
- // reload the client application from the server if there is a forced reload or the user navigated to a site with a different alias
- if (querystring.ContainsKey("reload") || (!NavigationManager.ToBaseRelativePath(_absoluteUri).ToLower().StartsWith(SiteState.Alias.Path.ToLower()) && !string.IsNullOrEmpty(SiteState.Alias.Path)))
+ // reload the client application from the server if there is a forced reload
+ if (querystring.ContainsKey("reload"))
{
if (querystring.ContainsKey("reload") && querystring["reload"] == "post")
{
@@ -126,7 +126,7 @@
}
else
{
- NavigationManager.NavigateTo(_absoluteUri.Replace("?reload", ""), true);
+ NavigationManager.NavigateTo(_absoluteUri.Replace("?reload", "").Replace("&reload", ""), true);
return;
}
}
@@ -163,31 +163,39 @@
else
{
user = PageState.User;
- }
+ }
// process any sync events
- var sync = await SyncService.GetSyncAsync(lastsyncdate);
+ var sync = await SyncService.GetSyncEventsAsync(lastsyncdate);
lastsyncdate = sync.SyncDate;
if (sync.SyncEvents.Any())
{
- // reload client application if server was restarted or site runtime/rendermode was modified
- if (PageState != null && sync.SyncEvents.Exists(item => (item.Action == SyncEventActions.Reload)))
+ // reload client application if server was restarted
+ if (sync.SyncEvents.Exists(item => item.Action == SyncEventActions.Reload && item.EntityName == EntityNames.Host))
{
NavigationManager.NavigateTo(_absoluteUri, true);
return;
}
- // when site information has changed the PageState needs to be refreshed
- if (sync.SyncEvents.Exists(item => item.EntityName == EntityNames.Site && item.EntityId == SiteState.Alias.SiteId))
+ // reload client application if site runtime/rendermode was modified
+ if (sync.SyncEvents.Exists(item => item.Action == SyncEventActions.Reload && item.EntityName == EntityNames.Site && item.EntityId == SiteState.Alias.SiteId))
{
- refresh = true;
+ NavigationManager.NavigateTo(_absoluteUri, true);
+ return;
}
- // when user information has changed the PageState needs to be refreshed as the list of pages/modules may have changed
- if (user != null && sync.SyncEvents.Exists(item => item.EntityName == EntityNames.User && item.EntityId == user.UserId))
+ // reload client application if current user auth information has changed
+ if (user != null && sync.SyncEvents.Exists(item => item.Action == SyncEventActions.Reload && item.EntityName == EntityNames.User && item.EntityId == user.UserId))
+ {
+ NavigationManager.NavigateTo(_absoluteUri, true);
+ return;
+ }
+ // refresh PageState when site information has changed
+ if (sync.SyncEvents.Exists(item => item.Action == SyncEventActions.Refresh && item.EntityName == EntityNames.Site && item.EntityId == SiteState.Alias.SiteId))
{
refresh = true;
}
}
+ // get site
if (PageState == null || refresh || PageState.Alias.SiteId != SiteState.Alias.SiteId)
{
site = await SiteService.GetSiteAsync(SiteState.Alias.SiteId);
diff --git a/Oqtane.Client/UI/ThemeBuilder.razor b/Oqtane.Client/UI/ThemeBuilder.razor
index ebe824ab..cae90f09 100644
--- a/Oqtane.Client/UI/ThemeBuilder.razor
+++ b/Oqtane.Client/UI/ThemeBuilder.razor
@@ -70,7 +70,7 @@
var elements = (">" + content.Replace("\n", "") + "<").Split("><");
foreach (var element in elements)
{
- if (!string.IsNullOrEmpty(element) && !element.Contains("script"))
+ if (!string.IsNullOrEmpty(element) && !element.ToLower().StartsWith("script"))
{
if (!headcontent.Contains("<" + element + ">"))
{
diff --git a/Oqtane.Database.MySQL/Oqtane.Database.MySQL.csproj b/Oqtane.Database.MySQL/Oqtane.Database.MySQL.csproj
index d193711f..e403cb75 100644
--- a/Oqtane.Database.MySQL/Oqtane.Database.MySQL.csproj
+++ b/Oqtane.Database.MySQL/Oqtane.Database.MySQL.csproj
@@ -2,7 +2,7 @@
net7.0
- 4.0.1
+ 4.0.2
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/v4.0.1
+ https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2
https://github.com/oqtane/oqtane.framework
Git
true
diff --git a/Oqtane.Database.MySQL/Oqtane.Database.MySQL.nuspec b/Oqtane.Database.MySQL/Oqtane.Database.MySQL.nuspec
index 1a370623..36a70725 100644
--- a/Oqtane.Database.MySQL/Oqtane.Database.MySQL.nuspec
+++ b/Oqtane.Database.MySQL/Oqtane.Database.MySQL.nuspec
@@ -2,7 +2,7 @@
Oqtane.Database.MySQL
- 4.0.1
+ 4.0.2
Shaun Walker
.NET Foundation
Oqtane MySQL Provider
@@ -12,7 +12,7 @@
false
MIT
https://github.com/oqtane/oqtane.framework
- https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1
+ https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2
icon.png
oqtane
diff --git a/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.csproj b/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.csproj
index c5231e7f..f735e3bb 100644
--- a/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.csproj
+++ b/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.csproj
@@ -2,7 +2,7 @@
net7.0
- 4.0.1
+ 4.0.2
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/v4.0.1
+ https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2
https://github.com/oqtane/oqtane.framework
Git
true
diff --git a/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.nuspec b/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.nuspec
index 5f2636f9..b41d754e 100644
--- a/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.nuspec
+++ b/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.nuspec
@@ -2,7 +2,7 @@
Oqtane.Database.PostgreSQL
- 4.0.1
+ 4.0.2
Shaun Walker
.NET Foundation
Oqtane PostgreSQL Provider
@@ -12,7 +12,7 @@
false
MIT
https://github.com/oqtane/oqtane.framework
- https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1
+ https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2
icon.png
oqtane
diff --git a/Oqtane.Database.SqlServer/Oqtane.Database.SqlServer.csproj b/Oqtane.Database.SqlServer/Oqtane.Database.SqlServer.csproj
index a70171ba..7d1e3d13 100644
--- a/Oqtane.Database.SqlServer/Oqtane.Database.SqlServer.csproj
+++ b/Oqtane.Database.SqlServer/Oqtane.Database.SqlServer.csproj
@@ -2,7 +2,7 @@
net7.0
- 4.0.1
+ 4.0.2
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/v4.0.1
+ https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2
https://github.com/oqtane/oqtane.framework
Git
true
diff --git a/Oqtane.Database.SqlServer/Oqtane.Database.SqlServer.nuspec b/Oqtane.Database.SqlServer/Oqtane.Database.SqlServer.nuspec
index 566e1ed0..3a6ae7ac 100644
--- a/Oqtane.Database.SqlServer/Oqtane.Database.SqlServer.nuspec
+++ b/Oqtane.Database.SqlServer/Oqtane.Database.SqlServer.nuspec
@@ -2,7 +2,7 @@
Oqtane.Database.SqlServer
- 4.0.1
+ 4.0.2
Shaun Walker
.NET Foundation
Oqtane SQL Server Provider
@@ -12,7 +12,7 @@
false
MIT
https://github.com/oqtane/oqtane.framework
- https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1
+ https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2
icon.png
oqtane
diff --git a/Oqtane.Database.Sqlite/Oqtane.Database.Sqlite.csproj b/Oqtane.Database.Sqlite/Oqtane.Database.Sqlite.csproj
index 9e0b7793..af6f2dbb 100644
--- a/Oqtane.Database.Sqlite/Oqtane.Database.Sqlite.csproj
+++ b/Oqtane.Database.Sqlite/Oqtane.Database.Sqlite.csproj
@@ -2,7 +2,7 @@
net7.0
- 4.0.1
+ 4.0.2
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/v4.0.1
+ https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2
https://github.com/oqtane/oqtane.framework
Git
true
diff --git a/Oqtane.Database.Sqlite/Oqtane.Database.Sqlite.nuspec b/Oqtane.Database.Sqlite/Oqtane.Database.Sqlite.nuspec
index c77f1cc7..381e99bf 100644
--- a/Oqtane.Database.Sqlite/Oqtane.Database.Sqlite.nuspec
+++ b/Oqtane.Database.Sqlite/Oqtane.Database.Sqlite.nuspec
@@ -2,7 +2,7 @@
Oqtane.Database.Sqlite
- 4.0.1
+ 4.0.2
Shaun Walker
.NET Foundation
Oqtane SQLite Provider
@@ -12,7 +12,7 @@
false
MIT
https://github.com/oqtane/oqtane.framework
- https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1
+ https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2
icon.png
oqtane
diff --git a/Oqtane.Maui/Main.razor b/Oqtane.Maui/Main.razor
index d139d631..f8b8069c 100644
--- a/Oqtane.Maui/Main.razor
+++ b/Oqtane.Maui/Main.razor
@@ -1,18 +1,52 @@
-
+@using System.Text.Json;
+@using System.Text.Json.Nodes;
-@code {
- Type ComponentType = Type.GetType("Oqtane.App, Oqtane.Client");
- private IDictionary Parameters { get; set; }
-
- protected override void OnInitialized()
- {
- Parameters = new Dictionary();
- Parameters.Add(new KeyValuePair("AntiForgeryToken", ""));
- Parameters.Add(new KeyValuePair("Runtime", "Hybrid"));
- Parameters.Add(new KeyValuePair("RenderMode", "Hybrid"));
- Parameters.Add(new KeyValuePair("VisitorId", -1));
- Parameters.Add(new KeyValuePair("RemoteIPAddress", ""));
- Parameters.Add(new KeyValuePair("AuthorizationToken", ""));
- }
+@if (string.IsNullOrEmpty(message))
+{
+
+}
+else
+{
+
@message
+}
+
+@code {
+ Type ComponentType = Type.GetType("Oqtane.App, Oqtane.Client");
+ private IDictionary Parameters { get; set; }
+ private string message = "";
+
+ protected override void OnInitialized()
+ {
+ Parameters = new Dictionary();
+ Parameters.Add(new KeyValuePair("AntiForgeryToken", ""));
+ Parameters.Add(new KeyValuePair("Runtime", "Hybrid"));
+ Parameters.Add(new KeyValuePair("RenderMode", "Hybrid"));
+ Parameters.Add(new KeyValuePair("VisitorId", -1));
+ Parameters.Add(new KeyValuePair("RemoteIPAddress", ""));
+ Parameters.Add(new KeyValuePair("AuthorizationToken", ""));
+
+ if (MauiConstants.UseAppSettings)
+ {
+ string file = Path.Combine(FileSystem.Current.AppDataDirectory, "appsettings.json");
+ if (File.Exists(file))
+ {
+ using FileStream stream = File.OpenRead(file);
+ using StreamReader reader = new StreamReader(stream);
+ var content = reader.ReadToEnd();
+ var obj = JsonSerializer.Deserialize(content)!;
+ if (string.IsNullOrEmpty((string)obj["Url"]) && string.IsNullOrEmpty(MauiConstants.ApiUrl))
+ {
+ message = "You Must Set The Url In Either MauiConstants.cs Or " + file;
+ }
+ }
+ }
+ else
+ {
+ if (string.IsNullOrEmpty(MauiConstants.ApiUrl))
+ {
+ message = "You Must Set The Url In MauiConstants.cs";
+ }
+ }
+ }
}
diff --git a/Oqtane.Maui/MauiConstants.cs b/Oqtane.Maui/MauiConstants.cs
new file mode 100644
index 00000000..7ff82bbb
--- /dev/null
+++ b/Oqtane.Maui/MauiConstants.cs
@@ -0,0 +1,13 @@
+namespace Oqtane.Maui;
+
+public static class MauiConstants
+{
+ // the API service url (used as fallback if not set in appsettings.json)
+ public static string ApiUrl = "";
+ //public static string ApiUrl = "http://localhost:44357/"; // for local development (Oqtane.Server must be already running for MAUI client to connect)
+ //public static string apiurl = "http://localhost:44357/sitename/"; // local microsite example
+ //public static string apiurl = "https://www.dnfprojects.com/"; // for testing remote site
+
+ // specify if you wish to allow users to override the url via appsettings.json in the AppDataDirectory
+ public static bool UseAppSettings = true;
+}
diff --git a/Oqtane.Maui/MauiProgram.cs b/Oqtane.Maui/MauiProgram.cs
index 471002a6..95a42117 100644
--- a/Oqtane.Maui/MauiProgram.cs
+++ b/Oqtane.Maui/MauiProgram.cs
@@ -6,20 +6,16 @@ using Oqtane.Modules;
using Oqtane.Services;
using System.Globalization;
using System.Text.Json;
+using System.Text.Json.Nodes;
namespace Oqtane.Maui;
public static class MauiProgram
{
- // the API service url
- //static string apiurl = "https://www.dnfprojects.com"; // for testing
- static string apiurl = "http://localhost:44357"; // for local development (Oqtane.Server must be already running for MAUI client to connect)
-
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
- builder
- .UseMauiApp()
+ builder.UseMauiApp()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
@@ -28,15 +24,21 @@ public static class MauiProgram
builder.Services.AddMauiBlazorWebView();
#if DEBUG
builder.Services.AddBlazorWebViewDeveloperTools();
- #endif
+#endif
- var httpClient = new HttpClient { BaseAddress = new Uri(apiurl) };
- httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(Shared.Constants.MauiUserAgent);
- builder.Services.AddSingleton(httpClient);
- builder.Services.AddHttpClient(); // IHttpClientFactory for calling remote services via RemoteServiceBase
+ var apiurl = LoadAppSettings();
- // dynamically load client assemblies
- LoadClientAssemblies(httpClient);
+ if (!string.IsNullOrEmpty(apiurl))
+ {
+ var httpClient = new HttpClient { BaseAddress = new Uri(GetBaseUrl(apiurl)) };
+ httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(Shared.Constants.MauiUserAgent);
+ httpClient.DefaultRequestHeaders.Add(Shared.Constants.MauiAliasPath, GetUrlPath(apiurl).Replace("/", ""));
+ builder.Services.AddSingleton(httpClient);
+ builder.Services.AddHttpClient(); // IHttpClientFactory for calling remote services via RemoteServiceBase
+
+ // dynamically load client assemblies
+ LoadClientAssemblies(httpClient, apiurl);
+ }
// register localization services
builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");
@@ -60,7 +62,37 @@ public static class MauiProgram
return builder.Build();
}
- private static void LoadClientAssemblies(HttpClient http)
+
+ private static string LoadAppSettings()
+ {
+ var url = MauiConstants.ApiUrl;
+ if (MauiConstants.UseAppSettings)
+ {
+ string file = Path.Combine(FileSystem.Current.AppDataDirectory, "appsettings.json");
+ if (File.Exists(file))
+ {
+ using FileStream stream = File.OpenRead(file);
+ using StreamReader reader = new StreamReader(stream);
+ var content = reader.ReadToEnd();
+ var obj = JsonSerializer.Deserialize(content)!;
+ if (!string.IsNullOrEmpty((string)obj["Url"]))
+ {
+ url = (string)obj["Url"];
+ }
+ }
+ else
+ {
+ // create template appsettings.json file
+ using (StreamWriter writer = File.CreateText(file))
+ {
+ writer.WriteLine("{ \"Url\": \"\" }");
+ }
+ }
+ }
+ return url;
+ }
+
+ private static void LoadClientAssemblies(HttpClient http, string apiurl)
{
try
{
@@ -84,7 +116,7 @@ public static class MauiProgram
if (files.Count() != 0)
{
// get list of assemblies from server
- var json = Task.Run(() => http.GetStringAsync("/api/Installation/list")).GetAwaiter().GetResult();
+ var json = Task.Run(() => http.GetStringAsync($"{GetUrlPath(apiurl)}api/Installation/list")).GetAwaiter().GetResult();
var assemblies = JsonSerializer.Deserialize>(json);
// determine which assemblies need to be downloaded
@@ -148,7 +180,7 @@ public static class MauiProgram
if (list.Count != 0)
{
// get assemblies from server
- var zip = Task.Run(() => http.GetByteArrayAsync("/api/Installation/load?list=" + string.Join(",", list))).GetAwaiter().GetResult();
+ var zip = Task.Run(() => http.GetByteArrayAsync($"{GetUrlPath(apiurl)}api/Installation/load?list=" + string.Join(",", list))).GetAwaiter().GetResult();
// asemblies and debug symbols are packaged in a zip file
using (ZipArchive archive = new ZipArchive(new MemoryStream(zip)))
@@ -199,7 +231,7 @@ public static class MauiProgram
}
catch (Exception ex)
{
- Debug.WriteLine($"Oqtane Error: Loading Client Assemblies {ex}");
+ Debug.WriteLine($"Error Loading Client Assemblies From {apiurl} - {ex}");
}
}
@@ -245,4 +277,17 @@ public static class MauiProgram
// could not interrogate assembly - likely missing dependencies
}
}
+
+ private static string GetBaseUrl(string url)
+ {
+ var uri = new Uri(url);
+ return uri.Scheme + "://"+ uri.Authority + "/";
+ }
+
+ private static string GetUrlPath(string url)
+ {
+ var path = new Uri(url).AbsolutePath.Substring(1);
+ path = (!string.IsNullOrEmpty(path) && !path.EndsWith("/")) ? path + "/" : path;
+ return path;
+ }
}
diff --git a/Oqtane.Maui/Oqtane.Maui.csproj b/Oqtane.Maui/Oqtane.Maui.csproj
index 8974e6bb..4a7b898a 100644
--- a/Oqtane.Maui/Oqtane.Maui.csproj
+++ b/Oqtane.Maui/Oqtane.Maui.csproj
@@ -6,7 +6,7 @@
Exe
- 4.0.1
+ 4.0.2
Oqtane
Shaun Walker
.NET Foundation
@@ -14,7 +14,7 @@
.NET Foundation
https://www.oqtane.org
https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE
- https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1
+ https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2
https://github.com/oqtane/oqtane.framework
Git
Oqtane.Maui
@@ -31,7 +31,7 @@
0E29FC31-1B83-48ED-B6E0-9F3C67B775D4
- 4.0.1
+ 4.0.2
1
14.2
@@ -44,7 +44,7 @@
-
+
diff --git a/Oqtane.Maui/Resources/AppIcon/appicon.svg b/Oqtane.Maui/Resources/AppIcon/appicon.svg
index 9d63b651..5def3c42 100644
--- a/Oqtane.Maui/Resources/AppIcon/appicon.svg
+++ b/Oqtane.Maui/Resources/AppIcon/appicon.svg
@@ -1,4 +1,19 @@
-
-
\ No newline at end of file
+
+
+
diff --git a/Oqtane.Package/Oqtane.Client.nuspec b/Oqtane.Package/Oqtane.Client.nuspec
index 752a2449..b1214b2d 100644
--- a/Oqtane.Package/Oqtane.Client.nuspec
+++ b/Oqtane.Package/Oqtane.Client.nuspec
@@ -2,7 +2,7 @@
Oqtane.Client
- 4.0.1
+ 4.0.2
Shaun Walker
.NET Foundation
Oqtane Framework
@@ -12,7 +12,7 @@
false
MIT
https://github.com/oqtane/oqtane.framework
- https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1
+ https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2
icon.png
oqtane
diff --git a/Oqtane.Package/Oqtane.Framework.nuspec b/Oqtane.Package/Oqtane.Framework.nuspec
index 5c5ad632..0d210547 100644
--- a/Oqtane.Package/Oqtane.Framework.nuspec
+++ b/Oqtane.Package/Oqtane.Framework.nuspec
@@ -2,7 +2,7 @@
Oqtane.Framework
- 4.0.1
+ 4.0.2
Shaun Walker
.NET Foundation
Oqtane Framework
@@ -11,8 +11,8 @@
.NET Foundation
false
MIT
- https://github.com/oqtane/oqtane.framework/releases/download/v4.0.1/Oqtane.Framework.4.0.1.Upgrade.zip
- https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1
+ https://github.com/oqtane/oqtane.framework/releases/download/v4.0.2/Oqtane.Framework.4.0.2.Upgrade.zip
+ https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2
icon.png
oqtane framework
diff --git a/Oqtane.Package/Oqtane.Server.nuspec b/Oqtane.Package/Oqtane.Server.nuspec
index 94fe9ab5..650a49f2 100644
--- a/Oqtane.Package/Oqtane.Server.nuspec
+++ b/Oqtane.Package/Oqtane.Server.nuspec
@@ -2,7 +2,7 @@
Oqtane.Server
- 4.0.1
+ 4.0.2
Shaun Walker
.NET Foundation
Oqtane Framework
@@ -12,7 +12,7 @@
false
MIT
https://github.com/oqtane/oqtane.framework
- https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1
+ https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2
icon.png
oqtane
diff --git a/Oqtane.Package/Oqtane.Shared.nuspec b/Oqtane.Package/Oqtane.Shared.nuspec
index f4170745..62a7514a 100644
--- a/Oqtane.Package/Oqtane.Shared.nuspec
+++ b/Oqtane.Package/Oqtane.Shared.nuspec
@@ -2,7 +2,7 @@
Oqtane.Shared
- 4.0.1
+ 4.0.2
Shaun Walker
.NET Foundation
Oqtane Framework
@@ -12,7 +12,7 @@
false
MIT
https://github.com/oqtane/oqtane.framework
- https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1
+ https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2
icon.png
oqtane
diff --git a/Oqtane.Package/Oqtane.Updater.nuspec b/Oqtane.Package/Oqtane.Updater.nuspec
index 622e8109..b2c73f3f 100644
--- a/Oqtane.Package/Oqtane.Updater.nuspec
+++ b/Oqtane.Package/Oqtane.Updater.nuspec
@@ -2,7 +2,7 @@
Oqtane.Updater
- 4.0.1
+ 4.0.2
Shaun Walker
.NET Foundation
Oqtane Framework
@@ -12,7 +12,7 @@
false
MIT
https://github.com/oqtane/oqtane.framework
- https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1
+ https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2
icon.png
oqtane
diff --git a/Oqtane.Package/install.ps1 b/Oqtane.Package/install.ps1
index 8f8c07ad..1981ea2f 100644
--- a/Oqtane.Package/install.ps1
+++ b/Oqtane.Package/install.ps1
@@ -1 +1 @@
-Compress-Archive -Path "..\Oqtane.Server\bin\Release\net7.0\publish\*" -DestinationPath "Oqtane.Framework.4.0.1.Install.zip" -Force
\ No newline at end of file
+Compress-Archive -Path "..\Oqtane.Server\bin\Release\net7.0\publish\*" -DestinationPath "Oqtane.Framework.4.0.2.Install.zip" -Force
\ No newline at end of file
diff --git a/Oqtane.Package/release.cmd b/Oqtane.Package/release.cmd
index c3e281c9..299aa439 100644
--- a/Oqtane.Package/release.cmd
+++ b/Oqtane.Package/release.cmd
@@ -14,7 +14,7 @@ dotnet publish ..\Oqtane.Server\Oqtane.Server.csproj /p:Configuration=Release
del /F/Q/S "..\Oqtane.Server\bin\Release\net7.0\publish\wwwroot\Content" > NUL
rmdir /Q/S "..\Oqtane.Server\bin\Release\net7.0\publish\wwwroot\Content"
setlocal ENABLEDELAYEDEXPANSION
-set retain=Oqtane.Modules.Admin.Login,Oqtane.Modules.HtmlText,Templates
+set retain=Oqtane.Modules.Admin.Login,Oqtane.Modules.HtmlText
for /D %%i in ("..\Oqtane.Server\bin\Release\net7.0\publish\wwwroot\Modules\*") do (
set /A found=0
for %%j in (%retain%) do (
@@ -22,7 +22,7 @@ if "%%~nxi" == "%%j" set /A found=1
)
if not !found! == 1 rmdir /Q/S "%%i"
)
-set retain=Oqtane.Themes.BlazorTheme,Oqtane.Themes.OqtaneTheme,Templates
+set retain=Oqtane.Themes.BlazorTheme,Oqtane.Themes.OqtaneTheme
for /D %%i in ("..\Oqtane.Server\bin\Release\net7.0\publish\wwwroot\Themes\*") do (
set /A found=0
for %%j in (%retain%) do (
diff --git a/Oqtane.Package/upgrade.ps1 b/Oqtane.Package/upgrade.ps1
index 0c20267b..4074dec9 100644
--- a/Oqtane.Package/upgrade.ps1
+++ b/Oqtane.Package/upgrade.ps1
@@ -1 +1 @@
-Compress-Archive -Path "..\Oqtane.Server\bin\Release\net7.0\publish\*" -DestinationPath "Oqtane.Framework.4.0.1.Upgrade.zip" -Force
\ No newline at end of file
+Compress-Archive -Path "..\Oqtane.Server\bin\Release\net7.0\publish\*" -DestinationPath "Oqtane.Framework.4.0.2.Upgrade.zip" -Force
\ No newline at end of file
diff --git a/Oqtane.Server/Controllers/FileController.cs b/Oqtane.Server/Controllers/FileController.cs
index 31ed3c80..636803b1 100644
--- a/Oqtane.Server/Controllers/FileController.cs
+++ b/Oqtane.Server/Controllers/FileController.cs
@@ -572,7 +572,7 @@ namespace Oqtane.Controllers
// validation
if (!Enum.TryParse(mode, true, out ResizeMode _)) mode = "crop";
if (!Enum.TryParse(position, true, out AnchorPositionMode _)) position = "center";
- if (!Color.TryParseHex("#" + background, out _)) background = "000000";
+ if (!Color.TryParseHex("#" + background, out _)) background = "transparent";
if (!int.TryParse(rotate, out _)) rotate = "0";
rotate = (int.Parse(rotate) < 0 || int.Parse(rotate) > 360) ? "0" : rotate;
if (!bool.TryParse(recreate, out _)) recreate = "false";
@@ -644,10 +644,23 @@ namespace Oqtane.Controllers
Mode = resizemode,
Position = anchorpositionmode,
Size = new Size(width, height)
- })
- .BackgroundColor(Color.ParseHex("#" + background)));
+ }));
- image.Save(imagepath, new PngEncoder());
+ if (background != "transparent")
+ {
+ image.Mutate(x => x
+ .BackgroundColor(Color.ParseHex("#" + background)));
+ }
+
+ PngEncoder encoder = new PngEncoder
+ {
+ ColorType = PngColorType.RgbWithAlpha,
+ TransparentColorMode = PngTransparentColorMode.Preserve,
+ BitDepth = PngBitDepth.Bit8,
+ CompressionLevel = PngCompressionLevel.BestSpeed
+ };
+
+ image.Save(imagepath, encoder);
}
}
}
@@ -677,7 +690,14 @@ namespace Oqtane.Controllers
path = Utilities.PathCombine(path, folder, Path.DirectorySeparatorChar.ToString());
if (!Directory.Exists(path))
{
- Directory.CreateDirectory(path);
+ try
+ {
+ Directory.CreateDirectory(path);
+ }
+ catch (Exception ex)
+ {
+ _logger.Log(LogLevel.Error, this, LogFunction.Create, ex, "Unable To Create Folder {Folder}", path);
+ }
}
}
}
diff --git a/Oqtane.Server/Controllers/FolderController.cs b/Oqtane.Server/Controllers/FolderController.cs
index 68feab68..24d252a7 100644
--- a/Oqtane.Server/Controllers/FolderController.cs
+++ b/Oqtane.Server/Controllers/FolderController.cs
@@ -43,7 +43,8 @@ namespace Oqtane.Controllers
{
foreach (Folder folder in _folders.GetFolders(SiteId))
{
- if (_userPermissions.IsAuthorized(User, PermissionNames.View, folder.PermissionList))
+ // note that Browse permission is used for this method
+ if (_userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.PermissionList))
{
folders.Add(folder);
}
@@ -87,6 +88,7 @@ namespace Oqtane.Controllers
public Folder GetByPath(int siteId, string path)
{
var folderPath = WebUtility.UrlDecode(path).Replace("\\", "/");
+ folderPath = (folderPath == "/") ? "" : folderPath;
if (!folderPath.EndsWith("/") && folderPath != "")
{
folderPath += "/";
diff --git a/Oqtane.Server/Controllers/InstallationController.cs b/Oqtane.Server/Controllers/InstallationController.cs
index c94163ca..5c439835 100644
--- a/Oqtane.Server/Controllers/InstallationController.cs
+++ b/Oqtane.Server/Controllers/InstallationController.cs
@@ -34,9 +34,9 @@ namespace Oqtane.Controllers
private readonly IAliasRepository _aliases;
private readonly ILogger _filelogger;
private readonly ITenantManager _tenantManager;
- private readonly ServerStateManager _serverState;
+ private readonly IServerStateManager _serverState;
- public InstallationController(IConfigManager configManager, IInstallationManager installationManager, IDatabaseManager databaseManager, ILocalizationManager localizationManager, IMemoryCache cache, IHttpContextAccessor accessor, IAliasRepository aliases, ILogger filelogger, ITenantManager tenantManager, ServerStateManager serverState)
+ public InstallationController(IConfigManager configManager, IInstallationManager installationManager, IDatabaseManager databaseManager, ILocalizationManager localizationManager, IMemoryCache cache, IHttpContextAccessor accessor, IAliasRepository aliases, ILogger filelogger, ITenantManager tenantManager, IServerStateManager serverState)
{
_configManager = configManager;
_installationManager = installationManager;
@@ -119,9 +119,9 @@ namespace Oqtane.Controllers
private List GetAssemblyList()
{
- int siteId = _tenantManager.GetAlias().SiteId;
+ var siteKey = _tenantManager.GetAlias().SiteKey;
- return _cache.GetOrCreate($"assemblieslist:{siteId}", entry =>
+ return _cache.GetOrCreate($"assemblieslist:{siteKey}", entry =>
{
var binFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
var assemblyList = new List();
@@ -134,7 +134,7 @@ namespace Oqtane.Controllers
}
// get site assemblies which should be downloaded to client
- var assemblies = _serverState.GetServerState(siteId).Assemblies;
+ var assemblies = _serverState.GetServerState(siteKey).Assemblies;
// populate assembly list
foreach (var assembly in assemblies)
@@ -179,9 +179,11 @@ namespace Oqtane.Controllers
private byte[] GetAssemblies(string list)
{
+ var siteKey = _tenantManager.GetAlias().SiteKey;
+
if (list == "*")
{
- return _cache.GetOrCreate("assemblies", entry =>
+ return _cache.GetOrCreate($"assemblies:{siteKey}", entry =>
{
return GetZIP(list);
});
diff --git a/Oqtane.Server/Controllers/ModuleDefinitionController.cs b/Oqtane.Server/Controllers/ModuleDefinitionController.cs
index 21379582..95380bef 100644
--- a/Oqtane.Server/Controllers/ModuleDefinitionController.cs
+++ b/Oqtane.Server/Controllers/ModuleDefinitionController.cs
@@ -283,23 +283,26 @@ namespace Oqtane.Controllers
var templates = new List();
var root = Directory.GetParent(_environment.ContentRootPath);
string templatePath = Utilities.PathCombine(_environment.WebRootPath, "Modules", "Templates", Path.DirectorySeparatorChar.ToString());
- foreach (string directory in Directory.GetDirectories(templatePath))
+ if (Directory.Exists(templatePath))
{
- string name = directory.Replace(templatePath, "");
- if (System.IO.File.Exists(Path.Combine(directory, "template.json")))
+ foreach (string directory in Directory.GetDirectories(templatePath))
{
- var template = JsonSerializer.Deserialize(System.IO.File.ReadAllText(Path.Combine(directory, "template.json")));
- template.Name = name;
- template.Location = "";
- if (template.Type.ToLower() != "internal")
+ string name = directory.Replace(templatePath, "");
+ if (System.IO.File.Exists(Path.Combine(directory, "template.json")))
{
- template.Location = Utilities.PathCombine(root.Parent.ToString(), Path.DirectorySeparatorChar.ToString());
+ var template = JsonSerializer.Deserialize(System.IO.File.ReadAllText(Path.Combine(directory, "template.json")));
+ template.Name = name;
+ template.Location = "";
+ if (template.Type.ToLower() != "internal")
+ {
+ template.Location = Utilities.PathCombine(root.Parent.ToString(), Path.DirectorySeparatorChar.ToString());
+ }
+ templates.Add(template);
+ }
+ else
+ {
+ templates.Add(new Template { Name = name, Title = name, Type = "External", Version = "", Location = Utilities.PathCombine(root.Parent.ToString(), Path.DirectorySeparatorChar.ToString()) });
}
- templates.Add(template);
- }
- else
- {
- templates.Add(new Template { Name = name, Title = name, Type = "External", Version = "", Location = Utilities.PathCombine(root.Parent.ToString(), Path.DirectorySeparatorChar.ToString()) });
}
}
return templates;
diff --git a/Oqtane.Server/Controllers/SyncController.cs b/Oqtane.Server/Controllers/SyncController.cs
index 6c4a136d..2d54d4ff 100644
--- a/Oqtane.Server/Controllers/SyncController.cs
+++ b/Oqtane.Server/Controllers/SyncController.cs
@@ -23,10 +23,16 @@ namespace Oqtane.Controllers
[HttpGet("{lastSyncDate}")]
public Sync Get(string lastSyncDate)
{
+ DateTime currentdate = DateTime.UtcNow;
+ DateTime lastdate = DateTime.ParseExact(lastSyncDate, "yyyyMMddHHmmssfff", CultureInfo.InvariantCulture);
+ if (lastdate == DateTime.MinValue)
+ {
+ lastdate = currentdate;
+ }
Sync sync = new Sync
{
- SyncDate = DateTime.UtcNow,
- SyncEvents = _syncManager.GetSyncEvents(_alias.TenantId, DateTime.ParseExact(lastSyncDate, "yyyyMMddHHmmssfff", CultureInfo.InvariantCulture))
+ SyncDate = currentdate,
+ SyncEvents = _syncManager.GetSyncEvents(_alias.TenantId, lastdate)
};
return sync;
}
diff --git a/Oqtane.Server/Controllers/ThemeController.cs b/Oqtane.Server/Controllers/ThemeController.cs
index 6606f8f9..5135ef22 100644
--- a/Oqtane.Server/Controllers/ThemeController.cs
+++ b/Oqtane.Server/Controllers/ThemeController.cs
@@ -137,23 +137,26 @@ namespace Oqtane.Controllers
var templates = new List();
var root = Directory.GetParent(_environment.ContentRootPath);
string templatePath = Utilities.PathCombine(_environment.WebRootPath, "Themes", "Templates", Path.DirectorySeparatorChar.ToString());
- foreach (string directory in Directory.GetDirectories(templatePath))
+ if (Directory.Exists(templatePath))
{
- string name = directory.Replace(templatePath, "");
- if (System.IO.File.Exists(Path.Combine(directory, "template.json")))
+ foreach (string directory in Directory.GetDirectories(templatePath))
{
- var template = JsonSerializer.Deserialize(System.IO.File.ReadAllText(Path.Combine(directory, "template.json")));
- template.Name = name;
- template.Location = "";
- if (template.Type.ToLower() != "internal")
+ string name = directory.Replace(templatePath, "");
+ if (System.IO.File.Exists(Path.Combine(directory, "template.json")))
{
- template.Location = Utilities.PathCombine(root.Parent.ToString(), Path.DirectorySeparatorChar.ToString());
+ var template = JsonSerializer.Deserialize(System.IO.File.ReadAllText(Path.Combine(directory, "template.json")));
+ template.Name = name;
+ template.Location = "";
+ if (template.Type.ToLower() != "internal")
+ {
+ template.Location = Utilities.PathCombine(root.Parent.ToString(), Path.DirectorySeparatorChar.ToString());
+ }
+ templates.Add(template);
+ }
+ else
+ {
+ templates.Add(new Template { Name = name, Title = name, Type = "External", Version = "", Location = Utilities.PathCombine(root.Parent.ToString(), Path.DirectorySeparatorChar.ToString()) });
}
- templates.Add(template);
- }
- else
- {
- templates.Add(new Template { Name = name, Title = name, Type = "External", Version = "", Location = Utilities.PathCombine(root.Parent.ToString(), Path.DirectorySeparatorChar.ToString()) });
}
}
return templates;
diff --git a/Oqtane.Server/Controllers/UserController.cs b/Oqtane.Server/Controllers/UserController.cs
index d6ef858b..11345d0e 100644
--- a/Oqtane.Server/Controllers/UserController.cs
+++ b/Oqtane.Server/Controllers/UserController.cs
@@ -2,7 +2,6 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Oqtane.Models;
-using Microsoft.AspNetCore.Identity;
using System.Threading.Tasks;
using System.Linq;
using System.Security.Claims;
@@ -22,23 +21,17 @@ namespace Oqtane.Controllers
public class UserController : Controller
{
private readonly IUserRepository _users;
- private readonly UserManager _identityUserManager;
- private readonly SignInManager _identitySignInManager;
private readonly ITenantManager _tenantManager;
- private readonly INotificationRepository _notifications;
private readonly IUserManager _userManager;
private readonly ISiteRepository _sites;
private readonly IUserPermissions _userPermissions;
private readonly IJwtManager _jwtManager;
private readonly ILogManager _logger;
- public UserController(IUserRepository users, UserManager identityUserManager, SignInManager identitySignInManager, ITenantManager tenantManager, INotificationRepository notifications, IUserManager userManager, ISiteRepository sites, IUserPermissions userPermissions, IJwtManager jwtManager, ILogManager logger)
+ public UserController(IUserRepository users, ITenantManager tenantManager, IUserManager userManager, ISiteRepository sites, IUserPermissions userPermissions, IJwtManager jwtManager, ILogManager logger)
{
_users = users;
- _identityUserManager = identityUserManager;
- _identitySignInManager = identitySignInManager;
_tenantManager = tenantManager;
- _notifications = notifications;
_userManager = userManager;
_sites = sites;
_userPermissions = userPermissions;
diff --git a/Oqtane.Server/Controllers/UserRoleController.cs b/Oqtane.Server/Controllers/UserRoleController.cs
index a33872f3..271d7c05 100644
--- a/Oqtane.Server/Controllers/UserRoleController.cs
+++ b/Oqtane.Server/Controllers/UserRoleController.cs
@@ -131,9 +131,8 @@ namespace Oqtane.Controllers
{
userRole = _userRoles.AddUserRole(userRole);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.UserRole, userRole.UserRoleId, SyncEventActions.Create);
+ _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.User, userRole.UserId, SyncEventActions.Reload);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "User Role Added {UserRole}", userRole);
-
- _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.User, userRole.UserId, SyncEventActions.Refresh);
}
else
{
@@ -154,7 +153,7 @@ namespace Oqtane.Controllers
{
userRole = _userRoles.UpdateUserRole(userRole);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.UserRole, userRole.UserRoleId, SyncEventActions.Update);
- _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.User, userRole.UserId, SyncEventActions.Refresh);
+ _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.User, userRole.UserId, SyncEventActions.Reload);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "User Role Updated {UserRole}", userRole);
}
else
@@ -171,25 +170,24 @@ namespace Oqtane.Controllers
[Authorize(Policy = $"{EntityNames.UserRole}:{PermissionNames.Write}:{RoleNames.Admin}")]
public void Delete(int id)
{
- UserRole userrole = _userRoles.GetUserRole(id);
- if (userrole != null && SiteValid(userrole.Role.SiteId) && RoleValid(userrole.Role.Name))
+ UserRole userRole = _userRoles.GetUserRole(id);
+ if (userRole != null && SiteValid(userRole.Role.SiteId) && RoleValid(userRole.Role.Name))
{
_userRoles.DeleteUserRole(id);
- _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.UserRole, userrole.UserRoleId, SyncEventActions.Delete);
- _logger.Log(LogLevel.Information, this, LogFunction.Delete, "User Role Deleted {UserRole}", userrole);
+ _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.UserRole, userRole.UserRoleId, SyncEventActions.Delete);
+ _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.User, userRole.UserId, SyncEventActions.Reload);
+ _logger.Log(LogLevel.Information, this, LogFunction.Delete, "User Role Deleted {UserRole}", userRole);
- if (userrole.Role.Name == RoleNames.Host)
+ if (userRole.Role.Name == RoleNames.Host)
{
// add site specific user roles to preserve user access
var role = _roles.GetRoles(_alias.SiteId).FirstOrDefault(item => item.Name == RoleNames.Registered);
- userrole = _userRoles.AddUserRole(new UserRole { UserId = userrole.UserId, RoleId = role.RoleId, EffectiveDate = null, ExpiryDate = null });
- _logger.Log(LogLevel.Information, this, LogFunction.Create, "User Role Added {UserRole}", userrole);
+ userRole = _userRoles.AddUserRole(new UserRole { UserId = userRole.UserId, RoleId = role.RoleId, EffectiveDate = null, ExpiryDate = null });
+ _logger.Log(LogLevel.Information, this, LogFunction.Create, "User Role Added {UserRole}", userRole);
role = _roles.GetRoles(_alias.SiteId).FirstOrDefault(item => item.Name == RoleNames.Admin);
- userrole = _userRoles.AddUserRole(new UserRole { UserId = userrole.UserId, RoleId = role.RoleId, EffectiveDate = null, ExpiryDate = null });
- _logger.Log(LogLevel.Information, this, LogFunction.Create, "User Role Added {UserRole}", userrole);
+ userRole = _userRoles.AddUserRole(new UserRole { UserId = userRole.UserId, RoleId = role.RoleId, EffectiveDate = null, ExpiryDate = null });
+ _logger.Log(LogLevel.Information, this, LogFunction.Create, "User Role Added {UserRole}", userRole);
}
-
- _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.User, userrole.UserId, SyncEventActions.Refresh);
}
else
{
diff --git a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs
index 73c5d9dd..22d04941 100644
--- a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs
+++ b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs
@@ -62,7 +62,7 @@ namespace Microsoft.Extensions.DependencyInjection
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
- services.AddSingleton();
+ services.AddSingleton();
return services;
}
diff --git a/Oqtane.Server/Infrastructure/Interfaces/IServerStateManager.cs b/Oqtane.Server/Infrastructure/Interfaces/IServerStateManager.cs
new file mode 100644
index 00000000..48115fe7
--- /dev/null
+++ b/Oqtane.Server/Infrastructure/Interfaces/IServerStateManager.cs
@@ -0,0 +1,7 @@
+namespace Oqtane.Infrastructure
+{
+ public interface IServerStateManager
+ {
+ ServerState GetServerState(string siteKey);
+ }
+}
diff --git a/Oqtane.Server/Infrastructure/LocalizationManager.cs b/Oqtane.Server/Infrastructure/LocalizationManager.cs
index 8eec7195..2051d2b5 100644
--- a/Oqtane.Server/Infrastructure/LocalizationManager.cs
+++ b/Oqtane.Server/Infrastructure/LocalizationManager.cs
@@ -40,9 +40,13 @@ namespace Oqtane.Infrastructure
public string[] GetInstalledCultures()
{
var cultures = new List();
- foreach (var file in Directory.EnumerateFiles(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), $"{Constants.ClientId}{Constants.SatelliteAssemblyExtension}", SearchOption.AllDirectories))
+ foreach (var file in Directory.EnumerateFiles(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), $"*{Constants.SatelliteAssemblyExtension}", SearchOption.AllDirectories))
{
- cultures.Add(Path.GetFileName(Path.GetDirectoryName(file)));
+ var culture = Path.GetFileName(Path.GetDirectoryName(file));
+ if (!cultures.Contains(culture))
+ {
+ cultures.Add(culture);
+ }
}
return cultures.OrderBy(c => c).ToArray();
}
diff --git a/Oqtane.Server/Infrastructure/Middleware/JwtMiddleware.cs b/Oqtane.Server/Infrastructure/Middleware/JwtMiddleware.cs
index f0e65998..7b8696fa 100644
--- a/Oqtane.Server/Infrastructure/Middleware/JwtMiddleware.cs
+++ b/Oqtane.Server/Infrastructure/Middleware/JwtMiddleware.cs
@@ -37,20 +37,46 @@ namespace Oqtane.Infrastructure
var identity = jwtManager.ValidateToken(token, secret, sitesettings.GetValue("JwtOptions:Issuer", ""), sitesettings.GetValue("JwtOptions:Audience", ""));
if (identity != null && identity.Claims.Any())
{
- // create user identity using jwt claims (note the difference in claimtype names)
- var user = new User
+ var idclaim = "nameid";
+ var nameclaim = "unique_name";
+ var legacynameclaim = "name"; // this was a breaking change in System.IdentityModel.Tokens.Jwt in .NET 7
+
+ // get jwt claims for userid and username
+ var userid = identity.Claims.FirstOrDefault(item => item.Type == idclaim)?.Value;
+ if (userid != null)
{
- UserId = int.Parse(identity.Claims.FirstOrDefault(item => item.Type == "nameid")?.Value),
- Username = identity.Claims.FirstOrDefault(item => item.Type == "name")?.Value
- };
- // jwt already contains the roles - we are reloading to ensure most accurate permissions
- var _userRoles = context.RequestServices.GetService(typeof(IUserRoleRepository)) as IUserRoleRepository;
+ if (!int.TryParse(userid, out _))
+ {
+ userid = null;
+ }
+ }
+ var username = identity.Claims.FirstOrDefault(item => item.Type == nameclaim)?.Value;
+ if (username == null)
+ {
+ // fallback for legacy clients
+ username = identity.Claims.FirstOrDefault(item => item.Type == legacynameclaim)?.Value;
+ }
- // set claims identity
- var claimsidentity = UserSecurity.CreateClaimsIdentity(alias, user, _userRoles.GetUserRoles(user.UserId, alias.SiteId).ToList());
- context.User = new ClaimsPrincipal(claimsidentity);
+ if (userid != null && username != null)
+ {
+ // create user identity
+ var user = new User
+ {
+ UserId = int.Parse(userid),
+ Username = username
+ };
- logger.Log(alias.SiteId, LogLevel.Information, "TokenValidation", Enums.LogFunction.Security, "Token Validated For User {Username}", user.Username);
+ // set claims identity (note jwt already contains the roles - we are reloading to ensure most accurate permissions)
+ var _userRoles = context.RequestServices.GetService(typeof(IUserRoleRepository)) as IUserRoleRepository;
+ var claimsidentity = UserSecurity.CreateClaimsIdentity(alias, user, _userRoles.GetUserRoles(user.UserId, alias.SiteId).ToList());
+ context.User = new ClaimsPrincipal(claimsidentity);
+
+ logger.Log(alias.SiteId, LogLevel.Information, "TokenValidation", Enums.LogFunction.Security, "Token Validated For UserId {UserId} And Username {Username}", user.UserId, user.Username);
+ }
+ else
+ {
+ logger.Log(alias.SiteId, LogLevel.Error, "TokenValidation", Enums.LogFunction.Security, "Token Validated But Could Not Locate UserId Or Username In Claims {Claims}", identity.Claims.ToString());
+ }
}
else
{
diff --git a/Oqtane.Server/Infrastructure/Middleware/TenantMiddleware.cs b/Oqtane.Server/Infrastructure/Middleware/TenantMiddleware.cs
index c3386bdd..0c415f85 100644
--- a/Oqtane.Server/Infrastructure/Middleware/TenantMiddleware.cs
+++ b/Oqtane.Server/Infrastructure/Middleware/TenantMiddleware.cs
@@ -22,6 +22,7 @@ namespace Oqtane.Infrastructure
var config = context.RequestServices.GetService(typeof(IConfigManager)) as IConfigManager;
string path = context.Request.Path.ToString();
+
if (config.IsInstalled() && !path.StartsWith("/_blazor"))
{
// get alias (note that this also sets SiteState.Alias)
@@ -43,6 +44,14 @@ namespace Oqtane.Infrastructure
});
context.Items.Add(Constants.HttpContextSiteSettingsKey, sitesettings);
+ // handle first request to site
+ var serverState = context.RequestServices.GetService(typeof(IServerStateManager)) as IServerStateManager;
+ if (!serverState.GetServerState(alias.SiteKey).IsInitialized)
+ {
+ var sites = context.RequestServices.GetService(typeof(ISiteRepository)) as ISiteRepository;
+ sites.InitializeSite(alias);
+ }
+
// rewrite path by removing alias path prefix from reserved route (api,pages,files) requests for consistent routes
if (!string.IsNullOrEmpty(alias.Path))
{
diff --git a/Oqtane.Server/Infrastructure/ServerState.cs b/Oqtane.Server/Infrastructure/ServerState.cs
index 6f3ac283..d51e7d17 100644
--- a/Oqtane.Server/Infrastructure/ServerState.cs
+++ b/Oqtane.Server/Infrastructure/ServerState.cs
@@ -5,9 +5,9 @@ namespace Oqtane.Infrastructure
{
public class ServerState
{
- public int SiteId { get; set; }
+ public string SiteKey { get; set; }
public List Assemblies { get; set; } = new List();
public ListScripts { get; set; } = new List();
- public bool IsMigrated { get; set; } = false;
+ public bool IsInitialized { get; set; } = false;
}
}
diff --git a/Oqtane.Server/Infrastructure/ServerStateManager.cs b/Oqtane.Server/Infrastructure/ServerStateManager.cs
index 630ea691..7db90d71 100644
--- a/Oqtane.Server/Infrastructure/ServerStateManager.cs
+++ b/Oqtane.Server/Infrastructure/ServerStateManager.cs
@@ -5,7 +5,7 @@ using Oqtane.Models;
namespace Oqtane.Infrastructure
{
// singleton
- public class ServerStateManager
+ public class ServerStateManager : IServerStateManager
{
private List _serverStates { get; set; }
@@ -14,36 +14,19 @@ namespace Oqtane.Infrastructure
_serverStates = new List();
}
- public ServerState GetServerState(int siteId)
+ public ServerState GetServerState(string siteKey)
{
- var serverState = _serverStates.FirstOrDefault(item => item.SiteId == siteId);
+ var serverState = _serverStates.FirstOrDefault(item => item.SiteKey == siteKey);
if (serverState == null)
{
serverState = new ServerState();
- serverState.SiteId = siteId;
+ serverState.SiteKey = siteKey;
serverState.Assemblies = new List();
serverState.Scripts = new List();
- return serverState;
- }
- else
- {
- return serverState;
- }
- }
-
- public void SetServerState(int siteId, ServerState serverState)
- {
- var serverstate = _serverStates.FirstOrDefault(item => item.SiteId == siteId);
- if (serverstate == null)
- {
- serverState.SiteId = siteId;
+ serverState.IsInitialized = false;
_serverStates.Add(serverState);
}
- else
- {
- serverstate.Assemblies = serverState.Assemblies;
- serverstate.Scripts = serverState.Scripts;
- }
+ return serverState;
}
}
}
diff --git a/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs b/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs
index 6a20b984..6819277a 100644
--- a/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs
+++ b/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs
@@ -133,35 +133,6 @@ namespace Oqtane.SiteTemplates
}
}
});
- _pageTemplates.Add(new PageTemplate
- {
- Name = "Develop",
- Parent = "",
- Order = 7,
- Path = "develop",
- Icon = "oi oi-wrench",
- IsNavigation = true,
- IsPersonalizable = false,
- PermissionList = new List {
- new Permission(PermissionNames.View, RoleNames.Host, true),
- new Permission(PermissionNames.Edit, RoleNames.Host, true)
- },
- PageTemplateModules = new List {
- new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Software Development", Pane = PaneNames.Default,
- PermissionList = new List {
- new Permission(PermissionNames.View, RoleNames.Host, true),
- new Permission(PermissionNames.Edit, RoleNames.Host, true)
- },
- Content = "Oqtane offers a Module Creator which allows you to create new modules to extend the framework with additional capabilities. Simply provide some basic information and the system will scaffold a completely functional module which includes all of the necessary code files and assets to get you up and running as quickly as possible. "
- },
- new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.ModuleCreator, Oqtane.Client", Title = "Module Creator", Pane = PaneNames.Default,
- PermissionList = new List {
- new Permission(PermissionNames.View, RoleNames.Host, true),
- new Permission(PermissionNames.Edit, RoleNames.Host, true)
- }
- }
- }
- });
if (System.IO.File.Exists(Path.Combine(_environment.WebRootPath, "images", "logo-white.png")))
{
diff --git a/Oqtane.Server/Infrastructure/TenantManager.cs b/Oqtane.Server/Infrastructure/TenantManager.cs
index 226f1d0a..7134e135 100644
--- a/Oqtane.Server/Infrastructure/TenantManager.cs
+++ b/Oqtane.Server/Infrastructure/TenantManager.cs
@@ -1,4 +1,5 @@
using System;
+using System.IO;
using System.Linq;
using Microsoft.AspNetCore.Http;
using Oqtane.Models;
@@ -57,7 +58,7 @@ namespace Oqtane.Infrastructure
alias.BaseUrl = "";
if (httpcontext.Request.Headers.ContainsKey("User-Agent") && httpcontext.Request.Headers["User-Agent"] == Shared.Constants.MauiUserAgent)
{
- alias.BaseUrl = alias.Protocol + alias.Name;
+ alias.BaseUrl = alias.Protocol + alias.Name.Replace("/" + alias.Path, "");
}
_siteState.Alias = alias;
}
diff --git a/Oqtane.Server/Infrastructure/UpgradeManager.cs b/Oqtane.Server/Infrastructure/UpgradeManager.cs
index c4d5e91f..3af4b4b7 100644
--- a/Oqtane.Server/Infrastructure/UpgradeManager.cs
+++ b/Oqtane.Server/Infrastructure/UpgradeManager.cs
@@ -231,7 +231,7 @@ namespace Oqtane.Infrastructure
new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
},
- Content = "The page you requested does not exist. "
+ Content = "The page you requested does not exist or you do not have sufficient rights to view it. "
}
}
});
diff --git a/Oqtane.Server/Managers/UserManager.cs b/Oqtane.Server/Managers/UserManager.cs
index 92677c78..5c2e607d 100644
--- a/Oqtane.Server/Managers/UserManager.cs
+++ b/Oqtane.Server/Managers/UserManager.cs
@@ -130,14 +130,14 @@ namespace Oqtane.Managers
if (!user.EmailConfirmed)
{
string token = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser);
- string url = alias.Protocol + "://" + alias.Name + "/login?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
+ string url = alias.Protocol + alias.Name + "/login?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
string body = "Dear " + user.DisplayName + ",\n\nIn Order To Complete The Registration Of Your User Account Please Click The Link Displayed Below:\n\n" + url + "\n\nThank You!";
var notification = new Notification(user.SiteId, User, "User Account Verification", body);
_notifications.AddNotification(notification);
}
else
{
- string url = alias.Protocol + "://" + alias.Name;
+ string url = alias.Protocol + alias.Name;
string body = "Dear " + user.DisplayName + ",\n\nA User Account Has Been Successfully Created For You. Please Use The Following Link To Access The Site:\n\n" + url + "\n\nThank You!";
var notification = new Notification(user.SiteId, User, "User Account Notification", body);
_notifications.AddNotification(notification);
@@ -178,7 +178,7 @@ namespace Oqtane.Managers
user = _users.UpdateUser(user);
_syncManager.AddSyncEvent(_tenantManager.GetAlias().TenantId, EntityNames.User, user.UserId, SyncEventActions.Update);
- _syncManager.AddSyncEvent(_tenantManager.GetAlias().TenantId, EntityNames.User, user.UserId, SyncEventActions.Refresh);
+ _syncManager.AddSyncEvent(_tenantManager.GetAlias().TenantId, EntityNames.User, user.UserId, SyncEventActions.Reload);
user.Password = ""; // remove sensitive information
_logger.Log(LogLevel.Information, this, LogFunction.Update, "User Updated {User}", user);
}
@@ -228,6 +228,7 @@ namespace Oqtane.Managers
// delete user
_users.DeleteUser(userid);
_syncManager.AddSyncEvent(_tenantManager.GetAlias().TenantId, EntityNames.User, userid, SyncEventActions.Delete);
+ _syncManager.AddSyncEvent(_tenantManager.GetAlias().TenantId, EntityNames.User, userid, SyncEventActions.Reload);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "User Deleted {UserId}", userid, result.ToString());
}
else
@@ -299,7 +300,7 @@ namespace Oqtane.Managers
var alias = _tenantManager.GetAlias();
user = _users.GetUser(user.Username);
string token = await _identityUserManager.GeneratePasswordResetTokenAsync(identityuser);
- string url = alias.Protocol + "://" + alias.Name + "/reset?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
+ string url = alias.Protocol + alias.Name + "/reset?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
string body = "Dear " + user.DisplayName + ",\n\nYou attempted multiple times unsuccessfully to log in to your account and it is now locked out. Please wait a few minutes and then try again... or use the link below to reset your password:\n\n" + url +
"\n\nPlease note that the link is only valid for 24 hours so if you are unable to take action within that time period, you should initiate another password reset on the site." +
"\n\nThank You!";
@@ -348,7 +349,7 @@ namespace Oqtane.Managers
var alias = _tenantManager.GetAlias();
user = _users.GetUser(user.Username);
string token = await _identityUserManager.GeneratePasswordResetTokenAsync(identityuser);
- string url = alias.Protocol + "://" + alias.Name + "/reset?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
+ string url = alias.Protocol + alias.Name + "/reset?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
string body = "Dear " + user.DisplayName + ",\n\nYou recently requested to reset your password. Please use the link below to complete the process:\n\n" + url +
"\n\nPlease note that the link is only valid for 24 hours so if you are unable to take action within that time period, you should initiate another password reset on the site." +
"\n\nIf you did not request to reset your password you can safely ignore this message." +
diff --git a/Oqtane.Server/Oqtane.Server.csproj b/Oqtane.Server/Oqtane.Server.csproj
index 4b8cc4bf..950d1ebf 100644
--- a/Oqtane.Server/Oqtane.Server.csproj
+++ b/Oqtane.Server/Oqtane.Server.csproj
@@ -3,7 +3,7 @@
net7.0
Debug;Release
- 4.0.1
+ 4.0.2
Oqtane
Shaun Walker
.NET Foundation
@@ -11,7 +11,7 @@
.NET Foundation
https://www.oqtane.org
https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE
- https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1
+ https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2
https://github.com/oqtane/oqtane.framework
Git
Oqtane
diff --git a/Oqtane.Server/Pages/Logout.cshtml.cs b/Oqtane.Server/Pages/Logout.cshtml.cs
index 0526c41c..6a9f8d30 100644
--- a/Oqtane.Server/Pages/Logout.cshtml.cs
+++ b/Oqtane.Server/Pages/Logout.cshtml.cs
@@ -3,6 +3,9 @@ using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
+using Oqtane.Extensions;
+using Oqtane.Infrastructure;
+using Oqtane.Managers;
using Oqtane.Shared;
namespace Oqtane.Pages
@@ -10,9 +13,28 @@ namespace Oqtane.Pages
[Authorize]
public class LogoutModel : PageModel
{
+ private readonly IUserManager _userManager;
+ private readonly ISyncManager _syncManager;
+
+ public LogoutModel(IUserManager userManager, ISyncManager syncManager)
+ {
+ _userManager = userManager;
+ _syncManager = syncManager;
+ }
+
public async Task OnPostAsync(string returnurl)
{
- await HttpContext.SignOutAsync(Constants.AuthenticationScheme);
+ if (HttpContext.User != null)
+ {
+ var alias = HttpContext.GetAlias();
+ var user = _userManager.GetUser(HttpContext.User.Identity.Name, alias.SiteId);
+ if (user != null)
+ {
+ _syncManager.AddSyncEvent(alias.TenantId, EntityNames.User, user.UserId, SyncEventActions.Reload);
+ }
+
+ await HttpContext.SignOutAsync(Constants.AuthenticationScheme);
+ }
returnurl = (returnurl == null) ? "/" : returnurl;
returnurl = (!returnurl.StartsWith("/")) ? "/" + returnurl : returnurl;
diff --git a/Oqtane.Server/Pages/_Host.cshtml.cs b/Oqtane.Server/Pages/_Host.cshtml.cs
index 98b94e13..ddee53c8 100644
--- a/Oqtane.Server/Pages/_Host.cshtml.cs
+++ b/Oqtane.Server/Pages/_Host.cshtml.cs
@@ -4,7 +4,6 @@ using Oqtane.Shared;
using Oqtane.Models;
using System;
using System.Linq;
-using System.Reflection;
using Oqtane.Repository;
using Microsoft.AspNetCore.Localization;
using Microsoft.Extensions.Configuration;
@@ -38,10 +37,11 @@ namespace Oqtane.Pages
private readonly IVisitorRepository _visitors;
private readonly IAliasRepository _aliases;
private readonly ISettingRepository _settings;
- private readonly ServerStateManager _serverState;
+ private readonly IThemeRepository _themes;
+ private readonly IServerStateManager _serverState;
private readonly ILogManager _logger;
- public HostModel(IConfigManager configuration, ITenantManager tenantManager, ILocalizationManager localizationManager, ILanguageRepository languages, IAntiforgery antiforgery, IJwtManager jwtManager, ISiteRepository sites, IPageRepository pages, IUrlMappingRepository urlMappings, IVisitorRepository visitors, IAliasRepository aliases, ISettingRepository settings, ServerStateManager serverState, ILogManager logger)
+ public HostModel(IConfigManager configuration, ITenantManager tenantManager, ILocalizationManager localizationManager, ILanguageRepository languages, IAntiforgery antiforgery, IJwtManager jwtManager, ISiteRepository sites, IPageRepository pages, IUrlMappingRepository urlMappings, IVisitorRepository visitors, IAliasRepository aliases, ISettingRepository settings, IThemeRepository themes, IServerStateManager serverState, ILogManager logger)
{
_configuration = configuration;
_tenantManager = tenantManager;
@@ -55,6 +55,7 @@ namespace Oqtane.Pages
_visitors = visitors;
_aliases = aliases;
_settings = settings;
+ _themes = themes;
_serverState = serverState;
_logger = logger;
}
@@ -113,7 +114,7 @@ namespace Oqtane.Pages
}
}
- var site = _sites.InitializeSite(alias);
+ var site = _sites.GetSite(alias.SiteId);
if (site != null && (!site.IsDeleted || url.Contains("admin/site")) && site.Runtime != "Hybrid")
{
Route route = new Route(url, alias.Path);
@@ -167,12 +168,13 @@ namespace Oqtane.Pages
}
// stylesheets
+ var themes = _themes.GetThemes().ToList();
var resources = new List();
if (string.IsNullOrEmpty(page.ThemeType))
{
page.ThemeType = site.DefaultThemeType;
}
- var theme = site.Themes.FirstOrDefault(item => item.Themes.Any(item => item.TypeName == page.ThemeType));
+ var theme = 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());
@@ -199,7 +201,7 @@ namespace Oqtane.Pages
}
HeadResources += ParseScripts(site.HeadContent);
BodyResources += ParseScripts(site.BodyContent);
- var scripts = _serverState.GetServerState(site.SiteId).Scripts;
+ var scripts = _serverState.GetServerState(alias.SiteKey).Scripts;
foreach (var script in scripts)
{
AddScript(script, alias);
diff --git a/Oqtane.Server/Repository/Interfaces/ISiteRepository.cs b/Oqtane.Server/Repository/Interfaces/ISiteRepository.cs
index f32123a9..fbba81f9 100644
--- a/Oqtane.Server/Repository/Interfaces/ISiteRepository.cs
+++ b/Oqtane.Server/Repository/Interfaces/ISiteRepository.cs
@@ -11,7 +11,7 @@ namespace Oqtane.Repository
Site GetSite(int siteId);
Site GetSite(int siteId, bool tracking);
void DeleteSite(int siteId);
- Site InitializeSite(Alias alias);
+ void InitializeSite(Alias alias);
void CreatePages(Site site, List pageTemplates, Alias alias);
}
}
diff --git a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs
index 93d0f7f4..99c1e6bf 100644
--- a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs
+++ b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs
@@ -21,10 +21,10 @@ namespace Oqtane.Repository
private readonly IPermissionRepository _permissions;
private readonly ITenantManager _tenants;
private readonly ISettingRepository _settings;
- private readonly ServerStateManager _serverState;
+ private readonly IServerStateManager _serverState;
private readonly string settingprefix = "SiteEnabled:";
- public ModuleDefinitionRepository(MasterDBContext context, IMemoryCache cache, IPermissionRepository permissions, ITenantManager tenants, ISettingRepository settings, ServerStateManager serverState)
+ public ModuleDefinitionRepository(MasterDBContext context, IMemoryCache cache, IPermissionRepository permissions, ITenantManager tenants, ISettingRepository settings, IServerStateManager serverState)
{
_db = context;
_cache = cache;
@@ -179,6 +179,8 @@ namespace Oqtane.Repository
if (siteId != -1)
{
+ var siteKey = _tenants.GetAlias().SiteKey;
+
// get all module definition permissions for site
List permissions = _permissions.GetPermissions(siteId, EntityNames.ModuleDefinition).ToList();
@@ -186,7 +188,7 @@ namespace Oqtane.Repository
var settings = _settings.GetSettings(EntityNames.ModuleDefinition).ToList();
// populate module definition site settings and permissions
- var serverState = _serverState.GetServerState(siteId);
+ var serverState = _serverState.GetServerState(siteKey);
foreach (ModuleDefinition moduledefinition in ModuleDefinitions)
{
moduledefinition.SiteId = siteId;
@@ -212,9 +214,9 @@ namespace Oqtane.Repository
{
foreach (var assembly in moduledefinition.Dependencies.Replace(".dll", "").Split(',', StringSplitOptions.RemoveEmptyEntries).Reverse())
{
- if (!serverState.Assemblies.Contains(assembly))
+ if (!serverState.Assemblies.Contains(assembly.Trim()))
{
- serverState.Assemblies.Insert(0, assembly);
+ serverState.Assemblies.Insert(0, assembly.Trim());
}
}
}
@@ -251,7 +253,6 @@ namespace Oqtane.Repository
}
}
}
- _serverState.SetServerState(siteId, serverState);
// clean up any orphaned permissions
var ids = new HashSet(ModuleDefinitions.Select(item => item.ModuleDefinitionId));
diff --git a/Oqtane.Server/Repository/SiteRepository.cs b/Oqtane.Server/Repository/SiteRepository.cs
index 72b34405..72e07bc7 100644
--- a/Oqtane.Server/Repository/SiteRepository.cs
+++ b/Oqtane.Server/Repository/SiteRepository.cs
@@ -27,13 +27,13 @@ namespace Oqtane.Repository
private readonly IThemeRepository _themeRepository;
private readonly IServiceProvider _serviceProvider;
private readonly IConfigurationRoot _config;
- private readonly ServerStateManager _serverState;
+ private readonly IServerStateManager _serverState;
private readonly ILogManager _logger;
private static readonly object _lock = new object();
public SiteRepository(TenantDBContext context, IRoleRepository roleRepository, IProfileRepository profileRepository, IFolderRepository folderRepository, IPageRepository pageRepository,
IModuleRepository moduleRepository, IPageModuleRepository pageModuleRepository, IModuleDefinitionRepository moduleDefinitionRepository, IThemeRepository themeRepository, IServiceProvider serviceProvider,
- IConfigurationRoot config, ServerStateManager serverState, ILogManager logger)
+ IConfigurationRoot config, IServerStateManager serverState, ILogManager logger)
{
_db = context;
_roleRepository = roleRepository;
@@ -95,23 +95,25 @@ namespace Oqtane.Repository
_db.SaveChanges();
}
- public Site InitializeSite(Alias alias)
+ public void InitializeSite(Alias alias)
{
- var site = GetSite(alias.SiteId);
-
- // load themes and module definitions
- site.Themes = _themeRepository.GetThemes().ToList();
- var moduleDefinitions = _moduleDefinitionRepository.GetModuleDefinitions(alias.SiteId);
-
- // site migrations
- var serverstate = _serverState.GetServerState(alias.SiteId);
- if (!serverstate.IsMigrated)
+ var serverstate = _serverState.GetServerState(alias.SiteKey);
+ if (!serverstate.IsInitialized)
{
- // ensure migrations are only executed once
+ // ensure site initialization is only executed once
lock (_lock)
{
- if (!serverstate.IsMigrated)
+ if (!serverstate.IsInitialized)
{
+ var site = GetSite(alias.SiteId);
+
+ // initialize theme Assemblies and Scripts
+ site.Themes = _themeRepository.GetThemes().ToList();
+
+ // initialize module Assemblies and Scripts
+ var moduleDefinitions = _moduleDefinitionRepository.GetModuleDefinitions(alias.SiteId);
+
+ // execute migrations
var version = ProcessSiteMigrations(alias, site);
version = ProcessPageTemplates(alias, site, moduleDefinitions, version);
if (site.Version != version)
@@ -119,13 +121,11 @@ namespace Oqtane.Repository
site.Version = version;
UpdateSite(site);
}
- serverstate.IsMigrated = true;
- _serverState.SetServerState(alias.SiteId, serverstate);
+
+ serverstate.IsInitialized = true;
}
}
- }
-
- return site;
+ }
}
private string ProcessSiteMigrations(Alias alias, Site site)
@@ -640,7 +640,7 @@ namespace Oqtane.Repository
new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
},
- Content = "The page you requested does not exist. "
+ Content = "The page you requested does not exist or you do not have sufficient rights to view it. "
}
}
});
diff --git a/Oqtane.Server/Repository/ThemeRepository.cs b/Oqtane.Server/Repository/ThemeRepository.cs
index d34d2f31..c555580b 100644
--- a/Oqtane.Server/Repository/ThemeRepository.cs
+++ b/Oqtane.Server/Repository/ThemeRepository.cs
@@ -22,10 +22,10 @@ namespace Oqtane.Repository
private readonly IMemoryCache _cache;
private readonly ITenantManager _tenants;
private readonly ISettingRepository _settings;
- private readonly ServerStateManager _serverState;
+ private readonly IServerStateManager _serverState;
private readonly string settingprefix = "SiteEnabled:";
- public ThemeRepository(MasterDBContext context, IMemoryCache cache, ITenantManager tenants, ISettingRepository settings, ServerStateManager serverState)
+ public ThemeRepository(MasterDBContext context, IMemoryCache cache, ITenantManager tenants, ISettingRepository settings, IServerStateManager serverState)
{
_db = context;
_cache = cache;
@@ -157,11 +157,13 @@ namespace Oqtane.Repository
if (siteId != -1)
{
+ var siteKey = _tenants.GetAlias().SiteKey;
+
// get settings for site
var settings = _settings.GetSettings(EntityNames.Theme).ToList();
// populate theme site settings
- var serverState = _serverState.GetServerState(siteId);
+ var serverState = _serverState.GetServerState(siteKey);
foreach (Theme theme in Themes)
{
theme.SiteId = siteId;
@@ -187,9 +189,9 @@ namespace Oqtane.Repository
{
foreach (var assembly in theme.Dependencies.Replace(".dll", "").Split(',', StringSplitOptions.RemoveEmptyEntries).Reverse())
{
- if (!serverState.Assemblies.Contains(assembly))
+ if (!serverState.Assemblies.Contains(assembly.Trim()))
{
- serverState.Assemblies.Insert(0, assembly);
+ serverState.Assemblies.Insert(0, assembly.Trim());
}
}
}
@@ -206,7 +208,6 @@ namespace Oqtane.Repository
}
}
}
- _serverState.SetServerState(siteId, serverState);
}
return Themes;
diff --git a/Oqtane.Server/Repository/UserRepository.cs b/Oqtane.Server/Repository/UserRepository.cs
index 2944c218..f5136c74 100644
--- a/Oqtane.Server/Repository/UserRepository.cs
+++ b/Oqtane.Server/Repository/UserRepository.cs
@@ -1,7 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore;
-using Oqtane.Extensions;
using Oqtane.Models;
using Oqtane.Shared;
@@ -36,7 +35,9 @@ namespace Oqtane.Repository
}
else
{
+ int siteId = user.SiteId;
user = _db.User.AsNoTracking().First(item => item.Username == user.Username);
+ user.SiteId = siteId;
}
// add folder for user
diff --git a/Oqtane.Server/wwwroot/Modules/Templates/External/Client/Services/[Module]Service.cs b/Oqtane.Server/wwwroot/Modules/Templates/External/Client/Services/[Module]Service.cs
index b6503356..fbcd1bef 100644
--- a/Oqtane.Server/wwwroot/Modules/Templates/External/Client/Services/[Module]Service.cs
+++ b/Oqtane.Server/wwwroot/Modules/Templates/External/Client/Services/[Module]Service.cs
@@ -17,7 +17,7 @@ namespace [Owner].Module.[Module].Services
public async Task> Get[Module]sAsync(int ModuleId)
{
- List [Module]s = await GetJsonAsync>(CreateAuthorizationPolicyUrl($"{Apiurl}?moduleid={ModuleId}", EntityNames.Module, ModuleId));
+ List [Module]s = await GetJsonAsync>(CreateAuthorizationPolicyUrl($"{Apiurl}?moduleid={ModuleId}", EntityNames.Module, ModuleId), Enumerable.Empty().ToList());
return [Module]s.OrderBy(item => item.Name).ToList();
}
diff --git a/Oqtane.Server/wwwroot/Modules/Templates/External/template.json b/Oqtane.Server/wwwroot/Modules/Templates/External/template.json
index f70ee9bc..777da289 100644
--- a/Oqtane.Server/wwwroot/Modules/Templates/External/template.json
+++ b/Oqtane.Server/wwwroot/Modules/Templates/External/template.json
@@ -1,5 +1,5 @@
{
"Title": "Default Module Template",
"Type": "External",
- "Version": "4.0.0"
+ "Version": "4.0.2"
}
diff --git a/Oqtane.Server/wwwroot/Packages/Oqtane.Database.MySQL.nupkg.bak b/Oqtane.Server/wwwroot/Packages/Oqtane.Database.MySQL.nupkg.bak
index 47ba984b..03c7d56f 100644
Binary files a/Oqtane.Server/wwwroot/Packages/Oqtane.Database.MySQL.nupkg.bak and b/Oqtane.Server/wwwroot/Packages/Oqtane.Database.MySQL.nupkg.bak differ
diff --git a/Oqtane.Server/wwwroot/Packages/Oqtane.Database.PostgreSQL.nupkg.bak b/Oqtane.Server/wwwroot/Packages/Oqtane.Database.PostgreSQL.nupkg.bak
index ee3324c0..87a45d22 100644
Binary files a/Oqtane.Server/wwwroot/Packages/Oqtane.Database.PostgreSQL.nupkg.bak and b/Oqtane.Server/wwwroot/Packages/Oqtane.Database.PostgreSQL.nupkg.bak differ
diff --git a/Oqtane.Server/wwwroot/Packages/Oqtane.Database.SqlServer.nupkg.bak b/Oqtane.Server/wwwroot/Packages/Oqtane.Database.SqlServer.nupkg.bak
index 603f3fd1..1d551e8f 100644
Binary files a/Oqtane.Server/wwwroot/Packages/Oqtane.Database.SqlServer.nupkg.bak and b/Oqtane.Server/wwwroot/Packages/Oqtane.Database.SqlServer.nupkg.bak differ
diff --git a/Oqtane.Server/wwwroot/Packages/Oqtane.Database.Sqlite.nupkg.bak b/Oqtane.Server/wwwroot/Packages/Oqtane.Database.Sqlite.nupkg.bak
index 697d02f4..1607b94c 100644
Binary files a/Oqtane.Server/wwwroot/Packages/Oqtane.Database.Sqlite.nupkg.bak and b/Oqtane.Server/wwwroot/Packages/Oqtane.Database.Sqlite.nupkg.bak differ
diff --git a/Oqtane.Server/wwwroot/package.png b/Oqtane.Server/wwwroot/package.png
new file mode 100644
index 00000000..ccc6f290
Binary files /dev/null and b/Oqtane.Server/wwwroot/package.png differ
diff --git a/Oqtane.Shared/Models/Alias.cs b/Oqtane.Shared/Models/Alias.cs
index c7338576..17ec6e80 100644
--- a/Oqtane.Shared/Models/Alias.cs
+++ b/Oqtane.Shared/Models/Alias.cs
@@ -70,7 +70,7 @@ namespace Oqtane.Models
}
///
- /// Protocol for the request from which the alias was resolved (ie. http or https )
+ /// Protocol for the request from which the alias was resolved (ie. http:// or https:// )
///
[NotMapped]
public string Protocol { get; set; }
diff --git a/Oqtane.Shared/Oqtane.Shared.csproj b/Oqtane.Shared/Oqtane.Shared.csproj
index e6062c0e..284c399a 100644
--- a/Oqtane.Shared/Oqtane.Shared.csproj
+++ b/Oqtane.Shared/Oqtane.Shared.csproj
@@ -3,7 +3,7 @@
net7.0
Debug;Release
- 4.0.1
+ 4.0.2
Oqtane
Shaun Walker
.NET Foundation
@@ -11,7 +11,7 @@
.NET Foundation
https://www.oqtane.org
https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE
- https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1
+ https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2
https://github.com/oqtane/oqtane.framework
Git
Oqtane
diff --git a/Oqtane.Shared/Shared/Constants.cs b/Oqtane.Shared/Shared/Constants.cs
index 79c7ada9..29874f0d 100644
--- a/Oqtane.Shared/Shared/Constants.cs
+++ b/Oqtane.Shared/Shared/Constants.cs
@@ -7,8 +7,8 @@ namespace Oqtane.Shared
{
public class Constants
{
- public static readonly string Version = "4.0.1";
- 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";
+ public static readonly string Version = "4.0.2";
+ 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";
public const string PackageId = "Oqtane.Framework";
public const string ClientId = "Oqtane.Client";
public const string UpdaterPackageId = "Oqtane.Updater";
@@ -79,6 +79,7 @@ namespace Oqtane.Shared
public static readonly string HttpContextSiteSettingsKey = "SiteSettings";
public static readonly string MauiUserAgent = "MAUI";
+ public static readonly string MauiAliasPath = "Alias-Path";
public static readonly string VisitorCookiePrefix = "APP_VISITOR_";
// Obsolete constants
diff --git a/Oqtane.Shared/Shared/Utilities.cs b/Oqtane.Shared/Shared/Utilities.cs
index cbab8fa3..cdf0e05e 100644
--- a/Oqtane.Shared/Shared/Utilities.cs
+++ b/Oqtane.Shared/Shared/Utilities.cs
@@ -133,7 +133,7 @@ namespace Oqtane.Shared
var url = (alias != null && !string.IsNullOrEmpty(alias.Path)) ? "/" + alias.Path : "";
mode = string.IsNullOrEmpty(mode) ? "crop" : mode;
position = string.IsNullOrEmpty(position) ? "center" : position;
- background = string.IsNullOrEmpty(background) ? "000000" : background;
+ background = string.IsNullOrEmpty(background) ? "transparent" : background;
return $"{alias?.BaseUrl}{url}{Constants.ImageUrl}{fileId}/{width}/{height}/{mode}/{position}/{background}/{rotate}/{recreate}";
}
diff --git a/Oqtane.Updater/Oqtane.Updater.csproj b/Oqtane.Updater/Oqtane.Updater.csproj
index d7f544f2..e84847c0 100644
--- a/Oqtane.Updater/Oqtane.Updater.csproj
+++ b/Oqtane.Updater/Oqtane.Updater.csproj
@@ -3,7 +3,7 @@
net7.0
Exe
- 4.0.1
+ 4.0.2
Oqtane
Shaun Walker
.NET Foundation
@@ -11,7 +11,7 @@
.NET Foundation
https://www.oqtane.org
https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE
- https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1
+ https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2
https://github.com/oqtane/oqtane.framework
Git
Oqtane
diff --git a/README.md b/README.md
index 485645d4..de6ac3a0 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# Latest Release
-[4.0.0](https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.0) was released on June 26, 2023 and is a major framework upgrade to .NET 7. This release includes 104 pull requests by 5 different contributors, pushing the total number of project commits all-time over 3600. The Oqtane framework continues to evolve at a rapid pace to meet the needs of .NET developers.
+[4.0.1](https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1) was released on July 18, 2023 and is primary focused on stabilization. This release includes 68 pull requests by 8 different contributors, pushing the total number of project commits all-time over 3800. The Oqtane framework continues to evolve at a rapid pace to meet the needs of .NET developers.
[](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Foqtane%2Foqtane.framework%2Fmaster%2Fazuredeploy.json)
@@ -53,7 +53,13 @@ Backlog (TBD)
5.0.0 (Q4 2023)
- [ ] Migration to .NET 8
-[4.0.0](https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.0) ( June 26, 2023 )
+4.0.2 ( Aug 2023 )
+- [x] Stabilization improvements
+
+[4.0.1](https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1) ( Jul 18, 2023 )
+- [x] Stabilization improvements
+
+[4.0.0](https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.0) ( Jun 26, 2023 )
- [x] Migration to .NET 7
- [x] Improved JavaScript, CSS, and Meta support
- [x] Optimized Client Assembly Loading
|