diff --git a/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/IDocumentAppService.cs b/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/IDocumentAppService.cs index 259d9edf0b..653d823ff6 100644 --- a/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/IDocumentAppService.cs +++ b/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/IDocumentAppService.cs @@ -19,5 +19,7 @@ namespace Volo.Docs.Documents Task> SearchAsync(DocumentSearchInput input); Task FullSearchEnabledAsync(); + + Task> GetUrlsAsync(string prefix); } } \ No newline at end of file diff --git a/modules/docs/src/Volo.Docs.Application/Volo/Docs/Documents/DocumentAppService.cs b/modules/docs/src/Volo.Docs.Application/Volo/Docs/Documents/DocumentAppService.cs index 9d5015d942..4d451a1d4f 100644 --- a/modules/docs/src/Volo.Docs.Application/Volo/Docs/Documents/DocumentAppService.cs +++ b/modules/docs/src/Volo.Docs.Application/Volo/Docs/Documents/DocumentAppService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text; using System.Threading.Tasks; using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Configuration; @@ -193,6 +194,93 @@ namespace Volo.Docs.Documents return await Task.FromResult(_docsElasticSearchOptions.Enable); } + public async Task> GetUrlsAsync(string prefix) + { + var documentUrls = new List(); + var projects = await _projectRepository.GetListAsync(); + + foreach (var project in projects) + { + var documents = await _documentRepository.GetListByProjectId(project.Id); + + foreach (var document in documents) + { + var version = GetProjectVersionPrefixIfExist(project) + document.Version; + var navigationDocument = await GetDocumentWithDetailsDtoAsync( + project, + project.NavigationDocumentName, + document.LanguageCode, + version + ); + + if (!DocsJsonSerializerHelper.TryDeserialize(navigationDocument.Content, + out var navigationNode)) + { + throw new UserFriendlyException( + $"Cannot validate navigation file '{project.NavigationDocumentName}' for the project {project.Name}."); + } + + navigationNode.Items?.ForEach(node => + { + documentUrls.AddIfNotContains( + GetDocumentLinks(node, documentUrls, prefix, project.ShortName, document) + ); + }); + } + } + + return documentUrls; + } + + private List GetDocumentLinks(NavigationNode node, List documentUrls, string prefix, + string shortName, Document document) + { + if (!IsExternalLink(node.Path)) + { + documentUrls.AddIfNotContains( + NormalizePath(prefix, node.Path, shortName, document) + ); + } + + node.Items?.ForEach(childNode => + { + GetDocumentLinks(childNode, documentUrls, prefix, shortName, document); + }); + + return documentUrls; + } + + private string NormalizePath(string prefix, string path, string shortName, Document document) + { + var pathWithoutFileExtension = RemoveFileExtensionFromPath(path, document.Format); + var normalizedPath = prefix + document.LanguageCode + "/" + shortName + "/" + document.Version + "/" + pathWithoutFileExtension; + + return normalizedPath; + } + + private string RemoveFileExtensionFromPath(string path, string format) + { + if (path == null) + { + return null; + } + + return path.EndsWith("." + format) + ? path.Left(path.Length - format.Length - 1) + : path; + } + + private static bool IsExternalLink(string path) + { + if (path.IsNullOrEmpty()) + { + return false; + } + + return path.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || + path.StartsWith("https://", StringComparison.OrdinalIgnoreCase); + } + public async Task GetParametersAsync(GetParametersDocumentInput input) { var project = await _projectRepository.GetAsync(input.ProjectId); @@ -353,7 +441,6 @@ namespace Volo.Docs.Documents } return project.ExtraProperties["VersionBranchPrefix"].ToString(); - } private GithubVersionProviderSource GetGithubVersionProviderSource(Project project) @@ -363,4 +450,4 @@ namespace Volo.Docs.Documents : GithubVersionProviderSource.Releases; } } -} +} \ No newline at end of file diff --git a/modules/docs/src/Volo.Docs.HttpApi/Volo/Docs/Documents/DocsDocumentController.cs b/modules/docs/src/Volo.Docs.HttpApi/Volo/Docs/Documents/DocsDocumentController.cs index d8d68ec0e8..354925f83e 100644 --- a/modules/docs/src/Volo.Docs.HttpApi/Volo/Docs/Documents/DocsDocumentController.cs +++ b/modules/docs/src/Volo.Docs.HttpApi/Volo/Docs/Documents/DocsDocumentController.cs @@ -61,6 +61,13 @@ namespace Volo.Docs.Documents return DocumentAppService.FullSearchEnabledAsync(); } + [HttpGet] + [Route("links")] + public Task> GetUrlsAsync(string prefix) + { + return DocumentAppService.GetUrlsAsync(prefix); + } + [HttpGet] [Route("parameters")] public Task GetParametersAsync(GetParametersDocumentInput input)