diff --git a/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/DocumentPartialTemplatesDto.cs b/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/DocumentPartialTemplatesDto.cs new file mode 100644 index 0000000000..bafb089bbd --- /dev/null +++ b/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/DocumentPartialTemplatesDto.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace Volo.Docs.Documents +{ + public class DocumentPartialTemplatesDto + { + public List Templates { get; set; } + } +} diff --git a/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/DocumentTemplateDto.cs b/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/DocumentTemplateDto.cs new file mode 100644 index 0000000000..37d94b9032 --- /dev/null +++ b/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/DocumentTemplateDto.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace Volo.Docs.Documents +{ + public class DocumentTemplateDto + { + public string Name { get; set; } + + public string Path { get; set; } + + public List SelfParameters { get; set; } + + public List ParentParameters { get; set; } + } +} \ No newline at end of file diff --git a/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/GetDocumentPartialTemplatesInput.cs b/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/GetDocumentPartialTemplatesInput.cs new file mode 100644 index 0000000000..52376c08ee --- /dev/null +++ b/modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/GetDocumentPartialTemplatesInput.cs @@ -0,0 +1,19 @@ +using System; +using System.ComponentModel.DataAnnotations; +using Volo.Docs.Language; +using Volo.Docs.Projects; + +namespace Volo.Docs.Documents +{ + public class GetDocumentPartialTemplatesInput + { + public Guid ProjectId { get; set; } + + [StringLength(ProjectConsts.MaxVersionNameLength)] + public string Version { get; set; } + + [Required] + [StringLength(LanguageConsts.MaxLanguageCodeLength)] + public string LanguageCode { get; set; } + } +} 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 cfc4840824..b73eea16be 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 @@ -13,6 +13,8 @@ namespace Volo.Docs.Documents Task GetParametersAsync(GetParametersDocumentInput input); + Task GetPartialTemplatesAsync(GetDocumentPartialTemplatesInput input); + Task GetResourceAsync(GetDocumentResourceInput input); } } \ 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 839c63860b..e120c5e37a 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 @@ -128,6 +128,33 @@ namespace Volo.Docs.Documents } } + public async Task GetPartialTemplatesAsync(GetDocumentPartialTemplatesInput input) + { + var project = await _projectRepository.GetAsync(input.ProjectId); + + try + { + if (string.IsNullOrWhiteSpace(project.PartialTemplatesDocumentName)) + { + return await Task.FromResult(null); + } + + var document = await GetDocumentWithDetailsDtoAsync( + project, + project.PartialTemplatesDocumentName, + input.LanguageCode, + input.Version + ); + + return JsonConvert.DeserializeObject(document.Content); + } + catch (DocumentNotFoundException) + { + Logger.LogWarning($"Partial template list file ({project.PartialTemplatesDocumentName}) not found."); + return new DocumentPartialTemplatesDto(); + } + } + protected virtual async Task GetDocumentWithDetailsDtoAsync( Project project, string documentName, diff --git a/modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/Projects/ProjectConsts.cs b/modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/Projects/ProjectConsts.cs index 927a1a4c59..0482ef5306 100644 --- a/modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/Projects/ProjectConsts.cs +++ b/modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/Projects/ProjectConsts.cs @@ -7,6 +7,7 @@ public const int MaxDefaultDocumentNameLength = 128; public const int MaxNavigationDocumentNameLength = 128; public const int MaxParametersDocumentNameLength = 128; + public const int MaxPartialTemplatesDocumentNameLength = 128; public const int MaxLatestVersionBranchNameLength = 128; public const int MaxVersionNameLength = 128; } diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubDocumentStore.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubDocumentStore.cs index bd399a4f83..ee9caa9621 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubDocumentStore.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubDocumentStore.cs @@ -37,6 +37,7 @@ namespace Volo.Docs.GitHub.Documents var commitHistoryUrl = project.GetGitHubUrlForCommitHistory() + documentName; var isNavigationDocument = documentName == project.NavigationDocumentName; var isParameterDocument = documentName == project.ParametersDocumentName; + var isPartialTemplatesDocumentName = documentName == project.PartialTemplatesDocumentName; var editLink = rootUrl.ReplaceFirst("/tree/", "/blob/") + languageCode + "/" + documentName; var localDirectory = ""; var fileName = documentName; @@ -57,7 +58,7 @@ namespace Volo.Docs.GitHub.Documents LocalDirectory = localDirectory, FileName = fileName, Contributors = new List(), - //Contributors = !isNavigationDocument && !isParameterDocument ? await GetContributors(commitHistoryUrl, token, userAgent): new List(), + //Contributors = !isNavigationDocument && !isParameterDocument && !isPartialTemplatesDocumentName ? await GetContributors(commitHistoryUrl, token, userAgent): new List(), Version = version, Content = await DownloadWebContentAsStringAsync(rawDocumentUrl, token, userAgent) }; diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Project.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Project.cs index f1cfd0d777..45b5801516 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Project.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Project.cs @@ -39,6 +39,11 @@ namespace Volo.Docs.Projects /// public virtual string ParametersDocumentName { get; protected set; } + /// + /// The document to be used for the partial document templates file (index). + /// + public virtual string PartialTemplatesDocumentName { get; protected set; } + public virtual string MinimumVersion { get; set; } /// @@ -63,7 +68,8 @@ namespace Volo.Docs.Projects [NotNull] string format, [NotNull] string defaultDocumentName = "Index", [NotNull] string navigationDocumentName = "docs-nav.json", - [NotNull] string parametersDocumentName = "docs-params.json") + [NotNull] string parametersDocumentName = "docs-params.json", + [NotNull] string partialTemplatesDocumentName = "docs-templates.json") { Id = id; @@ -74,6 +80,7 @@ namespace Volo.Docs.Projects DefaultDocumentName = Check.NotNullOrWhiteSpace(defaultDocumentName, nameof(defaultDocumentName)); NavigationDocumentName = Check.NotNullOrWhiteSpace(navigationDocumentName, nameof(navigationDocumentName)); ParametersDocumentName = Check.NotNullOrWhiteSpace(parametersDocumentName, nameof(parametersDocumentName)); + PartialTemplatesDocumentName = Check.NotNullOrWhiteSpace(partialTemplatesDocumentName, nameof(partialTemplatesDocumentName)); ExtraProperties = new Dictionary(); } @@ -98,6 +105,11 @@ namespace Volo.Docs.Projects ParametersDocumentName = Check.NotNullOrWhiteSpace(parametersDocumentName, nameof(parametersDocumentName)); } + public void SetPartialTemplatesDocumentName(string partialTemplatesDocumentName) + { + PartialTemplatesDocumentName = Check.NotNullOrWhiteSpace(partialTemplatesDocumentName, nameof(partialTemplatesDocumentName)); + } + public void SetDefaultDocumentName(string defaultDocumentName) { DefaultDocumentName = Check.NotNullOrWhiteSpace(defaultDocumentName, nameof(defaultDocumentName)); diff --git a/modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/EntityFrameworkCore/DocsDbContextModelBuilderExtensions.cs b/modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/EntityFrameworkCore/DocsDbContextModelBuilderExtensions.cs index f6a17b68d7..88fc0134eb 100644 --- a/modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/EntityFrameworkCore/DocsDbContextModelBuilderExtensions.cs +++ b/modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/EntityFrameworkCore/DocsDbContextModelBuilderExtensions.cs @@ -34,6 +34,7 @@ namespace Volo.Docs.EntityFrameworkCore b.Property(x => x.DefaultDocumentName).IsRequired().HasMaxLength(ProjectConsts.MaxDefaultDocumentNameLength); b.Property(x => x.NavigationDocumentName).IsRequired().HasMaxLength(ProjectConsts.MaxNavigationDocumentNameLength); b.Property(x => x.ParametersDocumentName).IsRequired().HasMaxLength(ProjectConsts.MaxParametersDocumentNameLength); + b.Property(x => x.PartialTemplatesDocumentName).IsRequired().HasMaxLength(ProjectConsts.MaxPartialTemplatesDocumentNameLength); b.Property(x => x.LatestVersionBranchName).HasMaxLength(ProjectConsts.MaxLatestVersionBranchNameLength); }); } 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 9ff91c8b17..ea2174186a 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 @@ -39,6 +39,11 @@ namespace Volo.Docs.Documents return DocumentAppService.GetNavigationAsync(input); } + public virtual Task GetPartialTemplatesAsync(GetDocumentPartialTemplatesInput input) + { + return DocumentAppService.GetPartialTemplatesAsync(input); + } + [HttpGet] [Route("resource")] public Task GetResourceAsync(GetDocumentResourceInput input) diff --git a/modules/docs/src/Volo.Docs.Web/HtmlConverting/IDocumentSectionRenderer.cs b/modules/docs/src/Volo.Docs.Web/HtmlConverting/IDocumentSectionRenderer.cs index 2d9511d05b..bb6d00015d 100644 --- a/modules/docs/src/Volo.Docs.Web/HtmlConverting/IDocumentSectionRenderer.cs +++ b/modules/docs/src/Volo.Docs.Web/HtmlConverting/IDocumentSectionRenderer.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; +using System.Collections.Generic; using System.Threading.Tasks; using Volo.Abp.DependencyInjection; @@ -8,8 +6,17 @@ namespace Volo.Docs.HtmlConverting { public interface IDocumentSectionRenderer: ITransientDependency { - Task RenderAsync(string doucment, DocumentRenderParameters parameters); + Task RenderAsync(string doucment, DocumentRenderParameters parameters = null, List partialTemplates = null); Task>> GetAvailableParametersAsync(string document); + + Task> GetPartialTemplatesInDocumentAsync(string documentContent); + } + + public class DocumentPartialTemplateContent + { + public string Name { get; set; } + + public string Content { get; set; } } } diff --git a/modules/docs/src/Volo.Docs.Web/HtmlConverting/PartialTemplateDto.cs b/modules/docs/src/Volo.Docs.Web/HtmlConverting/PartialTemplateDto.cs new file mode 100644 index 0000000000..3b0f3216dd --- /dev/null +++ b/modules/docs/src/Volo.Docs.Web/HtmlConverting/PartialTemplateDto.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace Volo.Docs.HtmlConverting +{ + public class PartialTemplateDto + { + public string Name { get; set; } + + public Dictionary Parameters { get; set; } + } +} \ No newline at end of file diff --git a/modules/docs/src/Volo.Docs.Web/HtmlConverting/ScribanDocumentSectionRenderer.cs b/modules/docs/src/Volo.Docs.Web/HtmlConverting/ScribanDocumentSectionRenderer.cs index df95bf985d..9041c359e8 100644 --- a/modules/docs/src/Volo.Docs.Web/HtmlConverting/ScribanDocumentSectionRenderer.cs +++ b/modules/docs/src/Volo.Docs.Web/HtmlConverting/ScribanDocumentSectionRenderer.cs @@ -1,11 +1,13 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text; using System.Threading.Tasks; using Newtonsoft.Json; using Scriban; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; +using Volo.Docs.Documents; namespace Volo.Docs.HtmlConverting { @@ -14,6 +16,7 @@ namespace Volo.Docs.HtmlConverting private const string jsonOpener = "````json"; private const string jsonCloser = "````"; private const string docs_param = "//[doc-params]"; + private const string docs_templates = "//[doc-template]"; public ILogger Logger { get; set; } @@ -22,8 +25,13 @@ namespace Volo.Docs.HtmlConverting Logger = NullLogger.Instance; } - public async Task RenderAsync(string document, DocumentRenderParameters parameters = null) + public async Task RenderAsync(string document, DocumentRenderParameters parameters = null, List partialTemplates = null) { + if (partialTemplates != null && partialTemplates.Any()) + { + document = SetPartialTemplates(document, partialTemplates); + } + var scribanTemplate = Template.Parse(document); if (parameters == null) @@ -99,7 +107,7 @@ namespace Volo.Docs.HtmlConverting if (jsonBeginningIndex < 0) { - return (-1,-1,""); + return (-1, -1, ""); } var jsonEndingIndex = document.Substring(jsonBeginningIndex).IndexOf(jsonCloser, StringComparison.Ordinal) + jsonBeginningIndex; @@ -116,5 +124,81 @@ namespace Volo.Docs.HtmlConverting return (-1, -1, ""); } + + public async Task> GetPartialTemplatesInDocumentAsync(string documentContent) + { + var templates = new List(); + + while (documentContent.Contains(jsonOpener)) + { + var afterJsonOpener = documentContent.Substring( + documentContent.IndexOf(jsonOpener, StringComparison.Ordinal) + jsonOpener.Length); + + var json = afterJsonOpener.Substring(0, + afterJsonOpener.IndexOf(jsonCloser, StringComparison.Ordinal)); + + documentContent = afterJsonOpener.Substring( + afterJsonOpener.IndexOf(jsonCloser, StringComparison.Ordinal) + jsonCloser.Length); + + if (!json.Contains(docs_templates)) + { + continue; + } + + json = json.Substring(json.IndexOf(docs_templates, StringComparison.Ordinal)); + + var template = JsonConvert.DeserializeObject(json); + + + templates.Add(template); + } + + return templates; + } + + private string SetPartialTemplates(string document, List templates) + { + var newDocument = new StringBuilder(); + + while (document.Contains(jsonOpener)) + { + var beforeJson = document.Substring(0, + document.IndexOf(jsonOpener, StringComparison.Ordinal) + jsonOpener.Length); + + var afterJsonOpener = document.Substring( + document.IndexOf(jsonOpener, StringComparison.Ordinal) + jsonOpener.Length); + + var json = afterJsonOpener.Substring(0, + afterJsonOpener.IndexOf(jsonCloser, StringComparison.Ordinal)); + + + if (!json.Contains(docs_templates)) + { + document = afterJsonOpener.Substring( + afterJsonOpener.IndexOf(jsonCloser, StringComparison.Ordinal) + jsonCloser.Length); + newDocument.Append(beforeJson + json + jsonCloser); + continue; + } + + json = json.Substring(json.IndexOf(docs_templates, StringComparison.Ordinal)); + + var templateName = JsonConvert.DeserializeObject(json)?.Name; + + var template = templates.FirstOrDefault(t => t.Name == templateName); + + var beforeTemplate = document.Substring(0, + document.IndexOf(jsonOpener, StringComparison.Ordinal)); + + var afterTemplate = document.Substring(0, + document.IndexOf(jsonCloser, StringComparison.Ordinal) + jsonCloser.Length); + + newDocument.Append(beforeTemplate + template?.Content + jsonCloser); + + document = afterJsonOpener.Substring( + afterJsonOpener.IndexOf(jsonCloser, StringComparison.Ordinal) + jsonCloser.Length); + } + + return newDocument.ToString(); + } } } 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 c653068baa..2572e3bfeb 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 @@ -394,10 +394,9 @@ namespace Volo.Docs.Pages.Documents.Project await SetDocumentPreferencesAsync(); SetUserPreferences(); - UserPreferences.Add("Document_Language_Code", LanguageCode); - UserPreferences.Add("Document_Version", Version); + var partialTemplates = await GetDocumentPartialTemplatesAsync(); - Document.Content = await _documentSectionRenderer.RenderAsync(Document.Content, UserPreferences); + Document.Content = await _documentSectionRenderer.RenderAsync(Document.Content, UserPreferences, partialTemplates); var converter = _documentToHtmlConverterFactory.Create(Document.Format ?? Project.Format); var content = converter.Convert(Project, Document, GetSpecificVersionOrLatest(), LanguageCode); @@ -418,9 +417,65 @@ namespace Volo.Docs.Pages.Documents.Project Document.Content = content; } + private async Task> GetDocumentPartialTemplatesAsync() + { + var contents = new List(); + + var projectPartialTemplates = await _documentAppService.GetPartialTemplatesAsync( + new GetDocumentPartialTemplatesInput() + { + ProjectId = Project.Id, + LanguageCode = LanguageCode, + Version = Version + }); + + if (!projectPartialTemplates?.Templates?.Any() ?? true) + { + return new List(); + } + + var partialTemplatesInDocument = await _documentSectionRenderer.GetPartialTemplatesInDocumentAsync(Document.Content); + + if (!partialTemplatesInDocument?.Any() ?? true) + { + return new List(); + } + + foreach (var partialTemplates in partialTemplatesInDocument) + { + foreach (var parameter in partialTemplates.Parameters) + { + if (!UserPreferences.ContainsKey(parameter.Key)) + { + UserPreferences.Add(parameter.Key, parameter.Value); + } + } + } + + foreach (var partialTemplate in projectPartialTemplates.Templates) + { + contents.Add(new DocumentPartialTemplateContent + { + Name = partialTemplate.Name, + Content = (await _documentAppService.GetAsync(new GetDocumentInput + { + LanguageCode = LanguageCode, + Name = partialTemplate.Path, + ProjectId = Project.Id, + Version = Version + })).Content + }); + } + + return contents; + } + private void SetUserPreferences() { - UserPreferences = new DocumentRenderParameters(); + UserPreferences = new DocumentRenderParameters + { + {"Document_Language_Code", LanguageCode}, {"Document_Version", Version} + }; var cookie = Request.Cookies["AbpDocsPreferences"]; @@ -473,6 +528,7 @@ namespace Volo.Docs.Pages.Documents.Project UserPreferences.Add(parameter.Name + "_Value", parameter.Values.FirstOrDefault().Value); } } + } public async Task SetDocumentPreferencesAsync() @@ -485,6 +541,7 @@ namespace Volo.Docs.Pages.Documents.Project Version = Version }); + if (projectParameters?.Parameters == null) { return;