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

This commit is contained in:
Leigh Pointer
2025-05-15 11:52:51 +02:00
77 changed files with 826 additions and 421 deletions

View File

@ -20,14 +20,16 @@ namespace Oqtane.Controllers
{
private readonly IFolderRepository _folders;
private readonly IUserPermissions _userPermissions;
private readonly IFileRepository _files;
private readonly ISyncManager _syncManager;
private readonly ILogManager _logger;
private readonly Alias _alias;
public FolderController(IFolderRepository folders, IUserPermissions userPermissions, ISyncManager syncManager, ILogManager logger, ITenantManager tenantManager)
public FolderController(IFolderRepository folders, IUserPermissions userPermissions, IFileRepository files, ISyncManager syncManager, ILogManager logger, ITenantManager tenantManager)
{
_folders = folders;
_userPermissions = userPermissions;
_files = files;
_syncManager = syncManager;
_logger = logger;
_alias = tenantManager.GetAlias();
@ -41,7 +43,8 @@ namespace Oqtane.Controllers
int SiteId;
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId)
{
foreach (Folder folder in _folders.GetFolders(SiteId))
var hierarchy = GetFoldersHierarchy(_folders.GetFolders(SiteId).ToList());
foreach (Folder folder in hierarchy)
{
// note that Browse permission is used for this method
if (_userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.PermissionList))
@ -49,7 +52,6 @@ namespace Oqtane.Controllers
folders.Add(folder);
}
}
folders = GetFoldersHierarchy(folders);
}
else
{
@ -244,34 +246,6 @@ namespace Oqtane.Controllers
return folder;
}
// PUT api/<controller>/?siteid=x&folderid=y&parentid=z
[HttpPut]
[Authorize(Roles = RoleNames.Registered)]
public void Put(int siteid, int folderid, int? parentid)
{
if (siteid == _alias.SiteId && _folders.GetFolder(folderid, false) != null && _userPermissions.IsAuthorized(User, siteid, EntityNames.Folder, folderid, PermissionNames.Edit))
{
int order = 1;
List<Folder> folders = _folders.GetFolders(siteid).ToList();
foreach (Folder folder in folders.Where(item => item.ParentId == parentid).OrderBy(item => item.Order))
{
if (folder.Order != order)
{
folder.Order = order;
_folders.UpdateFolder(folder);
_syncManager.AddSyncEvent(_alias, EntityNames.Folder, folder.FolderId, SyncEventActions.Update);
}
order += 2;
}
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Folder Order Updated {SiteId} {FolderId} {ParentId}", siteid, folderid, parentid);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Update, "Unauthorized Folder Put Attempt {SiteId} {FolderId} {ParentId}", siteid, folderid, parentid);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
}
// DELETE api/<controller>/5
[HttpDelete("{id}")]
[Authorize(Roles = RoleNames.Registered)]
@ -283,12 +257,20 @@ namespace Oqtane.Controllers
var folderPath = _folders.GetFolderPath(folder);
if (Directory.Exists(folderPath))
{
// remove all files from disk (including thumbnails, etc...)
foreach (var filePath in Directory.GetFiles(folderPath))
{
System.IO.File.Delete(filePath);
}
Directory.Delete(folderPath);
}
// remove files from database
foreach (var file in _files.GetFiles(id))
{
_files.DeleteFile(file.FileId);
}
_folders.DeleteFolder(id);
_syncManager.AddSyncEvent(_alias, EntityNames.Folder, folder.FolderId, SyncEventActions.Delete);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Folder Deleted {FolderId}", id);
@ -304,7 +286,6 @@ namespace Oqtane.Controllers
{
List<Folder> hierarchy = new List<Folder>();
Action<List<Folder>, Folder> getPath = null;
var folders1 = folders;
getPath = (folderList, folder) =>
{
IEnumerable<Folder> children;
@ -312,23 +293,23 @@ namespace Oqtane.Controllers
if (folder == null)
{
level = -1;
children = folders1.Where(item => item.ParentId == null);
children = folders.Where(item => item.ParentId == null);
}
else
{
level = folder.Level;
children = folders1.Where(item => item.ParentId == folder.FolderId);
children = folders.Where(item => item.ParentId == folder.FolderId);
}
foreach (Folder child in children)
{
child.Level = level + 1;
child.HasChildren = folders1.Any(item => item.ParentId == child.FolderId);
child.HasChildren = folders.Any(item => item.ParentId == child.FolderId);
hierarchy.Add(child);
if (getPath != null) getPath(folderList, child);
getPath(folderList, child);
}
};
folders = folders.OrderBy(item => item.Order).ToList();
folders = folders.OrderBy(item => item.Name).ToList();
getPath(folders, null);
// add any non-hierarchical items to the end of the list

View File

@ -76,6 +76,8 @@ namespace Oqtane.Controllers
module.ContainerType = pagemodule.ContainerType;
module.EffectiveDate = pagemodule.EffectiveDate;
module.ExpiryDate = pagemodule.ExpiryDate;
module.Header = pagemodule.Header;
module.Footer = pagemodule.Footer;
module.ModuleDefinition = _moduleDefinitions.FilterModuleDefinition(moduledefinitions.Find(item => item.ModuleDefinitionName == module.ModuleDefinitionName));

View File

@ -246,6 +246,10 @@ namespace Oqtane.Controllers
pagemodule.Pane = pm.Pane;
pagemodule.Order = pm.Order;
pagemodule.ContainerType = pm.ContainerType;
pagemodule.EffectiveDate = pm.EffectiveDate;
pagemodule.ExpiryDate = pm.ExpiryDate;
pagemodule.Header = pm.Header;
pagemodule.Footer = pm.Footer;
_pageModules.AddPageModule(pagemodule);
}
@ -295,38 +299,43 @@ namespace Oqtane.Controllers
var removed = GetPermissionsDifferences(currentPermissions, page.PermissionList);
// synchronize module permissions
if (added.Count > 0 || removed.Count > 0)
if (page.UpdateModulePermissions && (added.Count > 0 || removed.Count > 0))
{
foreach (PageModule pageModule in _pageModules.GetPageModules(page.SiteId).Where(item => item.PageId == page.PageId).ToList())
var pageModules = _pageModules.GetPageModules(page.SiteId);
foreach (PageModule pageModule in pageModules.Where(item => item.PageId == page.PageId).ToList())
{
var modulePermissions = _permissionRepository.GetPermissions(pageModule.Module.SiteId, EntityNames.Module, pageModule.Module.ModuleId).ToList();
// permissions added
foreach (Permission permission in added)
// ignore "shared" modules
if (!pageModules.Any(item => item.ModuleId == pageModule.ModuleId && item.PageId != pageModule.PageId))
{
if (!modulePermissions.Any(item => item.PermissionName == permission.PermissionName
&& item.RoleId == permission.RoleId && item.UserId == permission.UserId && item.IsAuthorized == permission.IsAuthorized))
var modulePermissions = _permissionRepository.GetPermissions(pageModule.Module.SiteId, EntityNames.Module, pageModule.Module.ModuleId).ToList();
// permissions added
foreach (Permission permission in added)
{
_permissionRepository.AddPermission(new Permission
if (!modulePermissions.Any(item => item.PermissionName == permission.PermissionName
&& item.RoleId == permission.RoleId && item.UserId == permission.UserId && item.IsAuthorized == permission.IsAuthorized))
{
SiteId = page.SiteId,
EntityName = EntityNames.Module,
EntityId = pageModule.ModuleId,
PermissionName = permission.PermissionName,
RoleId = permission.RoleId,
UserId = permission.UserId,
IsAuthorized = permission.IsAuthorized
});
_permissionRepository.AddPermission(new Permission
{
SiteId = page.SiteId,
EntityName = EntityNames.Module,
EntityId = pageModule.ModuleId,
PermissionName = permission.PermissionName,
RoleId = permission.RoleId,
UserId = permission.UserId,
IsAuthorized = permission.IsAuthorized
});
}
}
}
// permissions removed
foreach (Permission permission in removed)
{
var modulePermission = modulePermissions.FirstOrDefault(item => item.PermissionName == permission.PermissionName
&& item.RoleId == permission.RoleId && item.UserId == permission.UserId && item.IsAuthorized == permission.IsAuthorized);
if (modulePermission != null)
// permissions removed
foreach (Permission permission in removed)
{
_permissionRepository.DeletePermission(modulePermission.PermissionId);
var modulePermission = modulePermissions.FirstOrDefault(item => item.PermissionName == permission.PermissionName
&& item.RoleId == permission.RoleId && item.UserId == permission.UserId && item.IsAuthorized == permission.IsAuthorized);
if (modulePermission != null)
{
_permissionRepository.DeletePermission(modulePermission.PermissionId);
}
}
}
}

View File

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

View File

@ -135,6 +135,7 @@ namespace Oqtane.Controllers
if (_userPermissions.IsAuthorized(User, user.SiteId, EntityNames.User, -1, PermissionNames.Write, RoleNames.Admin) || _userPermissions.GetUser(User).UserId == user.UserId)
{
filtered.Email = user.Email;
filtered.TimeZoneId = user.TimeZoneId;
filtered.PhotoFileId = user.PhotoFileId;
filtered.LastLoginOn = user.LastLoginOn;
filtered.LastIPAddress = user.LastIPAddress;

View File

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

View File

@ -0,0 +1,31 @@
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Oqtane.Databases.Interfaces;
using Oqtane.Migrations.EntityBuilders;
using Oqtane.Repository;
namespace Oqtane.Migrations.Tenant
{
[DbContext(typeof(TenantDBContext))]
[Migration("Tenant.06.01.03.01")]
public class AddTimeZone : MultiDatabaseMigration
{
public AddTimeZone(IDatabase database) : base(database)
{
}
protected override void Up(MigrationBuilder migrationBuilder)
{
var siteEntityBuilder = new SiteEntityBuilder(migrationBuilder, ActiveDatabase);
siteEntityBuilder.AddStringColumn("TimeZoneId", 50, true);
var userEntityBuilder = new UserEntityBuilder(migrationBuilder, ActiveDatabase);
userEntityBuilder.AddStringColumn("TimeZoneId", 50, true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
// not implemented
}
}
}

View File

@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Oqtane.Databases.Interfaces;
using Oqtane.Migrations.EntityBuilders;
using Oqtane.Repository;
namespace Oqtane.Migrations.Tenant
{
[DbContext(typeof(TenantDBContext))]
[Migration("Tenant.06.01.03.02")]
public class AddModuleHeaderFooter : MultiDatabaseMigration
{
public AddModuleHeaderFooter(IDatabase database) : base(database)
{
}
protected override void Up(MigrationBuilder migrationBuilder)
{
var pageModuleEntityBuilder = new PageModuleEntityBuilder(migrationBuilder, ActiveDatabase);
pageModuleEntityBuilder.AddMaxStringColumn("Header", true);
pageModuleEntityBuilder.AddMaxStringColumn("Footer", true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
// not implemented
}
}
}

View File

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

View File

@ -442,6 +442,8 @@ namespace Oqtane.Repository
pageModule.Pane = (string.IsNullOrEmpty(pageTemplateModule.Pane)) ? PaneNames.Default : pageTemplateModule.Pane;
pageModule.Order = (pageTemplateModule.Order == 0) ? 1 : pageTemplateModule.Order;
pageModule.ContainerType = pageTemplateModule.ContainerType;
pageModule.Header = pageTemplateModule.Header;
pageModule.Footer = pageTemplateModule.Footer;
pageModule.IsDeleted = pageTemplateModule.IsDeleted;
pageModule.Module.PermissionList = new List<Permission>();
foreach (var permission in pageTemplateModule.PermissionList)

View File

@ -285,6 +285,8 @@ namespace Oqtane.Services
ContainerType = pagemodule.ContainerType,
EffectiveDate = pagemodule.EffectiveDate,
ExpiryDate = pagemodule.ExpiryDate,
Header = pagemodule.Header,
Footer = pagemodule.Footer,
ModuleDefinition = _moduleDefinitions.FilterModuleDefinition(moduledefinitions.Find(item => item.ModuleDefinitionName == pagemodule.Module.ModuleDefinitionName)),

View File

@ -225,6 +225,9 @@ namespace Oqtane
app.UseAuthorization();
app.UseAntiforgery();
// execute any IServerStartup logic
app.ConfigureOqtaneAssemblies(env);
if (_useSwagger)
{
app.UseSwagger();
@ -245,9 +248,6 @@ namespace Oqtane
.AddAdditionalAssemblies(typeof(SiteRouter).Assembly);
});
// execute any IServerStartup logic
app.ConfigureOqtaneAssemblies(env);
// simulate the fallback routing approach of traditional Blazor - allowing the custom SiteRouter to handle all routing concerns
app.UseEndpoints(endpoints =>
{

View File

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

View File

@ -0,0 +1,4 @@
using System.Resources;
using Microsoft.Extensions.Localization;
[assembly: RootNamespace("[Owner].Module.[Module].Server")]

View File

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

View File

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