diff --git a/modules/docs/app/VoloDocs.Web/VoloDocsWebModule.cs b/modules/docs/app/VoloDocs.Web/VoloDocsWebModule.cs index 4307725f63..e49cdc7dde 100644 --- a/modules/docs/app/VoloDocs.Web/VoloDocsWebModule.cs +++ b/modules/docs/app/VoloDocs.Web/VoloDocsWebModule.cs @@ -75,13 +75,13 @@ namespace VoloDocs.Web var hostingEnvironment = context.Services.GetHostingEnvironment(); var configuration = context.Services.GetConfiguration(); - Configure(options => - { - options.RoutePrefix = null; - options.SingleProjectMode.Enable = true; - options.SingleProjectMode.ProjectName = "abp"; - options.MultiLanguageMode = false; - }); + // Configure(options => + // { + // options.RoutePrefix = null; + // options.SingleProjectMode.Enable = true; + // options.SingleProjectMode.ProjectName = "abp"; + // options.MultiLanguageMode = false; + // }); Configure(options => { 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 0fe6c18699..ddf28ac00a 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 @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -305,7 +306,7 @@ namespace Volo.Docs.Documents return normalizedPathStringBuilder.ToString(); } - private string RemoveFileExtensionFromPath(string path, string format) + private static string RemoveFileExtensionFromPath(string path, string format) { if (path == null) { @@ -376,19 +377,29 @@ namespace Volo.Docs.Documents { return await GetDocumentAsync(documentName, project, languageCode, version); } - - var document = await _documentRepository.FindAsync(project.Id, documentName, languageCode, version); + + var document = await _documentRepository.FindAsync(project.Id, GetPossibleNames(documentName, project.Format), languageCode, version); if (document == null) { return await GetDocumentAsync(documentName, project, languageCode, version); } - if (document.LastCachedTime + _cacheTimeout < DateTime.Now) + if (document.LastCachedTime + _cacheTimeout >= DateTime.Now) { - return await GetDocumentAsync(documentName, project, languageCode, version, document); + return CreateDocumentWithDetailsDto(project, document); } - return CreateDocumentWithDetailsDto(project, document); + try + { + return await GetDocumentAsync(documentName, project, languageCode, version, document); + } + catch + { + Logger.LogWarning( + "Could not retrieve the document ({documentName}, {languageCode}, {version}) from the source. Using the cached version.", + documentName, languageCode, version); + return CreateDocumentWithDetailsDto(project, document); + } } protected virtual DocumentWithDetailsDto CreateDocumentWithDetailsDto(Project project, Document document) @@ -406,10 +417,8 @@ namespace Volo.Docs.Documents string languageCode, string version, Document oldDocument = null) { Logger.LogInformation($"Not found in the cache. Requesting {documentName} from the source..."); - - var source = _documentStoreFactory.Create(project.DocumentStoreType); - var sourceDocument = await source.GetDocumentAsync(project, documentName, languageCode, version, - oldDocument?.LastSignificantUpdateTime); + + var sourceDocument = await GetSourceDocument(project, documentName, languageCode, version, oldDocument); await _documentRepository.DeleteAsync(project.Id, sourceDocument.Name, sourceDocument.LanguageCode, sourceDocument.Version, autoSave: true); await _documentRepository.InsertAsync(sourceDocument, true); @@ -434,6 +443,55 @@ namespace Volo.Docs.Documents return CreateDocumentWithDetailsDto(project, sourceDocument); } + private static List GetPossibleNames(string originalDocumentName, string format) + { + var extension = Path.GetExtension(originalDocumentName); + if (extension != null && !extension.Equals("." + format, StringComparison.OrdinalIgnoreCase)) + { + return new List {originalDocumentName}; + } + + var possibleNames = new List {originalDocumentName}; + if (!originalDocumentName.EndsWith("/index." + format, StringComparison.OrdinalIgnoreCase)) + { + var documentNameWithoutExtension = RemoveFileExtensionFromPath(originalDocumentName, format); + possibleNames.Add(documentNameWithoutExtension + "/index." + format); + possibleNames.Add(documentNameWithoutExtension + "/Index." + format); + } + + return possibleNames; + } + + private async Task GetSourceDocument(Project project, string documentName, + string languageCode, string version, Document oldDocument) + { + var source = _documentStoreFactory.Create(project.DocumentStoreType); + + Document sourceDocument = null; + + Exception firstException = null; + foreach (var name in GetPossibleNames(documentName, project.Format)) + { + try + { + sourceDocument = await source.GetDocumentAsync(project, name, languageCode, version, + oldDocument?.LastSignificantUpdateTime); + break; + } + catch (Exception ex) + { + firstException ??= ex; + } + } + + if(sourceDocument == null) + { + throw firstException!; + } + + return sourceDocument; + } + private TimeSpan GetCacheTimeout() { var value = _configuration["Volo.Docs:DocumentCacheTimeoutInterval"]; 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 2b459505e8..6ecf7eb204 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 @@ -35,7 +35,17 @@ namespace Volo.Docs.Documents return false; } - if (string.Equals(documentName, Path, StringComparison.OrdinalIgnoreCase)) + var path = Path ?? string.Empty; + var pathHasExtension = System.IO.Path.HasExtension(path); + + if (!pathHasExtension) + { + var extension = System.IO.Path.GetExtension(documentName); + path = path.EnsureEndsWith('/') + "index" + extension; + } + + + if (string.Equals(documentName, path, StringComparison.OrdinalIgnoreCase)) { return true; } diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/IDocumentRepository.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/IDocumentRepository.cs index 86fb2cdc7f..e95a47c3f3 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/IDocumentRepository.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/IDocumentRepository.cs @@ -26,6 +26,13 @@ namespace Volo.Docs.Documents string version, bool includeDetails = true, CancellationToken cancellationToken = default); + + Task FindAsync(Guid projectId, + List possibleNames, + string languageCode, + string version, + bool includeDetails = true, + CancellationToken cancellationToken = default); Task DeleteAsync(Guid projectId, string name, diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubDocumentSource.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubDocumentSource.cs index 21ae3a460c..3698d5583e 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubDocumentSource.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubDocumentSource.cs @@ -410,18 +410,6 @@ namespace Volo.Docs.GitHub.Documents return await _githubRepositoryManager.GetFileRawStringContentAsync(rawDocumentUrl, token, userAgent); } - catch (HttpRequestException ex) - { - Logger.LogWarning($"Could not retrieve the document ({rawDocumentUrl}) from Github. Trying to get it from database..."); - - var document = await _documentRepository.FindAsync(project.Id, documentName, languageCode, version); - if (document == null) - { - throw new DocumentNotFoundException(rawDocumentUrl); - } - - return document.Content; - } catch (Exception ex) { //TODO: Only handle when document is really not available diff --git a/modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/Documents/EFCoreDocumentRepository.cs b/modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/Documents/EFCoreDocumentRepository.cs index a585545bdf..08fc63c37a 100644 --- a/modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/Documents/EFCoreDocumentRepository.cs +++ b/modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/Documents/EFCoreDocumentRepository.cs @@ -182,6 +182,16 @@ namespace Volo.Docs.Documents GetCancellationToken(cancellationToken)); } + public async Task FindAsync(Guid projectId, List possibleNames, string languageCode, string version, + bool includeDetails = true, CancellationToken cancellationToken = default) + { + return await (await GetDbSetAsync()).IncludeDetails(includeDetails) + .FirstOrDefaultAsync(x => + x.ProjectId == projectId && possibleNames.Contains(x.Name) && + x.LanguageCode == languageCode && x.Version == version, + GetCancellationToken(cancellationToken)); + } + public virtual async Task DeleteAsync(Guid projectId, string name, string languageCode, string version, bool autoSave = false, CancellationToken cancellationToken = default) { await DeleteAsync(x => diff --git a/modules/docs/src/Volo.Docs.MongoDB/Volo/Docs/Documents/MongoDocumentRepository.cs b/modules/docs/src/Volo.Docs.MongoDB/Volo/Docs/Documents/MongoDocumentRepository.cs index c678947f5c..7f55ef63b0 100644 --- a/modules/docs/src/Volo.Docs.MongoDB/Volo/Docs/Documents/MongoDocumentRepository.cs +++ b/modules/docs/src/Volo.Docs.MongoDB/Volo/Docs/Documents/MongoDocumentRepository.cs @@ -94,6 +94,16 @@ namespace Volo.Docs.Documents x.LanguageCode == languageCode && x.Version == version, GetCancellationToken(cancellationToken)); } + + public virtual async Task FindAsync(Guid projectId, List possibleNames, string languageCode, string version, + bool includeDetails = true, + CancellationToken cancellationToken = default) + { + return await (await GetMongoQueryableAsync(cancellationToken)).FirstOrDefaultAsync(x => x.ProjectId == projectId && + possibleNames.Contains(x.Name) && + x.LanguageCode == languageCode && + x.Version == version, GetCancellationToken(cancellationToken)); + } public virtual async Task DeleteAsync(Guid projectId, string name, string languageCode, string version, bool autoSave = false, CancellationToken cancellationToken = default) { diff --git a/modules/docs/src/Volo.Docs.Web/Markdown/MarkdownDocumentToHtmlConverter.cs b/modules/docs/src/Volo.Docs.Web/Markdown/MarkdownDocumentToHtmlConverter.cs index 04049ea058..61ba5cdfd9 100644 --- a/modules/docs/src/Volo.Docs.Web/Markdown/MarkdownDocumentToHtmlConverter.cs +++ b/modules/docs/src/Volo.Docs.Web/Markdown/MarkdownDocumentToHtmlConverter.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Net; using System.Text.RegularExpressions; using Microsoft.AspNetCore.Routing; @@ -96,8 +97,12 @@ namespace Volo.Docs.Markdown } var documentName = RemoveFileExtension(linkPart); + + var isFolder = !Path.HasExtension(linkPart); + + var isMdFile = linkPart.EndsWith(".md"); - if (UrlHelper.IsExternalLink(link) || !linkPart.EndsWith(".md")) + if (UrlHelper.IsExternalLink(link) || !(isMdFile || isFolder)) { return NormalizeLink(displayText, MdLinkFormat, link, projectShortName, version, 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 3d5dc7a3a4..93673d277c 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 @@ -482,33 +482,21 @@ namespace Volo.Docs.Pages.Documents.Project private async Task TrySetDocumentAsync() { - var documentPath = DocumentName ?? ""; - - if (Request.GetDisplayUrl().EndsWith("/index", StringComparison.OrdinalIgnoreCase)) - { - documentPath = documentPath.Substring(0, documentPath.LastIndexOf('/') + 1); - } - - var documentNames = new[] { DocumentName, documentPath.EnsureEndsWith('/') + "Index", documentPath.EnsureEndsWith('/') + "index" }.Distinct().ToArray(); var languages = new[] { LanguageCode, DefaultLanguageCode }.Where(x => !x.IsNullOrWhiteSpace()).Distinct().ToArray(); - var sb = new StringBuilder(); - foreach (var documentName in documentNames) + DocumentNameWithExtension = new StringBuilder().Append(DocumentName).Append('.').Append(Project.Format).ToString(); + foreach (var language in languages) { - DocumentName = documentName; - DocumentNameWithExtension = sb.Clear().Append(DocumentName).Append('.').Append(Project.Format).ToString(); - foreach (var language in languages) + try { - try - { - Document = await GetSpecificDocumentOrDefaultAsync(language); - DocumentLanguageCode = language; - await ConvertDocumentContentToHtmlAsync(); - return true; - } - catch (DocumentNotFoundException e) - { - Logger.LogWarning(e.Message); - } + Document = await GetSpecificDocumentOrDefaultAsync(language); + DocumentLanguageCode = language; + DocumentNameWithExtension = Document.Name; + await ConvertDocumentContentToHtmlAsync(); + return true; + } + catch (DocumentNotFoundException e) + { + Logger.LogWarning(e.Message); } }