diff --git a/Oqtane.Client/Modules/Admin/Modules/Settings.razor b/Oqtane.Client/Modules/Admin/Modules/Settings.razor index a3133216..9798c2f9 100644 --- a/Oqtane.Client/Modules/Admin/Modules/Settings.razor +++ b/Oqtane.Client/Modules/Admin/Modules/Settings.razor @@ -14,10 +14,16 @@ @if (_containers != null) {
+
+ +
+ +
+
- +
@@ -104,6 +110,7 @@ private ElementReference form; private bool validated = false; private List _containers = new List(); + private string _module; private string _title; private string _containerType; private string _allPages = "false"; @@ -125,6 +132,7 @@ protected override void OnInitialized() { + _module = ModuleState.ModuleDefinition.Name; _title = ModuleState.Title; _containers = ThemeService.GetContainerControls(PageState.Site.Themes, PageState.Page.ThemeType); _containerType = ModuleState.ContainerType; diff --git a/Oqtane.Client/Modules/Admin/UserProfile/Index.razor b/Oqtane.Client/Modules/Admin/UserProfile/Index.razor index e1eabc2c..2dd1303d 100644 --- a/Oqtane.Client/Modules/Admin/UserProfile/Index.razor +++ b/Oqtane.Client/Modules/Admin/UserProfile/Index.razor @@ -144,6 +144,11 @@ else @if (notifications != null) { + +


@if (filter == "to") @@ -159,22 +164,41 @@ else - @context.FromDisplayName - @context.Subject - @string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn) + + @if (context.IsRead) + { + @context.FromDisplayName + @context.Subject + @string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn) + } + else + { + @context.FromDisplayName + @context.Subject + @string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn) + } @{ - string input = "___"; - if (context.Body.Contains(input)) - { - context.Body = context.Body.Split(input)[0]; - context.Body = context.Body.Replace("\n", ""); - context.Body = context.Body.Replace("\r", ""); - } } - @(context.Body.Length > 100 ? (context.Body.Substring(0, 97) + "...") : context.Body) + string input = "___"; + if (context.Body.Contains(input)) + { + context.Body = context.Body.Split(input)[0]; + context.Body = context.Body.Replace("\n", ""); + context.Body = context.Body.Replace("\r", ""); + } + notificationSummary = context.Body.Length > 100 ? (context.Body.Substring(0, 97) + "...") : context.Body; + } + @if (context.IsRead) + { + @notificationSummary + } + else + { + @notificationSummary + } @@ -192,22 +216,42 @@ else - @context.ToDisplayName - @context.Subject - @string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn) + + @if (context.IsRead) + { + @context.ToDisplayName + @context.Subject + @string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn) + } + else + { + @context.ToDisplayName + @context.Subject + @string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn) + } + @{ - string input = "___"; - if (context.Body.Contains(input)) - { - context.Body = context.Body.Split(input)[0]; - context.Body = context.Body.Replace("\n", ""); - context.Body = context.Body.Replace("\r", ""); - } } - @(context.Body.Length > 100 ? (context.Body.Substring(0, 97) + "...") : context.Body) + string input = "___"; + if (context.Body.Contains(input)) + { + context.Body = context.Body.Split(input)[0]; + context.Body = context.Body.Replace("\n", ""); + context.Body = context.Body.Replace("\r", ""); + } + notificationSummary = context.Body.Length > 100 ? (context.Body.Substring(0, 97) + "...") : context.Body; + } + @if (context.IsRead) + { + @notificationSummary + } + else + { + @notificationSummary + } @@ -217,11 +261,6 @@ else
} -

