From 6a154d9846b1411176eec790b16f7408243bdf82 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Mon, 12 May 2025 12:00:48 +0800 Subject: [PATCH] Add HandleLeafDocumentAsync method and fix problems --- docs/en/docs-nav.json | 64 ++++++--- .../app/VoloDocs.Web/VoloDocsWebModule.cs | 1 + .../Volo/Docs/Documents/NavigationNode.cs | 3 + .../Pdf/DocsProjectPdfGeneratorOptions.cs | 4 + .../Pdf/IText/ITextHtmlToPdfRenderer.cs | 3 +- .../MarkdigPdfDocumentToHtmlConverter.cs | 51 ++++--- .../Docs/Projects/Pdf/ProjectPdfGenerator.cs | 131 ++++++++++++------ 7 files changed, 174 insertions(+), 83 deletions(-) diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index fc3844317d..d031e87466 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -83,47 +83,59 @@ { "text": "Overview", "path": "tutorials/book-store", - "isIndex": true + "isIndex": true, + "isInSeries": true }, { "text": "1: Creating the Server Side", - "path": "tutorials/book-store/part-01.md" + "path": "tutorials/book-store/part-01.md", + "isInSeries": true + }, { "text": "2: The Book List Page", - "path": "tutorials/book-store/part-02.md" + "path": "tutorials/book-store/part-02.md", + "isInSeries": true }, { "text": "3: Creating, Updating and Deleting Books", - "path": "tutorials/book-store/part-03.md" + "path": "tutorials/book-store/part-03.md", + "isInSeries": true }, { "text": "4: Integration Tests", - "path": "tutorials/book-store/part-04.md" + "path": "tutorials/book-store/part-04.md", + "isInSeries": true }, { "text": "5: Authorization", - "path": "tutorials/book-store/part-05.md" + "path": "tutorials/book-store/part-05.md", + "isInSeries": true }, { "text": "6: Authors: Domain Layer", - "path": "tutorials/book-store/part-06.md" + "path": "tutorials/book-store/part-06.md", + "isInSeries": true }, { "text": "7: Authors: Database Integration", - "path": "tutorials/book-store/part-07.md" + "path": "tutorials/book-store/part-07.md", + "isInSeries": true }, { "text": "8: Authors: Application Layer", - "path": "tutorials/book-store/part-08.md" + "path": "tutorials/book-store/part-08.md", + "isInSeries": true }, { "text": "9: Authors: User Interface", - "path": "tutorials/book-store/part-09.md" + "path": "tutorials/book-store/part-09.md", + "isInSeries": true }, { "text": "10: Book to Author Relation", - "path": "tutorials/book-store/part-10.md" + "path": "tutorials/book-store/part-10.md", + "isInSeries": true } ] }, @@ -135,27 +147,33 @@ { "text": "Overview", "path": "tutorials/book-store-with-abp-suite", - "isIndex": true + "isIndex": true, + "isInSeries": true }, { "text": "1: Creating the Solution", - "path": "tutorials/book-store-with-abp-suite/part-01.md" + "path": "tutorials/book-store-with-abp-suite/part-01.md", + "isInSeries": true }, { "text": "2: Creating the Books", - "path": "tutorials/book-store-with-abp-suite/part-02.md" + "path": "tutorials/book-store-with-abp-suite/part-02.md", + "isInSeries": true }, { "text": "3: Creating the Authors", - "path": "tutorials/book-store-with-abp-suite/part-03.md" + "path": "tutorials/book-store-with-abp-suite/part-03.md", + "isInSeries": true }, { "text": "4: Book to Author Relation", - "path": "tutorials/book-store-with-abp-suite/part-04.md" + "path": "tutorials/book-store-with-abp-suite/part-04.md", + "isInSeries": true }, { "text": "5: Customizing the Generated Code", - "path": "tutorials/book-store-with-abp-suite/part-05.md" + "path": "tutorials/book-store-with-abp-suite/part-05.md", + "isInSeries": true } ] }, @@ -2070,19 +2088,23 @@ "items": [ { "text": "Deploy to Azure Web App Service", - "path": "solution-templates/layered-web-application/deployment/azure-deployment/azure-deployment.md" + "path": "solution-templates/layered-web-application/deployment/azure-deployment/azure-deployment.md", + "isInSeries": true }, { "text": "Creating an Azure Web App Service Environment", - "path": "solution-templates/layered-web-application/deployment/azure-deployment/step1-create-azure-resources.md" + "path": "solution-templates/layered-web-application/deployment/azure-deployment/step1-create-azure-resources.md", + "isInSeries": true }, { "text": "Customizing the Configuration of Your ABP Application", - "path": "solution-templates/layered-web-application/deployment/azure-deployment/step2-configuration-application.md" + "path": "solution-templates/layered-web-application/deployment/azure-deployment/step2-configuration-application.md", + "isInSeries": true }, { "text": "Deploying Application With GitHub Actions", - "path": "solution-templates/layered-web-application/deployment/azure-deployment/step3-deployment-github-action.md" + "path": "solution-templates/layered-web-application/deployment/azure-deployment/step3-deployment-github-action.md", + "isInSeries": true } ] }, diff --git a/modules/docs/app/VoloDocs.Web/VoloDocsWebModule.cs b/modules/docs/app/VoloDocs.Web/VoloDocsWebModule.cs index 03875dd6dd..297a15c88d 100644 --- a/modules/docs/app/VoloDocs.Web/VoloDocsWebModule.cs +++ b/modules/docs/app/VoloDocs.Web/VoloDocsWebModule.cs @@ -180,6 +180,7 @@ namespace VoloDocs.Web options.BaseUrl = configuration["App:selfUrl"]; options.IndexPagePath = "Index.md"; options.CalculatePdfFileTitle = project => project.ShortName == "abp" ? "ABP Documentation" : null; + options.DocumentContentNormalizer = content => content.Replace("", "No").Replace("", "Yes"); }); Configure(options => diff --git a/modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/Documents/NavigationNode.cs b/modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/Documents/NavigationNode.cs index 9b270297a7..43ebfa9a76 100644 --- a/modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/Documents/NavigationNode.cs +++ b/modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/Documents/NavigationNode.cs @@ -27,6 +27,9 @@ namespace Volo.Docs.Documents [JsonPropertyName("ignoreOnDownload")] public bool IgnoreOnDownload { get; set; } + + [JsonPropertyName("isInSeries")] + public bool IsInSeries { get; set; } public bool IsLeaf => !HasChildItems; diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/DocsProjectPdfGeneratorOptions.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/DocsProjectPdfGeneratorOptions.cs index 8ab7bbbbea..ef24e6ffd7 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/DocsProjectPdfGeneratorOptions.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/DocsProjectPdfGeneratorOptions.cs @@ -46,6 +46,10 @@ public class DocsProjectPdfGeneratorOptions /// public Func CalculatePdfFileTitle { get; set; } + public Func HtmlContentNormalizer { get; set; } + + public Func DocumentContentNormalizer { get; set; } + public DocsProjectPdfGeneratorOptions() { HtmlLayout = $@" diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/IText/ITextHtmlToPdfRenderer.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/IText/ITextHtmlToPdfRenderer.cs index e28113d762..1ee6b679e3 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/IText/ITextHtmlToPdfRenderer.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/IText/ITextHtmlToPdfRenderer.cs @@ -7,6 +7,7 @@ using iText.Kernel.Pdf; using iText.Kernel.Pdf.Action; using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; +using Volo.Docs.Utils; using ITextDocument = iText.Layout.Document; namespace Volo.Docs.Projects.Pdf.IText; @@ -56,7 +57,7 @@ public class ITextHtmlToPdfRenderer : IHtmlToPdfRenderer ,ITransientDependency var outline = parentOutline.AddOutline(pdfDocumentNode.Title); if (!pdfDocumentNode.Id.IsNullOrWhiteSpace()) { - outline.AddAction(PdfAction.CreateGoTo(pdfDocumentNode.Id)); + outline.AddAction(UrlHelper.IsExternalLink(pdfDocumentNode.Id) ? PdfAction.CreateURI(pdfDocumentNode.Id) : PdfAction.CreateGoTo(pdfDocumentNode.Id)); } if (pdfDocumentNode.HasChildren) diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/Markdig/MarkdigPdfDocumentToHtmlConverter.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/Markdig/MarkdigPdfDocumentToHtmlConverter.cs index 0d50244ee9..22c94b5930 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/Markdig/MarkdigPdfDocumentToHtmlConverter.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/Markdig/MarkdigPdfDocumentToHtmlConverter.cs @@ -36,6 +36,7 @@ public class MarkdigPdfDocumentToHtmlConverter : IDocumentToHtmlConverter x.TrimStart().StartsWith("#")); - if (titleLine == null) - { - return content; - } - - var paramValues = pdfDocument.RenderParameters.Select(x => - { - var documentParam = documentParams.Parameters.FirstOrDefault(p => p.Name == x.Key); - return $"{documentParam?.Values[x.Value] ?? x.Value}"; - }); - - var newTitle = $"{titleLine.Trim()} ({string.Join(", ", paramValues)})"; - return content.Replace(titleLine, newTitle); + return Options.Value.DocumentContentNormalizer(content); } protected virtual string NormalizeHtmlContent(string htmlContent, PdfDocument pdfDocument) { htmlContent = WrapHtmlWithPageDiv(htmlContent, pdfDocument); - return ReplaceRelativeImageUrls(htmlContent, pdfDocument); + htmlContent = ReplaceRelativeImageUrls(htmlContent, pdfDocument); + if (Options.Value.HtmlContentNormalizer == null) + { + return htmlContent; + } + + return Options.Value.HtmlContentNormalizer(htmlContent); } private string WrapHtmlWithPageDiv(string htmlContent, PdfDocument pdfDocument) @@ -96,4 +90,27 @@ public class MarkdigPdfDocumentToHtmlConverter : IDocumentToHtmlConverter x.TrimStart().StartsWith("#")); + if (titleLine == null) + { + return content; + } + + var paramValues = pdfDocument.RenderParameters.Select(x => + { + var documentParam = documentParams.Parameters.FirstOrDefault(p => p.Name == x.Key); + return $"{documentParam?.Values[x.Value] ?? x.Value}"; + }); + + var newTitle = $"{titleLine.Trim()} ({string.Join(", ", paramValues)})"; + return content.Replace(titleLine, newTitle); + } } \ No newline at end of file diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/ProjectPdfGenerator.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/ProjectPdfGenerator.cs index 943b5d976b..de8be78ae1 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/ProjectPdfGenerator.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/ProjectPdfGenerator.cs @@ -13,6 +13,7 @@ using Volo.Abp.DependencyInjection; using Volo.Docs.Documents; using Volo.Docs.Documents.Rendering; using Volo.Docs.HtmlConverting; +using Volo.Docs.Utils; using Volo.Extensions; namespace Volo.Docs.Projects.Pdf; @@ -119,7 +120,18 @@ public class ProjectPdfGenerator : IProjectPdfGenerator, ITransientDependency protected virtual async Task RenderDocumentAsync(PdfDocument pdfDocument) { - return await DocumentSectionRenderer.RenderAsync(pdfDocument.Document.Content, pdfDocument.RenderParameters); + var parameters = new DocumentRenderParameters(); + if (pdfDocument.RenderParameters != null) + { + foreach (var renderParameter in pdfDocument.RenderParameters) + { + var documentParam = DocumentParams.Parameters.FirstOrDefault(p => p.Name == renderParameter.Key); + parameters.Add(renderParameter.Key, renderParameter.Value); + parameters.Add(renderParameter.Key + "_Value",documentParam?.Values[renderParameter.Value] ?? renderParameter.Value); + } + } + + return await DocumentSectionRenderer.RenderAsync(pdfDocument.Document.Content, parameters); } protected virtual async Task SetAllPdfDocumentsAsync( @@ -130,73 +142,55 @@ public class ProjectPdfGenerator : IProjectPdfGenerator, ITransientDependency PdfDocument parentPdfDocument = null) { var groupedCombinationsDocuments = new Dictionary>(); + foreach (var navigation in navigations) { if (navigation.IgnoreOnDownload) { continue; } + try { var pdfDocument = new PdfDocument - { + { Title = navigation.Text, - IgnoreOnOutline = navigation.Path == Options.Value.IndexPagePath + IgnoreOnOutline = navigation.Path == Options.Value.IndexPagePath, + Id = UrlHelper.IsExternalLink(navigation.Path) ? navigation.Path: null }; - - if (!navigation.Path.IsNullOrWhiteSpace() && !navigation.HasChildItems) + + if (!navigation.Path.IsNullOrWhiteSpace() && !UrlHelper.IsExternalLink(navigation.Path) && !navigation.HasChildItems) { - var document = await GetDocumentAsync(project, navigation.Path, version, languageCode); - var parameters = await DocumentSectionRenderer.GetAvailableParametersAsync(document.Content); - var parameterCombinations = GenerateAllParameterCombinations(parameters.Keys.ToList(), parameters); - var firstParameterCombination = parameterCombinations.FirstOrDefault(); - - pdfDocument.Document = document; - pdfDocument.RenderParameters = firstParameterCombination; - pdfDocument.Id = GetDocumentId(document.Name, document.Format ?? project.Format, firstParameterCombination, true); - pdfDocument.Title = GetDocumentTitle(navigation.Text, firstParameterCombination, DocumentParams); - - if(parameters.Count <= 1) + await HandleLeafDocumentAsync( + navigation, + project, + version, + languageCode, + parentPdfDocument, + groupedCombinationsDocuments, + pdfDocument + ); + } + + if (!navigation.IsInSeries) + { + if (parentPdfDocument == null) { - AddParameterCombinationsDocuments(parentPdfDocument, groupedCombinationsDocuments); + AllPdfDocuments.AddIfNotContains(pdfDocument); } else { - for (var i = 1; i < parameterCombinations.Count; i++) - { - var parameterCombination = parameterCombinations[i]; - var key = parameterCombination.Select(x => $"{x.Key}_{x.Value}").JoinAsString("-"); - if (!groupedCombinationsDocuments.ContainsKey(key)) - { - groupedCombinationsDocuments[key] = []; - } - - groupedCombinationsDocuments[key].Add(new PdfDocument - { - Document = document, - Id = GetDocumentId(document.Name, document.Format ?? project.Format, parameterCombination, false), - Title = GetDocumentTitle(navigation.Text, parameterCombination, DocumentParams), - RenderParameters = parameterCombination - }); - } + parentPdfDocument.Children.AddIfNotContains(pdfDocument); } } - if (parentPdfDocument == null) - { - AllPdfDocuments.AddIfNotContains(pdfDocument); - } - else - { - parentPdfDocument.Children.AddIfNotContains(pdfDocument); - } - if (navigation.HasChildItems) { + AddParameterCombinationsDocuments(parentPdfDocument, groupedCombinationsDocuments); await SetAllPdfDocumentsAsync(navigation.Items, project, version, languageCode, pdfDocument); } - if (navigation == navigations.Last()) + if (!navigation.IsInSeries || navigation == navigations.Last()) { AddParameterCombinationsDocuments(parentPdfDocument, groupedCombinationsDocuments); } @@ -208,6 +202,53 @@ public class ProjectPdfGenerator : IProjectPdfGenerator, ITransientDependency } } + private async Task HandleLeafDocumentAsync( + NavigationNode navigation, + Project project, + string version, + string languageCode, + PdfDocument parentPdfDocument, + Dictionary> groupedCombinationsDocuments, + PdfDocument leafDocument) + { + var document = await GetDocumentAsync(project, navigation.Path, version, languageCode); + var parameters = await DocumentSectionRenderer.GetAvailableParametersAsync(document.Content); + var parameterCombinations = GenerateAllParameterCombinations(parameters.Keys.ToList(), parameters); + var firstCombination = parameterCombinations.FirstOrDefault(); + + leafDocument.Document = document; + leafDocument.RenderParameters = firstCombination; + leafDocument.Id = GetDocumentId(document.Name, document.Format ?? project.Format, firstCombination, true); + leafDocument.Title = GetDocumentTitle(navigation.Text, firstCombination, DocumentParams); + + if (parameterCombinations.Count <= 1) + { + AddParameterCombinationsDocuments(parentPdfDocument, groupedCombinationsDocuments); + return; + } + + for (var i = 0; i < parameterCombinations.Count; i++) + { + var combination = parameterCombinations[i]; + var key = string.Join("-", combination.Select(x => $"{x.Key}_{x.Value}")); + + if (!groupedCombinationsDocuments.ContainsKey(key)) + { + groupedCombinationsDocuments[key] = []; + } + + var combinationDocument = i == 0 ? leafDocument : new PdfDocument + { + Document = document, + Id = GetDocumentId(document.Name, document.Format ?? project.Format, combination, false), + Title = GetDocumentTitle(navigation.Text, combination, DocumentParams), + RenderParameters = combination + }; + + groupedCombinationsDocuments[key].Add(combinationDocument); + } + } + private void AddParameterCombinationsDocuments(PdfDocument parentPdfDocument, Dictionary> groupedCombinationsDocuments) { foreach (var combinations in groupedCombinationsDocuments) @@ -221,6 +262,8 @@ public class ProjectPdfGenerator : IProjectPdfGenerator, ITransientDependency parentPdfDocument.Children.AddIfNotContains(combinations.Value); } } + + groupedCombinationsDocuments.Clear(); } private async Task GetNavigationAsync(