Browse Source

Add HandleLeafDocumentAsync method and fix problems

pull/22430/head
liangshiwei 1 year ago
parent
commit
6a154d9846
  1. 64
      docs/en/docs-nav.json
  2. 1
      modules/docs/app/VoloDocs.Web/VoloDocsWebModule.cs
  3. 3
      modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/Documents/NavigationNode.cs
  4. 4
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/DocsProjectPdfGeneratorOptions.cs
  5. 3
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/IText/ITextHtmlToPdfRenderer.cs
  6. 51
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/Markdig/MarkdigPdfDocumentToHtmlConverter.cs
  7. 131
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/ProjectPdfGenerator.cs

64
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
}
]
},

1
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("<i class=\"fa fa-minus text-secondary\"></i>", "No").Replace("<i class=\"fa fa-check text-success\"></i>", "Yes");
});
Configure<AbpBlobStoringOptions>(options =>

3
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;

4
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/DocsProjectPdfGeneratorOptions.cs

@ -46,6 +46,10 @@ public class DocsProjectPdfGeneratorOptions
/// </summary>
public Func<Project, string> CalculatePdfFileTitle { get; set; }
public Func<string, string> HtmlContentNormalizer { get; set; }
public Func<string, string> DocumentContentNormalizer { get; set; }
public DocsProjectPdfGeneratorOptions()
{
HtmlLayout = $@"

3
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)

51
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/Markdig/MarkdigPdfDocumentToHtmlConverter.cs

@ -36,6 +36,7 @@ public class MarkdigPdfDocumentToHtmlConverter : IDocumentToHtmlConverter<PdfDoc
{
return new MarkdownPipelineBuilder()
.UseAdvancedExtensions()
.UseBootstrap()
.Use(new AnchorLinkResolverExtension(pdfDocument))
.Build();
}
@ -43,32 +44,25 @@ public class MarkdigPdfDocumentToHtmlConverter : IDocumentToHtmlConverter<PdfDoc
protected virtual string NormalizeContent(string content, PdfDocument pdfDocument, DocumentParams documentParams)
{
content = Regex.Replace(content, @"`{3,4}json\s*//\[doc-nav\][\s\S]*?`{3,4}", string.Empty, RegexOptions.IgnoreCase);
if (pdfDocument.RenderParameters.IsNullOrEmpty())
content = SetContentTitle(content, pdfDocument, documentParams);
if (Options.Value.DocumentContentNormalizer == null)
{
return content;
}
var titleLine = content.Split(Environment.NewLine).FirstOrDefault(x => 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<PdfDoc
}, RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Multiline);
}
private static string SetContentTitle(string content, PdfDocument pdfDocument, DocumentParams documentParams)
{
if (pdfDocument.RenderParameters.IsNullOrEmpty())
{
return content;
}
var titleLine = content.Split(Environment.NewLine).FirstOrDefault(x => 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);
}
}

131
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<string> 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<string, List<PdfDocument>>();
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<string, List<PdfDocument>> 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<string,List<PdfDocument>> 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<NavigationNode> GetNavigationAsync(

Loading…
Cancel
Save