- }
@@ -246,6 +285,7 @@ else private string category = string.Empty; private string filter = "to"; private List notifications; + private string notificationSummary = string.Empty; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.View; diff --git a/Oqtane.Client/Modules/Admin/UserProfile/View.razor b/Oqtane.Client/Modules/Admin/UserProfile/View.razor index 947a6c7b..58a3d211 100644 --- a/Oqtane.Client/Modules/Admin/UserProfile/View.razor +++ b/Oqtane.Client/Modules/Admin/UserProfile/View.razor @@ -118,6 +118,9 @@ Notification notification = await NotificationService.GetNotificationAsync(notificationid); if (notification != null) { + notification.IsRead = true; + notification = await NotificationService.UpdateNotificationAsync(notification); + int userid = -1; if (notification.ToUserId == PageState.User.UserId) { diff --git a/Oqtane.Client/Modules/Controls/FileManager.razor b/Oqtane.Client/Modules/Controls/FileManager.razor index 97363437..2146ead7 100644 --- a/Oqtane.Client/Modules/Controls/FileManager.razor +++ b/Oqtane.Client/Modules/Controls/FileManager.razor @@ -219,7 +219,14 @@ if (folder != null) { _haseditpermission = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, folder.PermissionList); - _files = await FileService.GetFilesAsync(FolderId); + if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Browse, folder.PermissionList)) + { + _files = await FileService.GetFilesAsync(FolderId); + } + else + { + _files = new List(); + } } else { @@ -322,27 +329,29 @@ var folder = (Folder == Constants.PackagesFolder) ? Folder : FolderId.ToString(); await interop.UploadFiles(posturl, folder, _guid, SiteState.AntiForgeryToken); - // uploading is asynchronous so we need to wait for the uploads to complete - // note that this will only wait a maximum of 15 seconds which may not be long enough for very large file uploads - bool success = false; - int attempts = 0; - while (attempts < 5 && !success) + // uploading is asynchronous so we need to poll to determine if uploads are completed + var success = true; + int upload = 0; + while (upload < uploads.Length && success) { - attempts += 1; - Thread.Sleep(1000 * attempts); // progressive retry - - success = true; - List files = await FileService.GetFilesAsync(folder); - if (files.Count > 0) + success = false; + // note that progressive retry will only wait a maximum of 15 seconds which may not be long enough for very large file uploads + int attempts = 0; + while (attempts < 5 && !success) { - foreach (string upload in uploads) + attempts += 1; + Thread.Sleep(1000 * attempts); // progressive retry + + var file = await FileService.GetFileAsync(int.Parse(folder), uploads[upload]); + if (file != null) { - if (!files.Exists(item => item.Name == upload)) - { - success = false; - } + success = true; } } + if (success) + { + upload++; + } } // reset progress indicators @@ -372,14 +381,14 @@ else { // set FileId to first file in upload collection - await GetFiles(); - var file = _files.Where(item => item.Name == uploads[0]).FirstOrDefault(); + var file = await FileService.GetFileAsync(int.Parse(folder), uploads[0]); if (file != null) { FileId = file.FileId; await SetImage(); await OnUpload.InvokeAsync(FileId); } + await GetFiles(); StateHasChanged(); } } diff --git a/Oqtane.Client/Modules/Controls/Section.razor b/Oqtane.Client/Modules/Controls/Section.razor index 115e352d..928f1edc 100644 --- a/Oqtane.Client/Modules/Controls/Section.razor +++ b/Oqtane.Client/Modules/Controls/Section.razor @@ -17,7 +17,10 @@
- @ChildContent + @if (ChildContent != null) + { + @ChildContent + }
@code { @@ -26,7 +29,7 @@ private string _show = string.Empty; [Parameter] - public RenderFragment ChildContent { get; set; } + public RenderFragment ChildContent { get; set; } = null; [Parameter] public string Name { get; set; } // required - the name of the section diff --git a/Oqtane.Client/Modules/ModuleBase.cs b/Oqtane.Client/Modules/ModuleBase.cs index 8023da67..f1a086e5 100644 --- a/Oqtane.Client/Modules/ModuleBase.cs +++ b/Oqtane.Client/Modules/ModuleBase.cs @@ -9,7 +9,6 @@ using Oqtane.UI; using System.Collections.Generic; using Microsoft.JSInterop; using System.Linq; -using Oqtane.Themes; namespace Oqtane.Modules { diff --git a/Oqtane.Client/Oqtane.Client.csproj b/Oqtane.Client/Oqtane.Client.csproj index 19f19354..ae3e2589 100644 --- a/Oqtane.Client/Oqtane.Client.csproj +++ b/Oqtane.Client/Oqtane.Client.csproj @@ -4,7 +4,7 @@ net7.0 Exe Debug;Release - 4.0.0 + 4.0.1 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.0 + https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1 https://github.com/oqtane/oqtane.framework Git Oqtane diff --git a/Oqtane.Client/Resources/Modules/Admin/Modules/Settings.resx b/Oqtane.Client/Resources/Modules/Admin/Modules/Settings.resx index 7cd012dd..db947070 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Modules/Settings.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Modules/Settings.resx @@ -150,4 +150,10 @@ A Problem Was Encountered Loading Module {0}. The Module Is Either Invalid Or Does Not Exist. + + The name of the module + + + Module: + \ No newline at end of file diff --git a/Oqtane.Client/Services/FileService.cs b/Oqtane.Client/Services/FileService.cs index 3fef5c6e..05d3adff 100644 --- a/Oqtane.Client/Services/FileService.cs +++ b/Oqtane.Client/Services/FileService.cs @@ -46,6 +46,11 @@ namespace Oqtane.Services return await GetJsonAsync($"{Apiurl}/{fileId}"); } + public async Task GetFileAsync(int folderId, string name) + { + return await GetJsonAsync($"{Apiurl}/name/{name}/{folderId}"); + } + public async Task AddFileAsync(File file) { return await PostJsonAsync(Apiurl, file); diff --git a/Oqtane.Client/Services/Interfaces/IFileService.cs b/Oqtane.Client/Services/Interfaces/IFileService.cs index 66553b0c..1174947f 100644 --- a/Oqtane.Client/Services/Interfaces/IFileService.cs +++ b/Oqtane.Client/Services/Interfaces/IFileService.cs @@ -1,5 +1,6 @@ using Oqtane.Models; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; namespace Oqtane.Services @@ -33,6 +34,15 @@ namespace Oqtane.Services /// Task GetFileAsync(int fileId); + /// + /// Get a based on the and file name. + /// + /// Reference to the + /// name of the file + /// + /// + Task GetFileAsync(int folderId, string name); + /// /// Add / store a record. /// This does not contain the file contents. diff --git a/Oqtane.Client/Services/Interfaces/INotificationService.cs b/Oqtane.Client/Services/Interfaces/INotificationService.cs index d1be32a2..a831dd5d 100644 --- a/Oqtane.Client/Services/Interfaces/INotificationService.cs +++ b/Oqtane.Client/Services/Interfaces/INotificationService.cs @@ -18,6 +18,27 @@ namespace Oqtane.Services /// Task> GetNotificationsAsync(int siteId, string direction, int userId); + /// + /// + /// + /// + /// + /// + /// + /// + /// + Task> GetNotificationsAsync(int siteId, string direction, int userId, int count, bool isRead); + + /// + /// + /// + /// + /// + /// + /// + /// + Task GetNotificationCountAsync(int siteId, string direction, int userId, bool isRead); + /// /// Returns a specific notifications /// diff --git a/Oqtane.Client/Services/Interfaces/IPackageService.cs b/Oqtane.Client/Services/Interfaces/IPackageService.cs index f8617439..4ace8cc8 100644 --- a/Oqtane.Client/Services/Interfaces/IPackageService.cs +++ b/Oqtane.Client/Services/Interfaces/IPackageService.cs @@ -27,6 +27,17 @@ namespace Oqtane.Services /// Task> GetPackagesAsync(string type, string search, string price, string package); + /// + /// Returns a list of packages matching the given parameters + /// + /// + /// + /// + /// + /// + /// + Task> GetPackagesAsync(string type, string search, string price, string package, string sort); + /// /// Returns a specific package /// diff --git a/Oqtane.Client/Services/NotificationService.cs b/Oqtane.Client/Services/NotificationService.cs index 3368ff0b..f6fd1585 100644 --- a/Oqtane.Client/Services/NotificationService.cs +++ b/Oqtane.Client/Services/NotificationService.cs @@ -22,6 +22,20 @@ namespace Oqtane.Services return notifications.OrderByDescending(item => item.CreatedOn).ToList(); } + public async Task> GetNotificationsAsync(int siteId, string direction, int userId, int count, bool isRead) + { + var notifications = await GetJsonAsync>($"{Apiurl}/read?siteid={siteId}&direction={direction.ToLower()}&userid={userId}&count={count}&isread={isRead}"); + + return notifications.OrderByDescending(item => item.CreatedOn).ToList(); + } + + public async Task GetNotificationCountAsync(int siteId, string direction, int userId, bool isRead) + { + var notificationCount = await GetJsonAsync($"{Apiurl}/read-count?siteid={siteId}&direction={direction.ToLower()}&userid={userId}&isread={isRead}"); + + return notificationCount; + } + public async Task GetNotificationAsync(int notificationId) { return await GetJsonAsync($"{Apiurl}/{notificationId}"); diff --git a/Oqtane.Client/Services/PackageService.cs b/Oqtane.Client/Services/PackageService.cs index a45c73e6..78752d38 100644 --- a/Oqtane.Client/Services/PackageService.cs +++ b/Oqtane.Client/Services/PackageService.cs @@ -23,7 +23,12 @@ namespace Oqtane.Services public async Task> GetPackagesAsync(string type, string search, string price, string package) { - return await GetJsonAsync>($"{Apiurl}?type={type}&search={WebUtility.UrlEncode(search)}&price={price}&package={package}"); + return await GetPackagesAsync(type, search, price, package, ""); + } + + public async Task> GetPackagesAsync(string type, string search, string price, string package, string sort) + { + return await GetJsonAsync>($"{Apiurl}?type={type}&search={WebUtility.UrlEncode(search)}&price={price}&package={package}&sort={sort}"); } public async Task GetPackageAsync(string packageId, string version) diff --git a/Oqtane.Client/Services/ServiceBase.cs b/Oqtane.Client/Services/ServiceBase.cs index a5e7fd26..00b2412a 100644 --- a/Oqtane.Client/Services/ServiceBase.cs +++ b/Oqtane.Client/Services/ServiceBase.cs @@ -21,7 +21,7 @@ namespace Oqtane.Services _siteState = siteState; } - private HttpClient GetHttpClient() + public HttpClient GetHttpClient() { if (!_httpClient.DefaultRequestHeaders.Contains(Constants.AntiForgeryTokenHeaderName) && _siteState != null && !string.IsNullOrEmpty(_siteState.AntiForgeryToken)) { @@ -206,7 +206,6 @@ namespace Oqtane.Services Console.WriteLine($"Request: {response.RequestMessage.RequestUri}"); Console.WriteLine($"Response status: {response.StatusCode} {response.ReasonPhrase}"); } - return false; } diff --git a/Oqtane.Client/Themes/Controls/Theme/ControlPanel.razor b/Oqtane.Client/Themes/Controls/Theme/ControlPanel.razor index 4c3f9f0c..d35268a2 100644 --- a/Oqtane.Client/Themes/Controls/Theme/ControlPanel.razor +++ b/Oqtane.Client/Themes/Controls/Theme/ControlPanel.razor @@ -36,7 +36,7 @@ @if (_canViewAdminDashboard || UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList)) { - @@ -471,6 +471,12 @@ private async Task ToggleEditMode(bool EditMode) { + Page page = null; + if (PageState.Page.IsPersonalizable && PageState.User != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Registered)) + { + page = await PageService.AddPageAsync(PageState.Page.PageId, PageState.User.UserId); + } + if (_showEditMode) { if (EditMode) @@ -490,9 +496,8 @@ } else { - if (PageState.Page.IsPersonalizable && PageState.User != null) + if (PageState.Page.IsPersonalizable && PageState.User != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Registered)) { - var page = await PageService.AddPageAsync(PageState.Page.PageId, PageState.User.UserId); PageState.EditMode = true; NavigationManager.NavigateTo(NavigateUrl(page.Path, "edit=" + ((PageState.EditMode) ? "true" : "false"))); } diff --git a/Oqtane.Client/Themes/Controls/Theme/LoginBase.cs b/Oqtane.Client/Themes/Controls/Theme/LoginBase.cs index 9f4f4071..468e398e 100644 --- a/Oqtane.Client/Themes/Controls/Theme/LoginBase.cs +++ b/Oqtane.Client/Themes/Controls/Theme/LoginBase.cs @@ -19,7 +19,6 @@ namespace Oqtane.Themes.Controls [Inject] public IUserService UserService { get; set; } [Inject] public IJSRuntime jsRuntime { get; set; } [Inject] public IServiceProvider ServiceProvider { get; set; } - [Inject] public ILogService LoggingService { get; set; } protected void LoginUser() { diff --git a/Oqtane.Client/Themes/ThemeBase.cs b/Oqtane.Client/Themes/ThemeBase.cs index 8fcb1a30..2de61b1c 100644 --- a/Oqtane.Client/Themes/ThemeBase.cs +++ b/Oqtane.Client/Themes/ThemeBase.cs @@ -1,6 +1,9 @@ using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; +using Oqtane.Enums; using Oqtane.Models; +using Oqtane.Modules; +using Oqtane.Services; using Oqtane.Shared; using Oqtane.UI; using System; @@ -13,6 +16,9 @@ namespace Oqtane.Themes { public abstract class ThemeBase : ComponentBase, IThemeControl { + [Inject] + protected ILogService LoggingService { get; set; } + [Inject] protected IJSRuntime JSRuntime { get; set; } @@ -186,6 +192,148 @@ namespace Oqtane.Themes await interop.ScrollTo(0, 0, "smooth"); } + // logging methods + public async Task Log(Alias alias, LogLevel level, string function, Exception exception, string message, params object[] args) + { + LogFunction logFunction; + if (string.IsNullOrEmpty(function)) + { + // try to infer from page action + function = PageState.Action; + } + if (!Enum.TryParse(function, out logFunction)) + { + switch (function.ToLower()) + { + case "add": + logFunction = LogFunction.Create; + break; + case "edit": + logFunction = LogFunction.Update; + break; + case "delete": + logFunction = LogFunction.Delete; + break; + case "": + logFunction = LogFunction.Read; + break; + default: + logFunction = LogFunction.Other; + break; + } + } + await Log(alias, level, logFunction, exception, message, args); + } + + public async Task Log(Alias alias, LogLevel level, LogFunction function, Exception exception, string message, params object[] args) + { + int pageId = PageState.Page.PageId; + string category = GetType().AssemblyQualifiedName; + string feature = Utilities.GetTypeNameLastSegment(category, 1); + + await LoggingService.Log(alias, pageId, null, PageState.User?.UserId, category, feature, function, level, exception, message, args); + } + + public class Logger + { + private readonly ModuleBase _moduleBase; + + public Logger(ModuleBase moduleBase) + { + _moduleBase = moduleBase; + } + + public async Task LogTrace(string message, params object[] args) + { + await _moduleBase.Log(null, LogLevel.Trace, "", null, message, args); + } + + public async Task LogTrace(LogFunction function, string message, params object[] args) + { + await _moduleBase.Log(null, LogLevel.Trace, function, null, message, args); + } + + public async Task LogTrace(Exception exception, string message, params object[] args) + { + await _moduleBase.Log(null, LogLevel.Trace, "", exception, message, args); + } + + public async Task LogDebug(string message, params object[] args) + { + await _moduleBase.Log(null, LogLevel.Debug, "", null, message, args); + } + + public async Task LogDebug(LogFunction function, string message, params object[] args) + { + await _moduleBase.Log(null, LogLevel.Debug, function, null, message, args); + } + + public async Task LogDebug(Exception exception, string message, params object[] args) + { + await _moduleBase.Log(null, LogLevel.Debug, "", exception, message, args); + } + + public async Task LogInformation(string message, params object[] args) + { + await _moduleBase.Log(null, LogLevel.Information, "", null, message, args); + } + + public async Task LogInformation(LogFunction function, string message, params object[] args) + { + await _moduleBase.Log(null, LogLevel.Information, function, null, message, args); + } + + public async Task LogInformation(Exception exception, string message, params object[] args) + { + await _moduleBase.Log(null, LogLevel.Information, "", exception, message, args); + } + + public async Task LogWarning(string message, params object[] args) + { + await _moduleBase.Log(null, LogLevel.Warning, "", null, message, args); + } + + public async Task LogWarning(LogFunction function, string message, params object[] args) + { + await _moduleBase.Log(null, LogLevel.Warning, function, null, message, args); + } + + public async Task LogWarning(Exception exception, string message, params object[] args) + { + await _moduleBase.Log(null, LogLevel.Warning, "", exception, message, args); + } + + public async Task LogError(string message, params object[] args) + { + await _moduleBase.Log(null, LogLevel.Error, "", null, message, args); + } + + public async Task LogError(LogFunction function, string message, params object[] args) + { + await _moduleBase.Log(null, LogLevel.Error, function, null, message, args); + } + + public async Task LogError(Exception exception, string message, params object[] args) + { + await _moduleBase.Log(null, LogLevel.Error, "", exception, message, args); + } + + public async Task LogCritical(string message, params object[] args) + { + await _moduleBase.Log(null, LogLevel.Critical, "", null, message, args); + } + + public async Task LogCritical(LogFunction function, string message, params object[] args) + { + await _moduleBase.Log(null, LogLevel.Critical, function, null, message, args); + } + + public async Task LogCritical(Exception exception, string message, params object[] args) + { + await _moduleBase.Log(null, LogLevel.Critical, "", exception, message, args); + } + } + [Obsolete("ContentUrl(int fileId) is deprecated. Use FileUrl(int fileId) instead.", false)] public string ContentUrl(int fileid) { diff --git a/Oqtane.Client/UI/SiteRouter.razor b/Oqtane.Client/UI/SiteRouter.razor index cd3a1764..e1224b16 100644 --- a/Oqtane.Client/UI/SiteRouter.razor +++ b/Oqtane.Client/UI/SiteRouter.razor @@ -223,12 +223,12 @@ } if (page == null) { - // look for personalized page page = await PageService.GetPageAsync(route.PagePath, site.SiteId); } else { - if (user != null && page.IsPersonalizable) + // look for personalized page + if (user != null && page.IsPersonalizable && !UserSecurity.IsAuthorized(user, PermissionNames.Edit, page.PermissionList)) { var personalized = await PageService.GetPageAsync(route.PagePath + "/" + user.Username, site.SiteId); if (personalized != null) diff --git a/Oqtane.Database.MySQL/Oqtane.Database.MySQL.csproj b/Oqtane.Database.MySQL/Oqtane.Database.MySQL.csproj index 516a19b1..d193711f 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.0 + 4.0.1 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.0 + https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1 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 93c7397f..1a370623 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.0 + 4.0.1 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.0 + https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1 icon.png oqtane diff --git a/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.csproj b/Oqtane.Database.PostgreSQL/Oqtane.Database.PostgreSQL.csproj index 766bb3ca..c5231e7f 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.0 + 4.0.1 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.0 + https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1 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 dabb9c07..5f2636f9 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.0 + 4.0.1 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.0 + https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1 icon.png oqtane diff --git a/Oqtane.Database.SqlServer/Oqtane.Database.SqlServer.csproj b/Oqtane.Database.SqlServer/Oqtane.Database.SqlServer.csproj index a5f1eff0..a70171ba 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.0 + 4.0.1 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.0 + https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1 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 7a021d25..566e1ed0 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.0 + 4.0.1 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.0 + https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1 icon.png oqtane diff --git a/Oqtane.Database.Sqlite/Oqtane.Database.Sqlite.csproj b/Oqtane.Database.Sqlite/Oqtane.Database.Sqlite.csproj index a14adef3..9e0b7793 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.0 + 4.0.1 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.0 + https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1 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 9a36be5b..c77f1cc7 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.0 + 4.0.1 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.0 + https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1 icon.png oqtane diff --git a/Oqtane.Maui/Oqtane.Maui.csproj b/Oqtane.Maui/Oqtane.Maui.csproj index 1f9d42e8..8974e6bb 100644 --- a/Oqtane.Maui/Oqtane.Maui.csproj +++ b/Oqtane.Maui/Oqtane.Maui.csproj @@ -6,7 +6,7 @@ Exe - 4.0.0 + 4.0.1 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.0 + https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1 https://github.com/oqtane/oqtane.framework Git Oqtane.Maui @@ -31,7 +31,7 @@ 0E29FC31-1B83-48ED-B6E0-9F3C67B775D4 - 4.0.0 + 4.0.1 1 14.2 diff --git a/Oqtane.Package/Oqtane.Client.nuspec b/Oqtane.Package/Oqtane.Client.nuspec index b2b4def4..752a2449 100644 --- a/Oqtane.Package/Oqtane.Client.nuspec +++ b/Oqtane.Package/Oqtane.Client.nuspec @@ -2,7 +2,7 @@ Oqtane.Client - 4.0.0 + 4.0.1 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.0 + https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1 icon.png oqtane diff --git a/Oqtane.Package/Oqtane.Framework.nuspec b/Oqtane.Package/Oqtane.Framework.nuspec index 662219e6..5c5ad632 100644 --- a/Oqtane.Package/Oqtane.Framework.nuspec +++ b/Oqtane.Package/Oqtane.Framework.nuspec @@ -2,7 +2,7 @@ Oqtane.Framework - 4.0.0 + 4.0.1 Shaun Walker .NET Foundation Oqtane Framework @@ -11,8 +11,8 @@ .NET Foundation false MIT - https://github.com/oqtane/oqtane.framework/releases/download/v4.0.0/Oqtane.Framework.4.0.0.Upgrade.zip - https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.0 + 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 icon.png oqtane framework diff --git a/Oqtane.Package/Oqtane.Server.nuspec b/Oqtane.Package/Oqtane.Server.nuspec index 70a25469..94fe9ab5 100644 --- a/Oqtane.Package/Oqtane.Server.nuspec +++ b/Oqtane.Package/Oqtane.Server.nuspec @@ -2,7 +2,7 @@ Oqtane.Server - 4.0.0 + 4.0.1 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.0 + https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1 icon.png oqtane diff --git a/Oqtane.Package/Oqtane.Shared.nuspec b/Oqtane.Package/Oqtane.Shared.nuspec index 6b073a4a..f4170745 100644 --- a/Oqtane.Package/Oqtane.Shared.nuspec +++ b/Oqtane.Package/Oqtane.Shared.nuspec @@ -2,7 +2,7 @@ Oqtane.Shared - 4.0.0 + 4.0.1 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.0 + https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1 icon.png oqtane diff --git a/Oqtane.Package/Oqtane.Updater.nuspec b/Oqtane.Package/Oqtane.Updater.nuspec index 9e667cc9..622e8109 100644 --- a/Oqtane.Package/Oqtane.Updater.nuspec +++ b/Oqtane.Package/Oqtane.Updater.nuspec @@ -2,7 +2,7 @@ Oqtane.Updater - 4.0.0 + 4.0.1 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.0 + https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1 icon.png oqtane diff --git a/Oqtane.Package/install.ps1 b/Oqtane.Package/install.ps1 index 9d1c4f9e..8f8c07ad 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.0.Install.zip" -Force \ No newline at end of file +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 diff --git a/Oqtane.Package/upgrade.ps1 b/Oqtane.Package/upgrade.ps1 index 17d5c251..0c20267b 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.0.Upgrade.zip" -Force \ No newline at end of file +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 diff --git a/Oqtane.Server/Controllers/FileController.cs b/Oqtane.Server/Controllers/FileController.cs index 30cbab26..31ed3c80 100644 --- a/Oqtane.Server/Controllers/FileController.cs +++ b/Oqtane.Server/Controllers/FileController.cs @@ -123,8 +123,38 @@ namespace Oqtane.Controllers } else { - _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Get Attempt {FileId}", id); - HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + if (file != null) + { + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Get Attempt {FileId}", id); + HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + } + else + { + HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; + } + return null; + } + } + + [HttpGet("name/{name}/{folderId}")] + public Models.File Get(string name, int folderId) + { + Models.File file = _files.GetFile(folderId, name); + if (file != null && file.Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.PermissionList)) + { + return file; + } + else + { + if (file != null) + { + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Get Attempt {Name} For Folder {FolderId}", name, folderId); + HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + } + else + { + HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; + } return null; } } diff --git a/Oqtane.Server/Controllers/FolderController.cs b/Oqtane.Server/Controllers/FolderController.cs index 6914aac7..68feab68 100644 --- a/Oqtane.Server/Controllers/FolderController.cs +++ b/Oqtane.Server/Controllers/FolderController.cs @@ -43,7 +43,7 @@ namespace Oqtane.Controllers { foreach (Folder folder in _folders.GetFolders(SiteId)) { - if (_userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.PermissionList)) + if (_userPermissions.IsAuthorized(User, PermissionNames.View, folder.PermissionList)) { folders.Add(folder); } @@ -64,14 +64,21 @@ namespace Oqtane.Controllers public Folder Get(int id) { Folder folder = _folders.GetFolder(id); - if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.PermissionList)) + if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, folder.PermissionList)) { return folder; } else { - _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Folder Get Attempt {FolderId}", id); - HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + if (folder != null) + { + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Folder Get Attempt {FolderId}", id); + HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + } + else + { + HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; + } return null; } } @@ -80,19 +87,26 @@ namespace Oqtane.Controllers public Folder GetByPath(int siteId, string path) { var folderPath = WebUtility.UrlDecode(path).Replace("\\", "/"); - if (!folderPath.EndsWith("/")) + if (!folderPath.EndsWith("/") && folderPath != "") { folderPath += "/"; } Folder folder = _folders.GetFolder(siteId, folderPath); - if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.PermissionList)) + if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, folder.PermissionList)) { return folder; } else { - _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Folder Get Attempt {Path} For Site {SiteId}", path, siteId); - HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + if (folder != null) + { + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Folder Get Attempt {Path} For Site {SiteId}", path, siteId); + HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + } + else + { + HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; + } return null; } } diff --git a/Oqtane.Server/Controllers/LanguageController.cs b/Oqtane.Server/Controllers/LanguageController.cs index e4c6dbc7..e1ac9406 100644 --- a/Oqtane.Server/Controllers/LanguageController.cs +++ b/Oqtane.Server/Controllers/LanguageController.cs @@ -89,8 +89,15 @@ namespace Oqtane.Controllers } else { - _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Language Get Attempt {LanguageId}", id); - HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + if (language != null) + { + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Language Get Attempt {LanguageId}", id); + HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + } + else + { + HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; + } return null; } } diff --git a/Oqtane.Server/Controllers/ModuleController.cs b/Oqtane.Server/Controllers/ModuleController.cs index 7176057f..9422d019 100644 --- a/Oqtane.Server/Controllers/ModuleController.cs +++ b/Oqtane.Server/Controllers/ModuleController.cs @@ -113,8 +113,15 @@ namespace Oqtane.Controllers } else { - _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Module Get Attempt {ModuleId}", id); - HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + if (module != null) + { + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Module Get Attempt {ModuleId}", id); + HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + } + else + { + HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; + } return null; } } diff --git a/Oqtane.Server/Controllers/ModuleDefinitionController.cs b/Oqtane.Server/Controllers/ModuleDefinitionController.cs index c8114821..21379582 100644 --- a/Oqtane.Server/Controllers/ModuleDefinitionController.cs +++ b/Oqtane.Server/Controllers/ModuleDefinitionController.cs @@ -89,15 +89,22 @@ namespace Oqtane.Controllers if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId) { ModuleDefinition moduledefinition = _moduleDefinitions.GetModuleDefinition(id, SiteId); - if (_userPermissions.IsAuthorized(User, PermissionNames.Utilize, moduledefinition.PermissionList)) + if (moduledefinition != null && _userPermissions.IsAuthorized(User, PermissionNames.Utilize, moduledefinition.PermissionList)) { - if (string.IsNullOrEmpty(moduledefinition.Version)) moduledefinition.Version = new Version(1, 0, 0).ToString(); + moduledefinition.Version = (string.IsNullOrEmpty(moduledefinition.Version)) ? new Version(1, 0, 0).ToString() : moduledefinition.Version; return moduledefinition; } else { - _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized ModuleDefinition Get Attempt {ModuleDefinitionId} {SiteId}", id, siteid); - HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + if (moduledefinition != null) + { + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized ModuleDefinition Get Attempt {ModuleDefinitionId} {SiteId}", id, siteid); + HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + } + else + { + HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; + } return null; } } diff --git a/Oqtane.Server/Controllers/NotificationController.cs b/Oqtane.Server/Controllers/NotificationController.cs index dfdba5f1..e2bfd14e 100644 --- a/Oqtane.Server/Controllers/NotificationController.cs +++ b/Oqtane.Server/Controllers/NotificationController.cs @@ -9,6 +9,9 @@ using Oqtane.Repository; using Oqtane.Security; using System.Net; using System.Reflection.Metadata; +using Microsoft.Extensions.Localization; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using System.Linq; namespace Oqtane.Controllers { @@ -30,6 +33,72 @@ namespace Oqtane.Controllers _alias = tenantManager.GetAlias(); } + // GET: api//read?siteid=x&direction=to&userid=1&count=5&isread=false + [HttpGet("read")] + [Authorize(Roles = RoleNames.Registered)] + public IEnumerable Get(string siteid, string direction, string userid, string count, string isread) + { + IEnumerable notifications = null; + + int SiteId; + int UserId; + int Count; + bool IsRead; + if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId && int.TryParse(userid, out UserId) && int.TryParse(count, out Count) && bool.TryParse(isread, out IsRead) && IsAuthorized(UserId)) + { + if (direction == "to") + { + notifications = _notifications.GetNotifications(SiteId, -1, UserId, Count, IsRead); + } + else + { + notifications = _notifications.GetNotifications(SiteId, UserId, -1, Count, IsRead); + } + } + else + { + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Notification Get Attempt {SiteId} {Direction} {UserId} {Count} {isRead}", siteid, direction, userid, count, isread); + HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + notifications = null; + } + + + return notifications; + } + + // GET: api//read?siteid=x&direction=to&userid=1&count=5&isread=false + [HttpGet("read-count")] + [Authorize(Roles = RoleNames.Registered)] + public int Get(string siteid, string direction, string userid, string isread) + { + int notificationsCount = 0; + + int SiteId; + int UserId; + bool IsRead; + if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId && int.TryParse(userid, out UserId) && bool.TryParse(isread, out IsRead) && IsAuthorized(UserId)) + { + if (direction == "to") + { + notificationsCount = _notifications.GetNotificationCount(SiteId, -1, UserId, IsRead); + } + else + { + notificationsCount = _notifications.GetNotificationCount(SiteId, UserId, -1, IsRead); + } + } + else + { + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Notification Get Attempt {SiteId} {Direction} {UserId} {isRead}", siteid, direction, userid, isread); + HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + notificationsCount = 0; + } + + + return notificationsCount; + } + + // GET: api/?siteid=x&type=y&userid=z [HttpGet] [Authorize(Roles = RoleNames.Registered)] @@ -72,8 +141,15 @@ namespace Oqtane.Controllers } else { - _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Notification Get Attempt {NotificationId}", id); - HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + if (notification != null) + { + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Notification Get Attempt {NotificationId}", id); + HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + } + else + { + HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; + } return null; } } diff --git a/Oqtane.Server/Controllers/PackageController.cs b/Oqtane.Server/Controllers/PackageController.cs index d4ba5c21..fe2ad700 100644 --- a/Oqtane.Server/Controllers/PackageController.cs +++ b/Oqtane.Server/Controllers/PackageController.cs @@ -34,7 +34,7 @@ namespace Oqtane.Controllers // GET: api/?type=x&search=y&price=z&package=a [HttpGet] - public async Task> Get(string type, string search, string price, string package) + public async Task> Get(string type, string search, string price, string package, string sort) { // get packages List packages = new List(); @@ -44,7 +44,7 @@ namespace Oqtane.Controllers { client.DefaultRequestHeaders.Add("Referer", HttpContext.Request.Scheme + "://" + HttpContext.Request.Host.Value); client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue(Constants.PackageId, Constants.Version)); - packages = await GetJson>(client, Constants.PackageRegistryUrl + $"/api/registry/packages/?id={_configManager.GetInstallationId()}&type={type.ToLower()}&version={Constants.Version}&search={search}&price={price}&package={package}"); + packages = await GetJson>(client, Constants.PackageRegistryUrl + $"/api/registry/packages/?id={_configManager.GetInstallationId()}&type={type.ToLower()}&version={Constants.Version}&search={search}&price={price}&package={package}&sort={sort}"); } } return packages; diff --git a/Oqtane.Server/Controllers/PageController.cs b/Oqtane.Server/Controllers/PageController.cs index f22f7467..a23828f7 100644 --- a/Oqtane.Server/Controllers/PageController.cs +++ b/Oqtane.Server/Controllers/PageController.cs @@ -7,14 +7,9 @@ using System.Linq; using Oqtane.Security; using System.Net; using Oqtane.Enums; -using Oqtane.Extensions; using Oqtane.Infrastructure; using Oqtane.Repository; -using Oqtane.Modules.Admin.Users; using System.IO; -using Oqtane.Services; -using Oqtane.UI; -using System; namespace Oqtane.Controllers { @@ -92,8 +87,15 @@ namespace Oqtane.Controllers } else { - _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Page Get Attempt {PageId}", id); - HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + if (page != null) + { + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Page Get Attempt {PageId}", id); + HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + } + else + { + HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; + } return null; } } @@ -112,8 +114,15 @@ namespace Oqtane.Controllers } else { - _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Page Get Attempt {SiteId} {Path}", siteid, path); - HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + if (page != null) + { + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Page Get Attempt {SiteId} {Path}", siteid, path); + HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + } + else + { + HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; + } return null; } } @@ -180,64 +189,68 @@ namespace Oqtane.Controllers User user = _userPermissions.GetUser(User); if (parent != null && parent.SiteId == _alias.SiteId && parent.IsPersonalizable && user.UserId == int.Parse(userid)) { - page = new Page(); - page.SiteId = parent.SiteId; - page.ParentId = parent.PageId; - page.Name = user.DisplayName; - page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(page.Name); - page.Title = page.Name + " - " + parent.Name; - page.Order = 0; - page.IsNavigation = false; - page.Url = ""; - page.ThemeType = parent.ThemeType; - page.DefaultContainerType = parent.DefaultContainerType; - page.Icon = parent.Icon; - page.PermissionList = new List() + page = _pages.GetPage(parent.Path + "/" + user.Username, parent.SiteId); + if (page == null) { - new Permission(PermissionNames.View, int.Parse(userid), true), - new Permission(PermissionNames.View, RoleNames.Everyone, true), - new Permission(PermissionNames.Edit, int.Parse(userid), true) - }; - page.IsPersonalizable = false; - page.UserId = int.Parse(userid); - page = _pages.AddPage(page); - - // copy modules - List pagemodules = _pageModules.GetPageModules(page.SiteId).ToList(); - foreach (PageModule pm in pagemodules.Where(item => item.PageId == parent.PageId && !item.IsDeleted)) - { - Module module = new Module(); - module.SiteId = page.SiteId; - module.PageId = page.PageId; - module.ModuleDefinitionName = pm.Module.ModuleDefinitionName; - module.AllPages = false; - module.PermissionList = new List() + page = new Page(); + page.SiteId = parent.SiteId; + page.ParentId = parent.PageId; + page.Name = (!string.IsNullOrEmpty(user.DisplayName)) ? user.DisplayName : user.Username; + page.Path = parent.Path + "/" + user.Username; + page.Title = page.Name + " - " + parent.Name; + page.Order = 0; + page.IsNavigation = false; + page.Url = ""; + page.ThemeType = parent.ThemeType; + page.DefaultContainerType = parent.DefaultContainerType; + page.Icon = parent.Icon; + page.PermissionList = new List() { new Permission(PermissionNames.View, int.Parse(userid), true), new Permission(PermissionNames.View, RoleNames.Everyone, true), new Permission(PermissionNames.Edit, int.Parse(userid), true) }; - module = _modules.AddModule(module); + page.IsPersonalizable = false; + page.UserId = int.Parse(userid); + page = _pages.AddPage(page); - string content = _modules.ExportModule(pm.ModuleId); - if (content != "") + // copy modules + List pagemodules = _pageModules.GetPageModules(page.SiteId).ToList(); + foreach (PageModule pm in pagemodules.Where(item => item.PageId == parent.PageId && !item.IsDeleted)) { - _modules.ImportModule(module.ModuleId, content); + Module module = new Module(); + module.SiteId = page.SiteId; + module.PageId = page.PageId; + module.ModuleDefinitionName = pm.Module.ModuleDefinitionName; + module.AllPages = false; + module.PermissionList = new List() + { + new Permission(PermissionNames.View, int.Parse(userid), true), + new Permission(PermissionNames.View, RoleNames.Everyone, true), + new Permission(PermissionNames.Edit, int.Parse(userid), true) + }; + module = _modules.AddModule(module); + + string content = _modules.ExportModule(pm.ModuleId); + if (content != "") + { + _modules.ImportModule(module.ModuleId, content); + } + + PageModule pagemodule = new PageModule(); + pagemodule.PageId = page.PageId; + pagemodule.ModuleId = module.ModuleId; + pagemodule.Title = pm.Title; + pagemodule.Pane = pm.Pane; + pagemodule.Order = pm.Order; + pagemodule.ContainerType = pm.ContainerType; + + _pageModules.AddPageModule(pagemodule); } - PageModule pagemodule = new PageModule(); - pagemodule.PageId = page.PageId; - pagemodule.ModuleId = module.ModuleId; - pagemodule.Title = pm.Title; - pagemodule.Pane = pm.Pane; - pagemodule.Order = pm.Order; - pagemodule.ContainerType = pm.ContainerType; - - _pageModules.AddPageModule(pagemodule); + _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Page, page.PageId, SyncEventActions.Create); + _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, page.SiteId, SyncEventActions.Refresh); } - - _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Page, page.PageId, SyncEventActions.Create); - _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, page.SiteId, SyncEventActions.Refresh); } else { diff --git a/Oqtane.Server/Controllers/PageModuleController.cs b/Oqtane.Server/Controllers/PageModuleController.cs index c5b94e6d..3c445772 100644 --- a/Oqtane.Server/Controllers/PageModuleController.cs +++ b/Oqtane.Server/Controllers/PageModuleController.cs @@ -44,8 +44,15 @@ namespace Oqtane.Controllers } else { - _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized PageModule Get Attempt {PageModuleId}", id); - HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + if (pagemodule != null) + { + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized PageModule Get Attempt {PageModuleId}", id); + HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + } + else + { + HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; + } return null; } } @@ -61,8 +68,15 @@ namespace Oqtane.Controllers } else { - _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized PageModule Get Attempt {PageId} {ModuleId}", pageid, moduleid); - HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + if (pagemodule != null) + { + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized PageModule Get Attempt {PageId} {ModuleId}", pageid, moduleid); + HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + } + else + { + HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; + } return null; } } diff --git a/Oqtane.Server/Controllers/ProfileController.cs b/Oqtane.Server/Controllers/ProfileController.cs index 04f2c1bd..13e4e859 100644 --- a/Oqtane.Server/Controllers/ProfileController.cs +++ b/Oqtane.Server/Controllers/ProfileController.cs @@ -56,8 +56,15 @@ namespace Oqtane.Controllers } else { - _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Profile Get Attempt {ProfileId}", id); - HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + if (profile != null) + { + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Profile Get Attempt {ProfileId}", id); + HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + } + else + { + HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; + } return null; } } diff --git a/Oqtane.Server/Controllers/RoleController.cs b/Oqtane.Server/Controllers/RoleController.cs index 7a48bdc0..15a880ef 100644 --- a/Oqtane.Server/Controllers/RoleController.cs +++ b/Oqtane.Server/Controllers/RoleController.cs @@ -59,9 +59,16 @@ namespace Oqtane.Controllers return role; } else - { - _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Role Get Attempt {RoleId}", id); - HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + { + if (role != null) + { + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Role Get Attempt {RoleId}", id); + HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + } + else + { + HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; + } return null; } } diff --git a/Oqtane.Server/Controllers/SettingController.cs b/Oqtane.Server/Controllers/SettingController.cs index c341155a..9bb9fb8e 100644 --- a/Oqtane.Server/Controllers/SettingController.cs +++ b/Oqtane.Server/Controllers/SettingController.cs @@ -89,11 +89,15 @@ namespace Oqtane.Controllers } else { - if (entityName != EntityNames.Visitor) + if (setting != null && entityName != EntityNames.Visitor) { _logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access Setting {EntityName} {SettingId}", entityName, id); HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; } + else + { + HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; + } return null; } } diff --git a/Oqtane.Server/Controllers/SiteController.cs b/Oqtane.Server/Controllers/SiteController.cs index ccf89e01..f239eb14 100644 --- a/Oqtane.Server/Controllers/SiteController.cs +++ b/Oqtane.Server/Controllers/SiteController.cs @@ -79,7 +79,7 @@ namespace Oqtane.Controllers private Site GetSite(int siteid) { var site = _sites.GetSite(siteid); - if (site.SiteId == _alias.SiteId) + if (site != null && site.SiteId == _alias.SiteId) { // site settings site.Settings = _settings.GetSettings(EntityNames.Site, site.SiteId) @@ -153,8 +153,15 @@ namespace Oqtane.Controllers } else { - _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Site Get Attempt {SiteId}", siteid); - HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + if (site != null) + { + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Site Get Attempt {SiteId}", siteid); + HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + } + else + { + HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; + } return null; } } diff --git a/Oqtane.Server/Controllers/UrlMappingController.cs b/Oqtane.Server/Controllers/UrlMappingController.cs index ddfd2ddb..a81e2c35 100644 --- a/Oqtane.Server/Controllers/UrlMappingController.cs +++ b/Oqtane.Server/Controllers/UrlMappingController.cs @@ -55,9 +55,16 @@ namespace Oqtane.Controllers return urlMapping; } else - { - _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized UrlMapping Get Attempt {UrlMappingId}", id); - HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + { + if (urlMapping != null) + { + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized UrlMapping Get Attempt {UrlMappingId}", id); + HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + } + else + { + HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; + } return null; } } @@ -73,8 +80,15 @@ namespace Oqtane.Controllers } else { - _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized UrlMapping Get Attempt {SiteId} {Url}", siteid, url); - HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + if (urlMapping != null) + { + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized UrlMapping Get Attempt {SiteId} {Url}", siteid, url); + HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + } + else + { + HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; + } return null; } } diff --git a/Oqtane.Server/Controllers/UserController.cs b/Oqtane.Server/Controllers/UserController.cs index 0090dd43..1055012a 100644 --- a/Oqtane.Server/Controllers/UserController.cs +++ b/Oqtane.Server/Controllers/UserController.cs @@ -65,6 +65,10 @@ namespace Oqtane.Controllers user.SiteId = int.Parse(siteid); user.Roles = GetUserRoles(user.UserId, user.SiteId); } + else + { + HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; + } return Filter(user); } else @@ -88,6 +92,10 @@ namespace Oqtane.Controllers user.SiteId = int.Parse(siteid); user.Roles = GetUserRoles(user.UserId, user.SiteId); } + else + { + HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; + } return Filter(user); } else @@ -648,10 +656,14 @@ namespace Oqtane.Controllers foreach (UserRole userrole in userroles) { roles += userrole.Role.Name + ";"; - if (userrole.Role.Name == RoleNames.Host && userroles.Where(item => item.Role.Name == RoleNames.Admin).FirstOrDefault() == null) + if (userrole.Role.Name == RoleNames.Host && !userroles.Any(item => item.Role.Name == RoleNames.Admin)) { roles += RoleNames.Admin + ";"; } + if (userrole.Role.Name == RoleNames.Host && !userroles.Any(item => item.Role.Name == RoleNames.Registered)) + { + roles += RoleNames.Registered + ";"; + } } if (roles != "") roles = ";" + roles; return roles; diff --git a/Oqtane.Server/Controllers/UserRoleController.cs b/Oqtane.Server/Controllers/UserRoleController.cs index ab083144..a33872f3 100644 --- a/Oqtane.Server/Controllers/UserRoleController.cs +++ b/Oqtane.Server/Controllers/UserRoleController.cs @@ -79,8 +79,15 @@ namespace Oqtane.Controllers } else { - _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized User Role Get Attempt {UserRoleId}", id); - HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + if (userrole != null) + { + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized User Role Get Attempt {UserRoleId}", id); + HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + } + else + { + HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; + } return null; } } diff --git a/Oqtane.Server/Controllers/VisitorController.cs b/Oqtane.Server/Controllers/VisitorController.cs index c4a8de73..46bf8935 100644 --- a/Oqtane.Server/Controllers/VisitorController.cs +++ b/Oqtane.Server/Controllers/VisitorController.cs @@ -64,8 +64,15 @@ namespace Oqtane.Controllers } else { - _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Visitor Get Attempt {VisitorId}", id); - HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + if (visitor != null) + { + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Visitor Get Attempt {VisitorId}", id); + HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + } + else + { + HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; + } return null; } } diff --git a/Oqtane.Server/Migrations/Tenant/04000101_AddNotificationIsRead.cs b/Oqtane.Server/Migrations/Tenant/04000101_AddNotificationIsRead.cs new file mode 100644 index 00000000..c0f610a1 --- /dev/null +++ b/Oqtane.Server/Migrations/Tenant/04000101_AddNotificationIsRead.cs @@ -0,0 +1,35 @@ +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Oqtane.Databases.Interfaces; +using Oqtane.Migrations.EntityBuilders; +using Oqtane.Repository; +using Oqtane.Shared; + +namespace Oqtane.Migrations.Tenant +{ + [DbContext(typeof(TenantDBContext))] + [Migration("Tenant.04.00.01.01")] + public class AddNotificationIsRead : MultiDatabaseMigration + { + + public AddNotificationIsRead(IDatabase database) : base(database) + { + } + + protected override void Up(MigrationBuilder migrationBuilder) + { + var notificationEntityBuilder = new NotificationEntityBuilder(migrationBuilder, ActiveDatabase); + notificationEntityBuilder.AddBooleanColumn("IsRead", true); + notificationEntityBuilder.UpdateColumn("IsRead", "1", "bool", ""); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + var notificationEntityBuilder = new NotificationEntityBuilder(migrationBuilder, ActiveDatabase); + notificationEntityBuilder.DropColumn("IsRead"); + } + + } + + +} diff --git a/Oqtane.Server/Oqtane.Server.csproj b/Oqtane.Server/Oqtane.Server.csproj index 780e0400..4b8cc4bf 100644 --- a/Oqtane.Server/Oqtane.Server.csproj +++ b/Oqtane.Server/Oqtane.Server.csproj @@ -3,7 +3,7 @@ net7.0 Debug;Release - 4.0.0 + 4.0.1 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.0 + https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1 https://github.com/oqtane/oqtane.framework Git Oqtane diff --git a/Oqtane.Server/Repository/Interfaces/INotificationRepository.cs b/Oqtane.Server/Repository/Interfaces/INotificationRepository.cs index 34fb58be..948d7b53 100644 --- a/Oqtane.Server/Repository/Interfaces/INotificationRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/INotificationRepository.cs @@ -6,6 +6,8 @@ namespace Oqtane.Repository public interface INotificationRepository { IEnumerable GetNotifications(int siteId, int fromUserId, int toUserId); + IEnumerable GetNotifications(int siteId, int fromUserId, int toUserId, int count, bool isRead); + int GetNotificationCount(int siteId, int fromUserId, int toUserId, bool isRead); Notification AddNotification(Notification notification); Notification UpdateNotification(Notification notification); Notification GetNotification(int notificationId); diff --git a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs index a82cce99..93d0f7f4 100644 --- a/Oqtane.Server/Repository/ModuleDefinitionRepository.cs +++ b/Oqtane.Server/Repository/ModuleDefinitionRepository.cs @@ -101,6 +101,7 @@ namespace Oqtane.Repository ModuleDefinition.IsPortable = moduleDefinition.IsPortable; ModuleDefinition.Resources = moduleDefinition.Resources; ModuleDefinition.IsEnabled = moduleDefinition.IsEnabled; + ModuleDefinition.PackageName = moduleDefinition.PackageName; } return ModuleDefinition; diff --git a/Oqtane.Server/Repository/NotificationRepository.cs b/Oqtane.Server/Repository/NotificationRepository.cs index 7596ee94..43f9b386 100644 --- a/Oqtane.Server/Repository/NotificationRepository.cs +++ b/Oqtane.Server/Repository/NotificationRepository.cs @@ -33,6 +33,54 @@ namespace Oqtane.Repository .ToList(); } + public IEnumerable GetNotifications(int siteId, int fromUserId, int toUserId, int count, bool isRead) + { + if (toUserId == -1 && fromUserId == -1) + { + return _db.Notification + .Where(item => item.SiteId == siteId) + .Where(item => item.IsDelivered == false && item.IsDeleted == false) + .Where(item => item.SendOn == null || item.SendOn < System.DateTime.UtcNow) + .Where(item => item.IsRead == isRead) + .OrderByDescending(item => item.CreatedOn) + .ToList() + .Take(count); + } + + return _db.Notification + .Where(item => item.SiteId == siteId) + .Where(item => item.ToUserId == toUserId || toUserId == -1) + .Where(item => item.FromUserId == fromUserId || fromUserId == -1) + .Where(item => item.IsRead == isRead) + .OrderByDescending(item => item.CreatedOn) + .ToList() + .Take(count); + } + + public int GetNotificationCount(int siteId, int fromUserId, int toUserId, bool isRead) + { + if (toUserId == -1 && fromUserId == -1) + { + return _db.Notification + .Where(item => item.SiteId == siteId) + .Where(item => item.IsDelivered == false && item.IsDeleted == false) + .Where(item => item.SendOn == null || item.SendOn < System.DateTime.UtcNow) + .Where(item => item.IsRead == isRead) + .ToList() + .Count(); + + } + + return _db.Notification + .Where(item => item.SiteId == siteId) + .Where(item => item.ToUserId == toUserId || toUserId == -1) + .Where(item => item.FromUserId == fromUserId || fromUserId == -1) + .Where(item => item.IsRead == isRead) + .ToList() + .Count(); + } + + public Notification AddNotification(Notification notification) { _db.Notification.Add(notification); diff --git a/Oqtane.Server/Repository/ThemeRepository.cs b/Oqtane.Server/Repository/ThemeRepository.cs index 02e4a9c0..d34d2f31 100644 --- a/Oqtane.Server/Repository/ThemeRepository.cs +++ b/Oqtane.Server/Repository/ThemeRepository.cs @@ -89,6 +89,7 @@ namespace Oqtane.Repository Theme.Containers = theme.Containers; Theme.ThemeSettingsType = theme.ThemeSettingsType; Theme.ContainerSettingsType = theme.ContainerSettingsType; + Theme.PackageName = theme.PackageName; Themes.Add(Theme); } diff --git a/Oqtane.Server/wwwroot/Modules/Templates/External/Client/Modules/[Owner].Module.[Module]/Settings.razor b/Oqtane.Server/wwwroot/Modules/Templates/External/Client/Modules/[Owner].Module.[Module]/Settings.razor index d8f9bfaf..a29842ff 100644 --- a/Oqtane.Server/wwwroot/Modules/Templates/External/Client/Modules/[Owner].Module.[Module]/Settings.razor +++ b/Oqtane.Server/wwwroot/Modules/Templates/External/Client/Modules/[Owner].Module.[Module]/Settings.razor @@ -13,7 +13,7 @@
@code { - private string resourceType = "[Owner].[Module].Settings, [Owner].[Module].Client.Oqtane"; // for localization + private string resourceType = "[Owner].Module.[Module].Settings, [Owner].Module.[Module].Client.Oqtane"; // for localization public override string Title => "[Module] Settings"; string _value; diff --git a/Oqtane.Server/wwwroot/Modules/Templates/External/[Owner].Module.[Module].sln b/Oqtane.Server/wwwroot/Modules/Templates/External/[Owner].Module.[Module].sln index a9cbde70..e8622beb 100644 --- a/Oqtane.Server/wwwroot/Modules/Templates/External/[Owner].Module.[Module].sln +++ b/Oqtane.Server/wwwroot/Modules/Templates/External/[Owner].Module.[Module].sln @@ -20,9 +20,7 @@ Global EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {3AB6FCC9-EFEB-4C0E-A2CF-8103914C5196}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3AB6FCC9-EFEB-4C0E-A2CF-8103914C5196}.Debug|Any CPU.Build.0 = Debug|Any CPU {3AB6FCC9-EFEB-4C0E-A2CF-8103914C5196}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3AB6FCC9-EFEB-4C0E-A2CF-8103914C5196}.Release|Any CPU.Build.0 = Release|Any CPU {AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Debug|Any CPU.Build.0 = Debug|Any CPU {AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/Oqtane.Server/wwwroot/Packages/Oqtane.Database.MySQL.nupkg.bak b/Oqtane.Server/wwwroot/Packages/Oqtane.Database.MySQL.nupkg.bak index 72e692cf..47ba984b 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 ab6529ff..ee3324c0 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 e9cd97e6..603f3fd1 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 054330c8..697d02f4 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/Themes/Templates/External/[Owner].Theme.[Theme].sln b/Oqtane.Server/wwwroot/Themes/Templates/External/[Owner].Theme.[Theme].sln index 94aa3841..cd9d50da 100644 --- a/Oqtane.Server/wwwroot/Themes/Templates/External/[Owner].Theme.[Theme].sln +++ b/Oqtane.Server/wwwroot/Themes/Templates/External/[Owner].Theme.[Theme].sln @@ -16,9 +16,7 @@ Global EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {3AB6FCC9-EFEB-4C0E-A2CF-8103914C5196}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3AB6FCC9-EFEB-4C0E-A2CF-8103914C5196}.Debug|Any CPU.Build.0 = Debug|Any CPU {3AB6FCC9-EFEB-4C0E-A2CF-8103914C5196}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3AB6FCC9-EFEB-4C0E-A2CF-8103914C5196}.Release|Any CPU.Build.0 = Release|Any CPU {AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Debug|Any CPU.Build.0 = Debug|Any CPU {AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/Oqtane.Shared/Models/Notification.cs b/Oqtane.Shared/Models/Notification.cs index 975b28c8..d7e08c01 100644 --- a/Oqtane.Shared/Models/Notification.cs +++ b/Oqtane.Shared/Models/Notification.cs @@ -94,6 +94,10 @@ namespace Oqtane.Models /// public DateTime? SendOn { get; set; } + /// + /// If it has been read. See also + /// + public bool IsRead { get; set; } // constructors public Notification() {} @@ -174,6 +178,7 @@ namespace Oqtane.Models } IsDelivered = false; DeliveredOn = null; + IsRead = false; } } diff --git a/Oqtane.Shared/Models/Package.cs b/Oqtane.Shared/Models/Package.cs index 1a57e7bc..1e424744 100644 --- a/Oqtane.Shared/Models/Package.cs +++ b/Oqtane.Shared/Models/Package.cs @@ -32,6 +32,11 @@ namespace Oqtane.Models /// public string Description { get; set; } + /// + /// logo + /// + public int? LogoFileId { get; set; } + /// /// License for the Package. /// @@ -62,6 +67,11 @@ namespace Oqtane.Models /// public string PackageUrl { get; set; } + /// + /// The direct Url for getting support for the product + /// + public string SupportUrl { get; set; } + /// /// Indicates if any known security vulnerabilities exist /// diff --git a/Oqtane.Shared/Oqtane.Shared.csproj b/Oqtane.Shared/Oqtane.Shared.csproj index 793a2db5..e6062c0e 100644 --- a/Oqtane.Shared/Oqtane.Shared.csproj +++ b/Oqtane.Shared/Oqtane.Shared.csproj @@ -3,7 +3,7 @@ net7.0 Debug;Release - 4.0.0 + 4.0.1 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.0 + https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1 https://github.com/oqtane/oqtane.framework Git Oqtane diff --git a/Oqtane.Shared/Shared/Constants.cs b/Oqtane.Shared/Shared/Constants.cs index 916fa310..79c7ada9 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.0"; - 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"; + 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 const string PackageId = "Oqtane.Framework"; public const string ClientId = "Oqtane.Client"; public const string UpdaterPackageId = "Oqtane.Updater"; diff --git a/Oqtane.Updater/Oqtane.Updater.csproj b/Oqtane.Updater/Oqtane.Updater.csproj index eb79be2f..d7f544f2 100644 --- a/Oqtane.Updater/Oqtane.Updater.csproj +++ b/Oqtane.Updater/Oqtane.Updater.csproj @@ -3,7 +3,7 @@ net7.0 Exe - 4.0.0 + 4.0.1 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.0 + https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1 https://github.com/oqtane/oqtane.framework Git Oqtane