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(