diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index 87b3d56e88..03640f506c 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -130,7 +130,7 @@ { "text": "Book Store Application (with ABP Suite)", "isLazyExpandable": true, - "path": "tutorials/book-store-with-abp-suite/index.md", + "path": "tutorials/book-store-with-abp-suite", "items": [ { "text": "Overview", @@ -162,11 +162,11 @@ { "text": "Modular Monolith Application", "isLazyExpandable": true, - "path": "tutorials/modular-crm/index.md", + "path": "tutorials/modular-crm", "items": [ { "text": "Overview", - "path": "tutorials/modular-crm/index.md", + "path": "tutorials/modular-crm", "isIndex": true }, { @@ -206,11 +206,11 @@ { "text": "Microservice Solution", "isLazyExpandable": true, - "path": "tutorials/microservice/index.md", + "path": "tutorials/microservice", "items": [ { "text": "Overview", - "path": "tutorials/microservice/index.md", + "path": "tutorials/microservice", "isIndex": true }, { @@ -246,19 +246,19 @@ { "text": "Mobile Application Development", "isLazyExpandable": true, - "path": "tutorials/mobile/index.md", + "path": "tutorials/mobile", "items": [ { "text": "Overview", - "path": "tutorials/mobile/index.md" + "path": "tutorials/mobile" }, { "text": "MAUI", - "path": "tutorials/mobile/maui/index.md" + "path": "tutorials/mobile/maui" }, { "text": "React Native", - "path": "tutorials/mobile/react-native/index.md" + "path": "tutorials/mobile/react-native" } ] }, @@ -2689,7 +2689,7 @@ }, { "text": "Knowledge Base", - "path": "kb/index.md" + "path": "kb" }, { "text": "Penetration Test Report", diff --git a/modules/docs/src/Volo.Docs.Web/DocsUiOptions.cs b/modules/docs/src/Volo.Docs.Web/DocsUiOptions.cs index 702536c31d..a6b06768c3 100644 --- a/modules/docs/src/Volo.Docs.Web/DocsUiOptions.cs +++ b/modules/docs/src/Volo.Docs.Web/DocsUiOptions.cs @@ -38,6 +38,31 @@ namespace Volo.Docs public SingleProjectModeOptions SingleProjectMode { get; } = new (); public bool EnableEnlargeImage { get; set; } = true; + + public Func DocumentLinksNormalizer { get; set; } = link => + { + if (!link.EndsWith("/Index", StringComparison.OrdinalIgnoreCase)) + { + return link; + + } + return link.Substring(0, link.LastIndexOf("/Index", StringComparison.OrdinalIgnoreCase)); + }; + + public Func RedirectUrlResolver { get; set; } = url => + { + if (!url.EndsWith("/Index", StringComparison.OrdinalIgnoreCase)) + { + return null; + + } + return url.Substring(0, url.LastIndexOf("/Index", StringComparison.OrdinalIgnoreCase)); + }; + + public string? GetRedirectUrlIfNeeded(string url) + { + return RedirectUrlResolver.Invoke(url); + } private string GetFormattedRoutePrefix() { diff --git a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml.cs b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml.cs index d6acd8c262..2bb7c82c21 100644 --- a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml.cs +++ b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml.cs @@ -131,6 +131,12 @@ namespace Volo.Docs.Pages.Documents.Project return Redirect(decodedUrl); } + var redirectUrl = _uiOptions.GetRedirectUrlIfNeeded(displayUrl); + if (redirectUrl != null) + { + return RedirectPermanent(redirectUrl); + } + return await SetPageAsync(); } diff --git a/modules/docs/src/Volo.Docs.Web/Utils/DefaultDocsLinkGenerator.cs b/modules/docs/src/Volo.Docs.Web/Utils/DefaultDocsLinkGenerator.cs index bc91e17848..c3934ec16f 100644 --- a/modules/docs/src/Volo.Docs.Web/Utils/DefaultDocsLinkGenerator.cs +++ b/modules/docs/src/Volo.Docs.Web/Utils/DefaultDocsLinkGenerator.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Net; using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; using Volo.Docs.Pages.Documents.Project; @@ -9,10 +10,13 @@ namespace Volo.Docs.Utils; public class DefaultDocsLinkGenerator : IDocsLinkGenerator, ITransientDependency { protected LinkGenerator LinkGenerator { get; } + + protected IOptions DocsUiOptions { get; } - public DefaultDocsLinkGenerator(LinkGenerator linkGenerator) + public DefaultDocsLinkGenerator(LinkGenerator linkGenerator, IOptions docsUiOptions) { LinkGenerator = linkGenerator; + DocsUiOptions = docsUiOptions; } @@ -26,6 +30,7 @@ public class DefaultDocsLinkGenerator : IDocsLinkGenerator, ITransientDependency }; var encodedUrl = LinkGenerator.GetPathByPage("/Documents/Project/Index", values: routeValues); - return encodedUrl?.Replace("%2F", "/"); //Document name can contain path separator(/), so we need to decode it. + var url = encodedUrl?.Replace("%2F", "/"); //Document name can contain path separator(/), so we need to decode it. + return DocsUiOptions.Value.DocumentLinksNormalizer?.Invoke(url); } } \ No newline at end of file