diff --git a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/CmsKitWebUnifiedModule.cs b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/CmsKitWebUnifiedModule.cs index 0ab0ae4d6d..ec98cbcf78 100644 --- a/modules/cms-kit/host/Volo.CmsKit.Web.Unified/CmsKitWebUnifiedModule.cs +++ b/modules/cms-kit/host/Volo.CmsKit.Web.Unified/CmsKitWebUnifiedModule.cs @@ -36,6 +36,13 @@ using Volo.CmsKit.Reactions; using Volo.CmsKit.Tags; using Volo.CmsKit.Web; using Volo.CmsKit.Web.Contents; +using Microsoft.AspNetCore.Routing; +using Microsoft.AspNetCore.Mvc.Routing; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Volo.Abp.DependencyInjection; +using Volo.CmsKit.Public.Pages; + #if EntityFrameworkCore using Volo.Abp.SettingManagement.EntityFrameworkCore; @@ -54,7 +61,6 @@ using Volo.Abp.TenantManagement.MongoDB; using Volo.Abp.Identity.MongoDB; using Volo.Abp.PermissionManagement.MongoDB; using Volo.Abp.FeatureManagement.MongoDB; -using Volo.Abp.MongoDB; using Volo.Abp.BlobStoring.Database.MongoDB; using Volo.Abp.AuditLogging.MongoDB; using Volo.CmsKit.MongoDB; @@ -266,6 +272,7 @@ public class CmsKitWebUnifiedModule : AbpModule app.UseAuditing(); app.UseAbpSerilogEnrichers(); + app.UseConfiguredEndpoints(); using (var scope = context.ServiceProvider.CreateScope()) @@ -278,4 +285,4 @@ public class CmsKitWebUnifiedModule : AbpModule }); } } -} +} \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Pages/PageAdminAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Pages/PageAdminAppService.cs index e858bab189..4585077a49 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Pages/PageAdminAppService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Pages/PageAdminAppService.cs @@ -66,6 +66,8 @@ public class PageAdminAppService : CmsKitAdminAppServiceBase, IPageAdminAppServi input.MapExtraPropertiesTo(page); await PageRepository.InsertAsync(page); + await PageCache.RemoveAsync(PageCacheItem.GetKey(page.Slug)); + return ObjectMapper.Map(page); } @@ -77,6 +79,8 @@ public class PageAdminAppService : CmsKitAdminAppServiceBase, IPageAdminAppServi { await InvalidateDefaultHomePageCacheAsync(considerUow: true); } + + await PageCache.RemoveAsync(PageCacheItem.GetKey(page.Slug)); await PageManager.SetSlugAsync(page, input.Slug); @@ -102,6 +106,7 @@ public class PageAdminAppService : CmsKitAdminAppServiceBase, IPageAdminAppServi } await PageRepository.DeleteAsync(page); + await PageCache.RemoveAsync(PageCacheItem.GetKey(page.Slug)); } [Authorize(CmsKitAdminPermissions.Pages.SetAsHomePage)] @@ -115,6 +120,6 @@ public class PageAdminAppService : CmsKitAdminAppServiceBase, IPageAdminAppServi protected virtual async Task InvalidateDefaultHomePageCacheAsync(bool considerUow = false) { - await PageCache.RemoveAsync(PageConsts.DefaultHomePageCacheKey, considerUow: considerUow); + await PageCache.RemoveAsync(PageCacheItem.GetKey(PageConsts.DefaultHomePageCacheKey), considerUow: considerUow); } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Pages/PageConsts.cs b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Pages/PageConsts.cs index 69788463e2..f703fbac43 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Pages/PageConsts.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Pages/PageConsts.cs @@ -16,11 +16,5 @@ public static class PageConsts public static int MaxStyleLength { get; set; } = int.MaxValue; - public static string DefaultHomePageCacheKey { get; set; } = "DefaultHomePage"; - - private static string _urlPrefix = "/"; - public static string UrlPrefix { - get => _urlPrefix; - set => _urlPrefix = value.EnsureEndsWith('/').EnsureStartsWith('/'); - } + public static string DefaultHomePageCacheKey { get; set; } = "__DefaultHomePage"; } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Menus/MenuItemManager.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Menus/MenuItemManager.cs index 16fbbbdb8e..76d95f175f 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Menus/MenuItemManager.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Menus/MenuItemManager.cs @@ -22,7 +22,7 @@ public class MenuItemManager : CmsKitDomainServiceBase public virtual void SetPageUrl(MenuItem menuItem, Page page) { menuItem.SetPageId(page.Id); - menuItem.SetUrl(PageConsts.UrlPrefix + page.Slug); + menuItem.SetUrl(page.Slug.EnsureStartsWith('/')); } [UnitOfWork] diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Pages/PageCacheItem.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Pages/PageCacheItem.cs index 71c6800e04..4dadd04e8e 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Pages/PageCacheItem.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Pages/PageCacheItem.cs @@ -16,4 +16,9 @@ public class PageCacheItem : ExtensibleObject public string Script { get; set; } public string Style { get; set; } + + public static string GetKey(string slug) + { + return $"CmsPage_{slug}"; + } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Pages/PageManager.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Pages/PageManager.cs index c11a64689a..587ae91144 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Pages/PageManager.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Pages/PageManager.cs @@ -37,7 +37,7 @@ public class PageManager : DomainService CurrentTenant.Id); } - public virtual async Task SetSlugAsync(Page page, string newSlug) + public virtual async Task SetSlugAsync(Page page, [NotNull] string newSlug) { if (page.Slug != newSlug) { diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/SlugNormalizer.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/SlugNormalizer.cs index d7529441b1..0e3b229620 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/SlugNormalizer.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/SlugNormalizer.cs @@ -5,9 +5,16 @@ namespace Volo.CmsKit; public static class SlugNormalizer { - static readonly SlugHelper SlugHelper = new(); + static readonly SlugHelper SlugHelper = new(new SlugHelperConfiguration + { + AllowedChars = + { + '/' + } + }); + public static string Normalize(string value) { - return SlugHelper.GenerateSlug(value?.Unidecode()); + return SlugHelper.GenerateSlug(value?.Unidecode()).Trim('/'); } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Pages/IPagePublicAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Pages/IPagePublicAppService.cs index bec4261f19..93ebb3df38 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Pages/IPagePublicAppService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Pages/IPagePublicAppService.cs @@ -8,5 +8,6 @@ namespace Volo.CmsKit.Public.Pages; public interface IPagePublicAppService : IApplicationService { Task FindBySlugAsync([NotNull] string slug); + Task DoesSlugExistAsync([NotNull] string slug); Task FindDefaultHomePageAsync(); } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Pages/PagePublicAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Pages/PagePublicAppService.cs index 9940dceab1..cd0191e20d 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Pages/PagePublicAppService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Pages/PagePublicAppService.cs @@ -1,5 +1,6 @@ using System; using System.Threading.Tasks; +using JetBrains.Annotations; using Microsoft.Extensions.Caching.Distributed; using Volo.Abp.Caching; using Volo.Abp.Features; @@ -16,31 +17,35 @@ namespace Volo.CmsKit.Public.Pages; public class PagePublicAppService : CmsKitPublicAppServiceBase, IPagePublicAppService { protected IPageRepository PageRepository { get; } - protected PageManager PageManager { get; } + protected PageManager PageManager { get; } protected IDistributedCache PageCache { get; } - public PagePublicAppService(IPageRepository pageRepository, PageManager pageManager, IDistributedCache pageCache) + public PagePublicAppService( + IPageRepository pageRepository, + PageManager pageManager, + IDistributedCache pageCache) { PageRepository = pageRepository; PageManager = pageManager; - PageCache = pageCache; + PageCache = pageCache; } public virtual async Task FindBySlugAsync(string slug) { - var page = await PageRepository.FindBySlugAsync(slug); - if (page == null) + var cachedPage = await FindAndCacheBySlugAsync(slug); + + if (cachedPage == null) { return null; } - return ObjectMapper.Map(page); + return ObjectMapper.Map(cachedPage); } public virtual async Task FindDefaultHomePageAsync() { - var pageCacheItem = await PageCache.GetAsync(PageConsts.DefaultHomePageCacheKey); + var pageCacheItem = await PageCache.GetAsync(PageCacheItem.GetKey(PageConsts.DefaultHomePageCacheKey)); if (pageCacheItem is null) { var page = await PageManager.GetHomePageAsync(); @@ -51,10 +56,34 @@ public class PagePublicAppService : CmsKitPublicAppServiceBase, IPagePublicAppSe pageCacheItem = ObjectMapper.Map(page); - await PageCache.SetAsync(PageConsts.DefaultHomePageCacheKey, pageCacheItem, + await PageCache.SetAsync(PageCacheItem.GetKey(PageConsts.DefaultHomePageCacheKey), pageCacheItem, new DistributedCacheEntryOptions { AbsoluteExpiration = DateTimeOffset.Now.AddHours(1) }); } return ObjectMapper.Map(pageCacheItem); } + + public virtual async Task DoesSlugExistAsync([NotNull] string slug) + { + var cached = await FindAndCacheBySlugAsync(slug); + + return cached != null; + } + + internal virtual async Task FindAndCacheBySlugAsync(string slug) + { + var pageCacheItem = await PageCache.GetOrAddAsync(PageCacheItem.GetKey(slug), async () => + { + var page = await PageRepository.FindBySlugAsync(slug); + // If page is not found, cache it as null to prevent further queries. + if (page is null) + { + return null; + } + + return ObjectMapper.Map(page); + }); + + return pageCacheItem; + } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/ClientProxies/Volo/CmsKit/Public/Pages/PagesPublicClientProxy.Generated.cs b/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/ClientProxies/Volo/CmsKit/Public/Pages/PagesPublicClientProxy.Generated.cs index 6a7df3e4e2..4fb1e8bc9f 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/ClientProxies/Volo/CmsKit/Public/Pages/PagesPublicClientProxy.Generated.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/ClientProxies/Volo/CmsKit/Public/Pages/PagesPublicClientProxy.Generated.cs @@ -30,4 +30,12 @@ public partial class PagesPublicClientProxy : ClientProxyBase(nameof(FindDefaultHomePageAsync)); } + + public virtual async Task DoesSlugExistAsync(string slug) + { + return await RequestAsync(nameof(DoesSlugExistAsync), new ClientProxyRequestTypeValue + { + { typeof(string), slug } + }); + } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/ClientProxies/cms-kit-generate-proxy.json b/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/ClientProxies/cms-kit-generate-proxy.json index bfde04ba1d..477938b6de 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/ClientProxies/cms-kit-generate-proxy.json +++ b/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi.Client/ClientProxies/cms-kit-generate-proxy.json @@ -829,6 +829,23 @@ "typeSimple": "Volo.CmsKit.Contents.PageDto" } }, + { + "name": "DoesSlugExistAsync", + "parametersOnMethod": [ + { + "name": "slug", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "returnValue": { + "type": "System.Boolean", + "typeSimple": "boolean" + } + }, { "name": "FindDefaultHomePageAsync", "parametersOnMethod": [], @@ -845,7 +862,7 @@ "uniqueName": "FindBySlugAsyncBySlug", "name": "FindBySlugAsync", "httpMethod": "GET", - "url": "api/cms-kit-public/pages/{slug}", + "url": "api/cms-kit-public/pages/by-slug", "supportedVersions": [], "parametersOnMethod": [ { @@ -866,8 +883,8 @@ "typeSimple": "string", "isOptional": false, "defaultValue": null, - "constraintTypes": [], - "bindingSourceId": "Path", + "constraintTypes": null, + "bindingSourceId": "Query", "descriptorName": "" } ], @@ -882,7 +899,7 @@ "uniqueName": "FindDefaultHomePageAsync", "name": "FindDefaultHomePageAsync", "httpMethod": "GET", - "url": "api/cms-kit-public/pages", + "url": "api/cms-kit-public/pages/home", "supportedVersions": [], "parametersOnMethod": [], "parameters": [], @@ -892,6 +909,43 @@ }, "allowAnonymous": null, "implementFrom": "Volo.CmsKit.Public.Pages.IPagePublicAppService" + }, + "DoesSlugExistAsyncBySlug": { + "uniqueName": "DoesSlugExistAsyncBySlug", + "name": "DoesSlugExistAsync", + "httpMethod": "GET", + "url": "api/cms-kit-public/pages/exist", + "supportedVersions": [], + "parametersOnMethod": [ + { + "name": "slug", + "typeAsString": "System.String, System.Private.CoreLib", + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null + } + ], + "parameters": [ + { + "nameOnMethod": "slug", + "name": "slug", + "jsonName": null, + "type": "System.String", + "typeSimple": "string", + "isOptional": false, + "defaultValue": null, + "constraintTypes": null, + "bindingSourceId": "Query", + "descriptorName": "" + } + ], + "returnValue": { + "type": "System.Boolean", + "typeSimple": "boolean" + }, + "allowAnonymous": null, + "implementFrom": "Volo.CmsKit.Public.Pages.IPagePublicAppService" } } }, diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi/Volo/CmsKit/Public/Pages/PagesPublicController.cs b/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi/Volo/CmsKit/Public/Pages/PagesPublicController.cs index d6550977ef..55534f8ac1 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi/Volo/CmsKit/Public/Pages/PagesPublicController.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi/Volo/CmsKit/Public/Pages/PagesPublicController.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using JetBrains.Annotations; using Microsoft.AspNetCore.Mvc; using Volo.Abp; using Volo.Abp.Features; @@ -24,15 +25,23 @@ public class PagesPublicController : CmsKitPublicControllerBase, IPagePublicAppS } [HttpGet] - [Route("{slug}")] - public virtual Task FindBySlugAsync(string slug) + [Route("by-slug")] + public virtual Task FindBySlugAsync([FromQuery]string slug) { return PageAppService.FindBySlugAsync(slug); } [HttpGet] - public Task FindDefaultHomePageAsync() + [Route("home")] + public virtual Task FindDefaultHomePageAsync() { return PageAppService.FindDefaultHomePageAsync(); } + + [HttpGet] + [Route("exist")] + public virtual Task DoesSlugExistAsync([NotNull][FromQuery] string slug) + { + return PageAppService.DoesSlugExistAsync(slug); + } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPagesConfigurationExtensions.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPagesConfigurationExtensions.cs new file mode 100644 index 0000000000..bf14634b2e --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPagesConfigurationExtensions.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Routing; +using Volo.Abp.GlobalFeatures; +using Volo.CmsKit.GlobalFeatures; +using Volo.CmsKit.Public.Web.Pages; + +namespace Volo.CmsKit.Public.Web; + +public static class CmsKitPagesConfigurationExtensions +{ + /// + /// Maps CMS Kit to the routing. + /// + /// + /// The matching order for the dynamic route. Lower is prior. + public static IEndpointRouteBuilder MapCmsPageRoute(this IEndpointRouteBuilder builder, int? order = null) + { + if (!GlobalFeatureManager.Instance.IsEnabled()) + { + return builder; + } + + builder + .MapDynamicPageRoute("/", state: null, order: int.MinValue); + + if (order is null) + { + builder + .MapDynamicPageRoute("{**slug}"); + } + else + { + builder + .MapDynamicPageRoute("{**slug}", state: default, order: order.Value); + } + + return builder; + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPublicWebModule.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPublicWebModule.cs index 36308b1839..74177352be 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPublicWebModule.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPublicWebModule.cs @@ -1,18 +1,17 @@ using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; -using Volo.Abp; using Volo.Abp.AspNetCore.Mvc.Localization; -using Volo.Abp.Ui.LayoutHooks; using Volo.Abp.AutoMapper; using Volo.Abp.Caching; using Volo.Abp.GlobalFeatures; using Volo.Abp.Http.ProxyScripting.Generators.JQuery; using Volo.Abp.Modularity; +using Volo.Abp.Ui.LayoutHooks; using Volo.Abp.UI.Navigation; using Volo.Abp.VirtualFileSystem; using Volo.CmsKit.GlobalFeatures; using Volo.CmsKit.Localization; -using Volo.CmsKit.Pages; using Volo.CmsKit.Public.Web.Menus; using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.GlobalResources.Script; using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.GlobalResources.Style; @@ -73,6 +72,14 @@ public class CmsKitPublicWebModule : AbpModule { options.KeyPrefix = "CmsKit:"; }); + + Configure(options => + { + options.EndpointConfigureActions.Add(context => + { + context.Endpoints.MapCmsPageRoute(); + }); + }); } public override void PostConfigureServices(ServiceConfigurationContext context) @@ -81,7 +88,6 @@ public class CmsKitPublicWebModule : AbpModule { Configure(options => { - options.Conventions.AddPageRoute("/Public/CmsKit/Pages/Index", PageConsts.UrlPrefix + "{slug:minlength(1)}"); options.Conventions.AddPageRoute("/Public/CmsKit/Blogs/Index", @"/blogs/{blogSlug:minlength(1)}"); options.Conventions.AddPageRoute("/Public/CmsKit/Blogs/BlogPost", @"/blogs/{blogSlug}/{blogPostSlug:minlength(1)}"); }); @@ -101,16 +107,5 @@ public class CmsKitPublicWebModule : AbpModule ); }); } - - } - - public override void OnApplicationInitialization(ApplicationInitializationContext context) - { - var app = context.GetApplicationBuilder(); - - if (GlobalFeatureManager.Instance.IsEnabled()) - { - app.UseHomePageDefaultMiddleware(); - } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/DefaultHomePageMiddleware.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/DefaultHomePageMiddleware.cs deleted file mode 100644 index 752e5f2f42..0000000000 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/DefaultHomePageMiddleware.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; -using Volo.Abp.DependencyInjection; -using Volo.Abp.Features; -using Volo.CmsKit.Features; -using Volo.CmsKit.Pages; -using Volo.CmsKit.Public.Pages; - -namespace Volo.CmsKit.Public.Web; - -public class DefaultHomePageMiddleware : IMiddleware, ITransientDependency -{ - public async Task InvokeAsync(HttpContext context, RequestDelegate next) - { - var featureChecker = context.RequestServices.GetRequiredService(); - - if (context.Request.Path.Value == "/") - { - if (await featureChecker.IsEnabledAsync(CmsKitFeatures.PageEnable)) - { - var pagePublicAppService = context.RequestServices.GetRequiredService(); - - var page = await pagePublicAppService.FindDefaultHomePageAsync(); - if (page != null) - { - context.Request.Path = $"{PageConsts.UrlPrefix}{page.Slug}"; - } - } - } - - await next(context); - } -} diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/HomePageDefaultMiddlewareExtensions.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/HomePageDefaultMiddlewareExtensions.cs deleted file mode 100644 index c5a2d8516f..0000000000 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/HomePageDefaultMiddlewareExtensions.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Microsoft.AspNetCore.Builder; - -namespace Volo.CmsKit.Public.Web; - -public static class HomePageDefaultMiddlewareExtensions -{ - public static IApplicationBuilder UseHomePageDefaultMiddleware(this IApplicationBuilder builder) - { - return builder.UseMiddleware(); - } -} diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKitHomePageRouteValueTransformer.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKitHomePageRouteValueTransformer.cs new file mode 100644 index 0000000000..3ac271938b --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKitHomePageRouteValueTransformer.cs @@ -0,0 +1,40 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Routing; +using Microsoft.AspNetCore.Routing; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Features; +using Volo.CmsKit.Features; +using Volo.CmsKit.Public.Pages; + +namespace Volo.CmsKit.Public.Web.Pages; + +public class CmsKitHomePageRouteValueTransformer : DynamicRouteValueTransformer, ITransientDependency +{ + protected IFeatureChecker FeatureChecker { get; } + + protected IPagePublicAppService PagePublicAppService { get; } + + public CmsKitHomePageRouteValueTransformer(IFeatureChecker featureChecker, IPagePublicAppService pagePublicAppService) + { + FeatureChecker = featureChecker; + PagePublicAppService = pagePublicAppService; + } + + public override async ValueTask TransformAsync(HttpContext httpContext, RouteValueDictionary values) + { + if (await FeatureChecker.IsEnabledAsync(CmsKitFeatures.PageEnable)) + { + var page = await PagePublicAppService.FindDefaultHomePageAsync(); + if (page is not null) + { + values = new RouteValueDictionary(); + + values["page"] = "/Public/CmsKit/Pages/Index"; + values["slug"] = page.Slug; + } + } + + return values; + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKitPageRouteValueTransformer.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKitPageRouteValueTransformer.cs new file mode 100644 index 0000000000..d9bba6cd0b --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKitPageRouteValueTransformer.cs @@ -0,0 +1,43 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Routing; +using Microsoft.AspNetCore.Routing; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Features; +using Volo.CmsKit.Features; +using Volo.CmsKit.Public.Pages; + +namespace Volo.CmsKit.Public.Web.Pages; + +public class CmsKitPageRouteValueTransformer : DynamicRouteValueTransformer, ITransientDependency +{ + protected IFeatureChecker FeatureChecker { get; } + protected IPagePublicAppService PagePublicAppService { get; } + + public CmsKitPageRouteValueTransformer(IFeatureChecker featureChecker, IPagePublicAppService pagePublicAppService) + { + FeatureChecker = featureChecker; + PagePublicAppService = pagePublicAppService; + } + + public override async ValueTask TransformAsync(HttpContext httpContext, RouteValueDictionary values) + { + if (values.TryGetValue("slug", out var slugParameter) && slugParameter is not null) + { + if (!await FeatureChecker.IsEnabledAsync(CmsKitFeatures.PageEnable)) + { + return values; + } + + var slug = slugParameter.ToString().TrimStart('/'); + var exist = await PagePublicAppService.DoesSlugExistAsync(slug); + + if (exist) + { + values["page"] = "/Public/CmsKit/Pages/Index"; + } + } + + return values; + } +} \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Pages/Index.cshtml b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Pages/Index.cshtml index 4237eb5c31..d55ffebad7 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Pages/Index.cshtml +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Pages/Index.cshtml @@ -1,4 +1,4 @@ -@page +@page "/pages/{**slug}" @addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap @using Volo.CmsKit.Contents @using Volo.CmsKit.Web.Pages.CmsKit.Components.Contents diff --git a/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Menus/MenuItemAdminAppService_Tests.cs b/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Menus/MenuItemAdminAppService_Tests.cs index cd210a3ba5..509070fd7d 100644 --- a/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Menus/MenuItemAdminAppService_Tests.cs +++ b/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Menus/MenuItemAdminAppService_Tests.cs @@ -32,6 +32,7 @@ public class MenuItemAdminAppService_Tests : CmsKitApplicationTestBase menu.ShouldNotBeNull(); } + [Fact] public async Task GetListAsync_ShouldWorkProperly() { var result = await MenuAdminAppService.GetListAsync(); @@ -69,7 +70,6 @@ public class MenuItemAdminAppService_Tests : CmsKitApplicationTestBase }); }); - var menu = await MenuRepository.FindAsync(TestData.MenuItem_1_Id); menu.ShouldNotBeNull(); @@ -82,7 +82,6 @@ public class MenuItemAdminAppService_Tests : CmsKitApplicationTestBase { await MenuAdminAppService.DeleteAsync(TestData.MenuItem_1_Id); - var menu = await MenuRepository.FindAsync(TestData.MenuItem_1_Id); menu.ShouldBeNull(); diff --git a/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Pages/PagePublicAppService_Tests.cs b/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Pages/PagePublicAppService_Tests.cs index 0eeb3210a9..8372de3cb5 100644 --- a/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Pages/PagePublicAppService_Tests.cs +++ b/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Pages/PagePublicAppService_Tests.cs @@ -33,4 +33,20 @@ public class PagePublicAppService_Tests : CmsKitApplicationTestBase page.ShouldBeNull(); } + + [Fact] + public async Task DoesSlugExistAsync_ShouldReturnTrue_WhenExists() + { + var result = await _pageAppService.DoesSlugExistAsync(_data.Page_1_Slug); + + result.ShouldBeTrue(); + } + + [Fact] + public async Task DoesSlugExistAsync_ShouldReturnFalse_WhenDoesNotExist() + { + var result = await _pageAppService.DoesSlugExistAsync("not-exist-url"); + + result.ShouldBeFalse(); + } } diff --git a/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Blogs/SlugExtensions_Tests.cs b/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Blogs/SlugExtensions_Tests.cs index 941f82c7bf..ea07fb335c 100644 --- a/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Blogs/SlugExtensions_Tests.cs +++ b/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Blogs/SlugExtensions_Tests.cs @@ -129,4 +129,32 @@ public class SlugExtensions_Tests // Assert actual.ShouldBe(expected); } + + [Fact] + public void NormalizeSlug_ShouldWorkProperly_WithMultipleLevel() + { + // Arrange + var name = "path/to/my/page"; + var expected = "path/to/my/page"; + + // Act + var actual = SlugNormalizer.Normalize(name); + + // Assert + actual.ShouldBe(expected); + } + + [Fact] + public void NormalizeSlug_ShouldWorkProperly_WithMultipleLevelStartingSlash() + { + // Arrange + var name = "/path/to/my/page"; + var expected = "path/to/my/page"; + + // Act + var actual = SlugNormalizer.Normalize(name); + + // Assert + actual.ShouldBe(expected); + } } diff --git a/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Menus/MenuManager_Test.cs b/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Menus/MenuManager_Test.cs index 514aa47797..bcc1612d41 100644 --- a/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Menus/MenuManager_Test.cs +++ b/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Menus/MenuManager_Test.cs @@ -32,7 +32,7 @@ public class MenuManager_Test : CmsKitDomainTestBase menuManager.SetPageUrl(menuItem, page); menuItem.Url.ShouldNotBeNullOrEmpty(); - menuItem.Url.ShouldBe(PageConsts.UrlPrefix + page.Slug); + menuItem.Url.ShouldBe(page.Slug.EnsureStartsWith('/')); } [Fact]