diff --git a/modules/docs/src/Volo.Docs.Web/HtmlConverting/DocumentNavigationDto.cs b/modules/docs/src/Volo.Docs.Web/HtmlConverting/DocumentNavigationDto.cs
new file mode 100644
index 0000000000..bcfbdb5a25
--- /dev/null
+++ b/modules/docs/src/Volo.Docs.Web/HtmlConverting/DocumentNavigationDto.cs
@@ -0,0 +1,7 @@
+namespace Volo.Docs.HtmlConverting;
+
+public class DocumentNavigationDto
+{
+ public string Name { get; set; }
+ public string Path { get; set; }
+}
\ No newline at end of file
diff --git a/modules/docs/src/Volo.Docs.Web/HtmlConverting/DocumentNavigationsDto.cs b/modules/docs/src/Volo.Docs.Web/HtmlConverting/DocumentNavigationsDto.cs
new file mode 100644
index 0000000000..ccee9b5d18
--- /dev/null
+++ b/modules/docs/src/Volo.Docs.Web/HtmlConverting/DocumentNavigationsDto.cs
@@ -0,0 +1,7 @@
+namespace Volo.Docs.HtmlConverting;
+
+public class DocumentNavigationsDto
+{
+ public DocumentNavigationDto Previous { get; set; }
+ public DocumentNavigationDto Next { get; set; }
+}
\ No newline at end of file
diff --git a/modules/docs/src/Volo.Docs.Web/HtmlConverting/IDocumentSectionRenderer.cs b/modules/docs/src/Volo.Docs.Web/HtmlConverting/IDocumentSectionRenderer.cs
index 0738b4fd5c..73560a7273 100644
--- a/modules/docs/src/Volo.Docs.Web/HtmlConverting/IDocumentSectionRenderer.cs
+++ b/modules/docs/src/Volo.Docs.Web/HtmlConverting/IDocumentSectionRenderer.cs
@@ -11,5 +11,7 @@ namespace Volo.Docs.HtmlConverting
Task>> GetAvailableParametersAsync(string document);
Task> GetPartialTemplatesInDocumentAsync(string documentContent);
+
+ Task GetDocumentNavigationsAsync(string documentContent);
}
}
diff --git a/modules/docs/src/Volo.Docs.Web/HtmlConverting/ScribanDocumentSectionRenderer.cs b/modules/docs/src/Volo.Docs.Web/HtmlConverting/ScribanDocumentSectionRenderer.cs
index 1ade06aa26..b28e42d611 100644
--- a/modules/docs/src/Volo.Docs.Web/HtmlConverting/ScribanDocumentSectionRenderer.cs
+++ b/modules/docs/src/Volo.Docs.Web/HtmlConverting/ScribanDocumentSectionRenderer.cs
@@ -17,6 +17,7 @@ namespace Volo.Docs.HtmlConverting
private const string JsonCloser = "````";
private const string DocsParam = "//[doc-params]";
private const string DocsTemplates = "//[doc-template]";
+ private const string DocsNav = "//[doc-nav]";
public ILogger Logger { get; set; }
@@ -41,7 +42,7 @@ namespace Volo.Docs.HtmlConverting
var result = await scribanTemplate.RenderAsync(parameters);
- return RemoveOptionsJson(result);
+ return RemoveOptionsJson(result, DocsParam, DocsNav);
}
public async Task>> GetAvailableParametersAsync(string document)
@@ -53,7 +54,7 @@ namespace Volo.Docs.HtmlConverting
return new Dictionary>();
}
- var (jsonBeginningIndex, jsonEndingIndex, insideJsonSection) = GetJsonBeginEndIndexesAndPureJson(document);
+ var (jsonBeginningIndex, jsonEndingIndex, insideJsonSection) = GetJsonBeginEndIndexesAndPureJson(document, DocsParam);
if (jsonBeginningIndex < 0 || jsonEndingIndex <= 0 || string.IsNullOrWhiteSpace(insideJsonSection))
{
@@ -76,36 +77,42 @@ namespace Volo.Docs.HtmlConverting
}
}
- private static string RemoveOptionsJson(string document)
+ private static string RemoveOptionsJson(string document, params string[] sectionNames)
{
- var orgDocument = document;
- try
+ foreach (var sectionName in sectionNames)
{
- if (!document.Contains(JsonOpener) || !document.Contains(DocsParam))
+ var orgDocument = document;
+
+ try
{
- return orgDocument;
- }
+ if (!document.Contains(JsonOpener) || !document.Contains(sectionName))
+ {
+ continue;
+ }
- var (jsonBeginningIndex, jsonEndingIndex, insideJsonSection) = GetJsonBeginEndIndexesAndPureJson(document);
+ var (jsonBeginningIndex, jsonEndingIndex, insideJsonSection) = GetJsonBeginEndIndexesAndPureJson(document, sectionName);
- if (jsonBeginningIndex < 0 || jsonEndingIndex <= 0 || string.IsNullOrWhiteSpace(insideJsonSection))
+ if (jsonBeginningIndex < 0 || jsonEndingIndex <= 0 || string.IsNullOrWhiteSpace(insideJsonSection))
+ {
+ continue;
+ }
+
+ document = document.Remove(
+ jsonBeginningIndex - JsonOpener.Length,
+ (jsonEndingIndex + JsonCloser.Length) - (jsonBeginningIndex - JsonOpener.Length)
+ );
+ }
+ catch (Exception)
{
- return orgDocument;
+ document = orgDocument;
}
-
- return document.Remove(
- jsonBeginningIndex - JsonOpener.Length,
- (jsonEndingIndex + JsonCloser.Length) - (jsonBeginningIndex - JsonOpener.Length)
- );
- }
- catch (Exception)
- {
- return orgDocument;
}
+
+ return document;
}
- private static (int, int, string) GetJsonBeginEndIndexesAndPureJson(string document)
+ private static (int, int, string) GetJsonBeginEndIndexesAndPureJson(string document, string sectionName)
{
var searchedIndex = 0;
@@ -121,7 +128,7 @@ namespace Volo.Docs.HtmlConverting
var jsonEndingIndex = document.Substring(jsonBeginningIndex).IndexOf(JsonCloser, StringComparison.Ordinal) + jsonBeginningIndex;
var insideJsonSection = document[jsonBeginningIndex..jsonEndingIndex];
- if (insideJsonSection.IndexOf(DocsParam, StringComparison.Ordinal) < 0)
+ if (insideJsonSection.IndexOf(sectionName, StringComparison.Ordinal) < 0)
{
searchedIndex = jsonEndingIndex + JsonCloser.Length;
continue;
@@ -169,6 +176,30 @@ namespace Volo.Docs.HtmlConverting
return await Task.FromResult(templates);
}
+ public Task GetDocumentNavigationsAsync(string documentContent)
+ {
+ if (!documentContent.Contains(JsonOpener) || !documentContent.Contains(DocsNav))
+ {
+ return Task.FromResult(new DocumentNavigationsDto());
+ }
+
+ var (jsonBeginningIndex, jsonEndingIndex, insideJsonSection) = GetJsonBeginEndIndexesAndPureJson(documentContent, DocsNav);
+
+ if (jsonBeginningIndex < 0 || jsonEndingIndex <= 0 || string.IsNullOrWhiteSpace(insideJsonSection))
+ {
+ return Task.FromResult(new DocumentNavigationsDto());
+ }
+
+ var pureJson = insideJsonSection.Replace(DocsNav, "").Trim();
+
+ if (!DocsJsonSerializerHelper.TryDeserialize(pureJson, out var navigationDocumentNames))
+ {
+ throw new UserFriendlyException("ERROR-20200327: Cannot validate JSON content for `NavigationDocumentNames`!");
+ }
+
+ return Task.FromResult(navigationDocumentNames);
+ }
+
private static string SetPartialTemplates(string document, IReadOnlyCollection templates)
{
var newDocument = new StringBuilder();
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 0ffa883d8d..05bc45b1b0 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
@@ -82,6 +82,8 @@ namespace Volo.Docs.Pages.Documents.Project
public bool FullSearchEnabled { get; set; }
public bool IsLatestVersion { get; private set; }
+
+ public DocumentNavigationsDto DocumentNavigationsDto { get; private set; }
private const int MaxDescriptionMetaTagLength = 200;
private readonly IDocumentAppService _documentAppService;
@@ -239,6 +241,20 @@ namespace Volo.Docs.Pages.Documents.Project
DocumentsUrlPrefix + LanguageCode + "/" + ProjectName + "/" +
DocsAppConsts.Latest + "/" + DocumentName;
}
+
+ public string CreateDocumentLink(string documentName)
+ {
+ return new StringBuilder()
+ .Append(DocumentsUrlPrefix)
+ .Append(LanguageCode)
+ .Append('/')
+ .Append(ProjectName)
+ .Append('/')
+ .Append(LatestVersionInfo.IsSelected ? DocsAppConsts.Latest : Version)
+ .Append('/')
+ .Append(documentName)
+ .ToString();
+ }
private IActionResult RedirectToDefaultLanguage()
{
@@ -508,6 +524,8 @@ namespace Volo.Docs.Pages.Documents.Project
SetUserPreferences();
var partialTemplates = await GetDocumentPartialTemplatesAsync();
+
+ DocumentNavigationsDto = await _documentSectionRenderer.GetDocumentNavigationsAsync(Document.Content);
Document.Content = await _documentSectionRenderer.RenderAsync(Document.Content, UserPreferences, partialTemplates);
}