From a164e1b786e9c1f50a3aa71b4ab91783fc044ede Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Tue, 3 Dec 2019 11:41:13 +0300 Subject: [PATCH] Docs module conditional section feature resolves https://github.com/abpframework/abp/issues/1626 --- .../HtmlConverting/DocumentSectionConsts.cs | 10 +++ .../DocumentSectionDictionary.cs | 8 ++ .../HtmlConverting/DocumentSectionFinder.cs | 67 ++++++++++++++++ .../DocumentSectionHtmlReplacer.cs | 22 +++++ .../HtmlConverting/IDocumentSectionFinder.cs | 9 +++ .../IDocumentSectionHtmlReplacer.cs | 9 +++ .../Pages/Documents/Project/Index.cshtml | 17 ++++ .../Pages/Documents/Project/Index.cshtml.cs | 14 +++- .../Pages/Documents/Project/index.js | 80 ++++++++++++++++++- 9 files changed, 234 insertions(+), 2 deletions(-) create mode 100644 modules/docs/src/Volo.Docs.Web/HtmlConverting/DocumentSectionConsts.cs create mode 100644 modules/docs/src/Volo.Docs.Web/HtmlConverting/DocumentSectionDictionary.cs create mode 100644 modules/docs/src/Volo.Docs.Web/HtmlConverting/DocumentSectionFinder.cs create mode 100644 modules/docs/src/Volo.Docs.Web/HtmlConverting/DocumentSectionHtmlReplacer.cs create mode 100644 modules/docs/src/Volo.Docs.Web/HtmlConverting/IDocumentSectionFinder.cs create mode 100644 modules/docs/src/Volo.Docs.Web/HtmlConverting/IDocumentSectionHtmlReplacer.cs diff --git a/modules/docs/src/Volo.Docs.Web/HtmlConverting/DocumentSectionConsts.cs b/modules/docs/src/Volo.Docs.Web/HtmlConverting/DocumentSectionConsts.cs new file mode 100644 index 0000000000..ca3e8697e4 --- /dev/null +++ b/modules/docs/src/Volo.Docs.Web/HtmlConverting/DocumentSectionConsts.cs @@ -0,0 +1,10 @@ +namespace Volo.Docs.HtmlConverting +{ + public class DocumentSectionConsts + { + public const string SectionOpenerPrefix = "[doc-section:"; + public const string SectionOpenerKeyValueSeparator = "="; + public const string SectionOpenerPostfix = "]"; + public const string SectionCloser = "[/doc-section]"; + } +} diff --git a/modules/docs/src/Volo.Docs.Web/HtmlConverting/DocumentSectionDictionary.cs b/modules/docs/src/Volo.Docs.Web/HtmlConverting/DocumentSectionDictionary.cs new file mode 100644 index 0000000000..1354a548a8 --- /dev/null +++ b/modules/docs/src/Volo.Docs.Web/HtmlConverting/DocumentSectionDictionary.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; + +namespace Volo.Docs.HtmlConverting +{ + public class DocumentSectionDictionary : Dictionary> + { + } +} diff --git a/modules/docs/src/Volo.Docs.Web/HtmlConverting/DocumentSectionFinder.cs b/modules/docs/src/Volo.Docs.Web/HtmlConverting/DocumentSectionFinder.cs new file mode 100644 index 0000000000..b8d8fd8c17 --- /dev/null +++ b/modules/docs/src/Volo.Docs.Web/HtmlConverting/DocumentSectionFinder.cs @@ -0,0 +1,67 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using System; +using System.Collections.Generic; + +namespace Volo.Docs.HtmlConverting +{ + public class DocumentSectionFinder : IDocumentSectionFinder + { + public ILogger Logger { get; set; } + + public DocumentSectionFinder() + { + Logger = NullLogger.Instance; + } + + public DocumentSectionDictionary Find(string document) + { + return FindNextSections(document, new DocumentSectionDictionary()); + try + { + return FindNextSections(document, new DocumentSectionDictionary()); + } + catch (Exception e) + { + Logger.LogWarning( + $"Incorrect usage of \"{DocumentSectionConsts.SectionOpenerPrefix}" + + $"KEY{DocumentSectionConsts.SectionOpenerKeyValueSeparator}" + + $"VALUE{DocumentSectionConsts.SectionOpenerPostfix}..{DocumentSectionConsts.SectionCloser}\" syntax."); + return new DocumentSectionDictionary(); + } + } + + private DocumentSectionDictionary FindNextSections(string targetPartOfDocument, DocumentSectionDictionary sections) + { + var startOfSection = targetPartOfDocument.IndexOf(DocumentSectionConsts.SectionOpenerPrefix, StringComparison.InvariantCulture); + + if (startOfSection < 0) + { + return sections; + } + + var endOfSectionOpener = targetPartOfDocument.Substring(startOfSection).IndexOf(DocumentSectionConsts.SectionOpenerPostfix, StringComparison.InvariantCulture); + + var keyValueWithSeparator = targetPartOfDocument.Substring(startOfSection) + [DocumentSectionConsts.SectionOpenerPrefix.Length..endOfSectionOpener]; + + var keyValue = keyValueWithSeparator.Split(DocumentSectionConsts.SectionOpenerKeyValueSeparator); + var key = keyValue[0]; + var value = keyValue[1]; + + if (sections.ContainsKey(key)) + { + sections[key].AddIfNotContains(value); + } + else + { + sections.Add(key, new List() { value }); + } + + var endOfSection = targetPartOfDocument.IndexOf(DocumentSectionConsts.SectionCloser, StringComparison.InvariantCulture) + + DocumentSectionConsts.SectionCloser.Length; + + return FindNextSections(targetPartOfDocument.Substring(endOfSection), sections); + } + } +} diff --git a/modules/docs/src/Volo.Docs.Web/HtmlConverting/DocumentSectionHtmlReplacer.cs b/modules/docs/src/Volo.Docs.Web/HtmlConverting/DocumentSectionHtmlReplacer.cs new file mode 100644 index 0000000000..55a7013d12 --- /dev/null +++ b/modules/docs/src/Volo.Docs.Web/HtmlConverting/DocumentSectionHtmlReplacer.cs @@ -0,0 +1,22 @@ +namespace Volo.Docs.HtmlConverting +{ + public class DocumentSectionHtmlReplacer : IDocumentSectionHtmlReplacer + { + public string Replace(string document, DocumentSectionDictionary sections) + { + foreach (var key in sections) + { + foreach (var value in key.Value) + { + document = document.Replace( + DocumentSectionConsts.SectionOpenerPrefix + key.Key + DocumentSectionConsts.SectionOpenerKeyValueSeparator + value + DocumentSectionConsts.SectionOpenerPostfix, + $"
"); + } + } + + document = document.Replace(DocumentSectionConsts.SectionCloser, "
"); + + return document; + } + } +} diff --git a/modules/docs/src/Volo.Docs.Web/HtmlConverting/IDocumentSectionFinder.cs b/modules/docs/src/Volo.Docs.Web/HtmlConverting/IDocumentSectionFinder.cs new file mode 100644 index 0000000000..14f7944869 --- /dev/null +++ b/modules/docs/src/Volo.Docs.Web/HtmlConverting/IDocumentSectionFinder.cs @@ -0,0 +1,9 @@ +using Volo.Abp.DependencyInjection; + +namespace Volo.Docs.HtmlConverting +{ + public interface IDocumentSectionFinder: ITransientDependency + { + public DocumentSectionDictionary Find(string document); + } +} diff --git a/modules/docs/src/Volo.Docs.Web/HtmlConverting/IDocumentSectionHtmlReplacer.cs b/modules/docs/src/Volo.Docs.Web/HtmlConverting/IDocumentSectionHtmlReplacer.cs new file mode 100644 index 0000000000..7f517bc341 --- /dev/null +++ b/modules/docs/src/Volo.Docs.Web/HtmlConverting/IDocumentSectionHtmlReplacer.cs @@ -0,0 +1,9 @@ +using Volo.Abp.DependencyInjection; + +namespace Volo.Docs.HtmlConverting +{ + public interface IDocumentSectionHtmlReplacer : ITransientDependency + { + public string Replace(string document, DocumentSectionDictionary sections); + } +} diff --git a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml index f74a47fb50..afa397556f 100644 --- a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml +++ b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml @@ -207,10 +207,27 @@ } } + + + @if (Model.DocumentSections != null && Model.DocumentSections.Count > 0) + { + foreach (var key in Model.DocumentSections) + { + @(key.Key + ":") + + } + } +
+ @if (Model.DocumentLanguageIsDifferent) { 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 d675251ac4..c06f24f7fa 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 @@ -56,22 +56,30 @@ namespace Volo.Docs.Pages.Documents.Project public bool DocumentLanguageIsDifferent { get; set; } + public DocumentSectionDictionary DocumentSections { get; set; } + private readonly IDocumentAppService _documentAppService; private readonly IDocumentToHtmlConverterFactory _documentToHtmlConverterFactory; private readonly IProjectAppService _projectAppService; + private readonly IDocumentSectionHtmlReplacer _documentSectionHtmlReplacer; + private readonly IDocumentSectionFinder _documentSectionFinder; private readonly DocsUiOptions _uiOptions; public IndexModel( IDocumentAppService documentAppService, IDocumentToHtmlConverterFactory documentToHtmlConverterFactory, IProjectAppService projectAppService, - IOptions options) + IOptions options, + IDocumentSectionHtmlReplacer documentSectionHtmlReplacer, + IDocumentSectionFinder documentSectionFinder) { ObjectMapperContext = typeof(DocsWebModule); _documentAppService = documentAppService; _documentToHtmlConverterFactory = documentToHtmlConverterFactory; _projectAppService = projectAppService; + _documentSectionHtmlReplacer = documentSectionHtmlReplacer; + _documentSectionFinder = documentSectionFinder; _uiOptions = options.Value; } @@ -374,6 +382,10 @@ namespace Volo.Docs.Pages.Documents.Project private void ConvertDocumentContentToHtml() { + DocumentSections = _documentSectionFinder.Find(Document.Content); + + Document.Content = _documentSectionHtmlReplacer.Replace(Document.Content, DocumentSections); + var converter = _documentToHtmlConverterFactory.Create(Document.Format ?? Project.Format); var content = converter.Convert(Project, Document, GetSpecificVersionOrLatest(), LanguageCode); diff --git a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/index.js b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/index.js index 8eb2083749..6b27b20a7f 100644 --- a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/index.js +++ b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/index.js @@ -2,7 +2,7 @@ $(function () { var initNavigationFilter = function (navigationContainerId) { - + var $navigation = $("#" + navigationContainerId); @@ -102,12 +102,90 @@ ); }; + + var initSections = function () { + var getQueryStringParameterByName = function (name) { + var url = window.location.href; + name = name.replace(/[\[\]]/g, '\\$&'); + var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'), + results = regex.exec(url); + if (!results) return null; + if (!results[2]) return ''; + return decodeURIComponent(results[2].replace(/\+/g, ' ')); + }; + + var setQueryString = function () { + var uri = window.location.href.toString(); + + if (uri.indexOf("?") > 0) { + uri = uri.substring(0, uri.indexOf("?")); + } + + var comboboxes = $(".doc-section-combobox"); + + var new_uri = uri + "?"; + + for (var i = 0; i < comboboxes.length; i++) { + var key = $(comboboxes[i]).data('key'); + var value = comboboxes[i].value; + + new_uri += key + "=" + value; + + if (i !== comboboxes.length - 1) { + new_uri += "&"; + } + } + + window.history.replaceState({}, document.title, new_uri); + }; + + var changeSections = function (combobox) { + var key = $(combobox).data('key'); + var value = combobox.value; + + $(".doc-section[data-key=" + key + "]").hide(); + $(".doc-section[data-key=" + key + "][data-value=" + value + "]").show(); + + localStorage["abp-doc-section-" + key] = value; + + setQueryString(); + }; + + $(".doc-section-combobox").change(function () { + changeSections(this); + }); + + var comboboxes = $(".doc-section-combobox"); + + for (var i = 0; i < comboboxes.length; i++) { + var key = $(comboboxes[i]).data("key"); + + var cacheValue = localStorage["abp-doc-section-" + key]; + + if (cacheValue) { + comboboxes[i].value = cacheValue; + } + var queryValue = getQueryStringParameterByName(key); + + if (queryValue) { + comboboxes[i].value = queryValue; + } + } + + for (var k = 0; k < comboboxes.length; k++) { + changeSections(comboboxes[k]); + } + }; + + initNavigationFilter("sidebar-scroll"); initAnchorTags(".docs-page .docs-body"); initSocialShareLinks(); + initSections(); + }); })(jQuery);