diff --git a/Directory.Packages.props b/Directory.Packages.props
index 906b37bd37..a8ec5e695c 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -55,7 +55,7 @@
-
+
diff --git a/modules/docs/Volo.Docs.sln b/modules/docs/Volo.Docs.sln
index 6bb8d84d0a..77c5ae1761 100644
--- a/modules/docs/Volo.Docs.sln
+++ b/modules/docs/Volo.Docs.sln
@@ -63,6 +63,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Docs.MongoDB.Tests", "
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Docs.Installer", "src\Volo.Docs.Installer\Volo.Docs.Installer.csproj", "{50B9AC1D-C03E-47AA-9ED8-E7986BCFABA1}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "common", "common", "{AA7E58C3-E223-455F-81BD-4D2E2E152624}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Docs.Common.Application.Contracts", "src\Volo.Docs.Common.Application.Contracts\Volo.Docs.Common.Application.Contracts.csproj", "{D75944C2-FAB0-41A8-AB2A-1A2F97849302}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Docs.Common.Application", "src\Volo.Docs.Common.Application\Volo.Docs.Common.Application.csproj", "{A96F4615-8405-493D-AA06-727D6F29B5F5}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -165,6 +171,14 @@ Global
{50B9AC1D-C03E-47AA-9ED8-E7986BCFABA1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{50B9AC1D-C03E-47AA-9ED8-E7986BCFABA1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{50B9AC1D-C03E-47AA-9ED8-E7986BCFABA1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D75944C2-FAB0-41A8-AB2A-1A2F97849302}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D75944C2-FAB0-41A8-AB2A-1A2F97849302}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D75944C2-FAB0-41A8-AB2A-1A2F97849302}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D75944C2-FAB0-41A8-AB2A-1A2F97849302}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A96F4615-8405-493D-AA06-727D6F29B5F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A96F4615-8405-493D-AA06-727D6F29B5F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A96F4615-8405-493D-AA06-727D6F29B5F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A96F4615-8405-493D-AA06-727D6F29B5F5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -197,6 +211,9 @@ Global
{DBE846CD-1BED-4F2C-ABF2-94F6240BCB9B} = {A982A58E-1E92-4764-9F56-39E7AABB8556}
{C5E2A2A3-D54D-4C2E-97BA-EA50A49ED7AD} = {59D430A9-AC61-4457-8338-5DA0705ABB5D}
{50B9AC1D-C03E-47AA-9ED8-E7986BCFABA1} = {A982A58E-1E92-4764-9F56-39E7AABB8556}
+ {AA7E58C3-E223-455F-81BD-4D2E2E152624} = {42416152-5BAB-4706-93A6-57A19E71FE14}
+ {D75944C2-FAB0-41A8-AB2A-1A2F97849302} = {AA7E58C3-E223-455F-81BD-4D2E2E152624}
+ {A96F4615-8405-493D-AA06-727D6F29B5F5} = {AA7E58C3-E223-455F-81BD-4D2E2E152624}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {13691265-2547-4FFF-B757-E8FACB05679D}
diff --git a/modules/docs/src/Volo.Docs.Common.Application.Contracts/Volo.Docs.Common.Application.Contracts.csproj b/modules/docs/src/Volo.Docs.Common.Application.Contracts/Volo.Docs.Common.Application.Contracts.csproj
new file mode 100644
index 0000000000..acde6b2a6c
--- /dev/null
+++ b/modules/docs/src/Volo.Docs.Common.Application.Contracts/Volo.Docs.Common.Application.Contracts.csproj
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+ netstandard2.0;netstandard2.1;net8.0;net9.0
+ Volo.Docs.Common.Application.Contracts
+ Volo.Docs.Common.Application.Contracts
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/docs/src/Volo.Docs.Common.Application.Contracts/Volo/Docs/Common/DocsCommonApplicationContractsModule.cs b/modules/docs/src/Volo.Docs.Common.Application.Contracts/Volo/Docs/Common/DocsCommonApplicationContractsModule.cs
new file mode 100644
index 0000000000..bc2741fd41
--- /dev/null
+++ b/modules/docs/src/Volo.Docs.Common.Application.Contracts/Volo/Docs/Common/DocsCommonApplicationContractsModule.cs
@@ -0,0 +1,31 @@
+using Volo.Abp.Application;
+using Volo.Abp.Authorization;
+using Volo.Abp.Localization;
+using Volo.Abp.Modularity;
+using Volo.Abp.VirtualFileSystem;
+using Volo.Docs.Localization;
+
+namespace Volo.Docs.Common;
+
+[DependsOn(
+ typeof(DocsDomainSharedModule),
+ typeof(AbpDddApplicationContractsModule),
+ typeof(AbpAuthorizationAbstractionsModule)
+)]
+public class DocsCommonApplicationContractsModule : AbpModule
+{
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ Configure(options =>
+ {
+ options.FileSets.AddEmbedded();
+ });
+
+ Configure(options =>
+ {
+ options.Resources
+ .Get()
+ .AddVirtualJson("Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts");
+ });
+ }
+}
\ No newline at end of file
diff --git a/modules/docs/src/Volo.Docs.Common.Application.Contracts/Volo/Docs/Common/DocsCommonPermissionDefinitionProvider.cs b/modules/docs/src/Volo.Docs.Common.Application.Contracts/Volo/Docs/Common/DocsCommonPermissionDefinitionProvider.cs
new file mode 100644
index 0000000000..5f59a60548
--- /dev/null
+++ b/modules/docs/src/Volo.Docs.Common.Application.Contracts/Volo/Docs/Common/DocsCommonPermissionDefinitionProvider.cs
@@ -0,0 +1,21 @@
+using Volo.Abp.Authorization.Permissions;
+using Volo.Abp.Localization;
+using Volo.Docs.Localization;
+
+namespace Volo.Docs.Common
+{
+ public class DocsCommonPermissionDefinitionProvider : PermissionDefinitionProvider
+ {
+ public override void Define(IPermissionDefinitionContext context)
+ {
+ var group = context.AddGroup(DocsCommonPermissions.GroupName, L("Permission:DocumentManagement.Common"));
+
+ group.AddPermission(DocsCommonPermissions.Documents.PdfCreation, L("Permission:PdfCreation"));
+ }
+
+ private static LocalizableString L(string name)
+ {
+ return LocalizableString.Create(name);
+ }
+ }
+}
diff --git a/modules/docs/src/Volo.Docs.Common.Application.Contracts/Volo/Docs/Common/DocsCommonPermissions.cs b/modules/docs/src/Volo.Docs.Common.Application.Contracts/Volo/Docs/Common/DocsCommonPermissions.cs
new file mode 100644
index 0000000000..c0aae9275a
--- /dev/null
+++ b/modules/docs/src/Volo.Docs.Common.Application.Contracts/Volo/Docs/Common/DocsCommonPermissions.cs
@@ -0,0 +1,19 @@
+using Volo.Abp.Reflection;
+
+namespace Volo.Docs.Common
+{
+ public class DocsCommonPermissions
+ {
+ public const string GroupName = "Docs.Common";
+
+ public static class Documents
+ {
+ public const string PdfCreation = GroupName + ".PdfCreation";
+ }
+
+ public static string[] GetAll()
+ {
+ return ReflectionHelper.GetPublicConstantsRecursively(typeof(DocsCommonPermissions));
+ }
+ }
+}
diff --git a/modules/docs/src/Volo.Docs.Common.Application.Contracts/Volo/Docs/Common/DocsCommonRemoteServiceConsts.cs b/modules/docs/src/Volo.Docs.Common.Application.Contracts/Volo/Docs/Common/DocsCommonRemoteServiceConsts.cs
new file mode 100644
index 0000000000..6faa6d18fd
--- /dev/null
+++ b/modules/docs/src/Volo.Docs.Common.Application.Contracts/Volo/Docs/Common/DocsCommonRemoteServiceConsts.cs
@@ -0,0 +1,9 @@
+namespace Volo.Docs.Common
+{
+ public static class DocsCommonRemoteServiceConsts
+ {
+ public const string RemoteServiceName = "AbpDocsCommon";
+
+ public const string ModuleName = "docs-common";
+ }
+}
diff --git a/modules/docs/src/Volo.Docs.Common.Application.Contracts/Volo/Docs/Common/Documents/DocumentPdfGeneratorInput.cs b/modules/docs/src/Volo.Docs.Common.Application.Contracts/Volo/Docs/Common/Documents/DocumentPdfGeneratorInput.cs
new file mode 100644
index 0000000000..989a0e9c40
--- /dev/null
+++ b/modules/docs/src/Volo.Docs.Common.Application.Contracts/Volo/Docs/Common/Documents/DocumentPdfGeneratorInput.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace Volo.Docs.Common.Documents;
+
+public class DocumentPdfGeneratorInput
+{
+ public Guid ProjectId { get; set; }
+
+ public string Version { get; set; }
+
+ public string LanguageCode { get; set; }
+}
\ No newline at end of file
diff --git a/modules/docs/src/Volo.Docs.Common.Application.Contracts/Volo/Docs/Common/Documents/IDocumentPdfGeneratorAppService.cs b/modules/docs/src/Volo.Docs.Common.Application.Contracts/Volo/Docs/Common/Documents/IDocumentPdfGeneratorAppService.cs
new file mode 100644
index 0000000000..dc8d87e820
--- /dev/null
+++ b/modules/docs/src/Volo.Docs.Common.Application.Contracts/Volo/Docs/Common/Documents/IDocumentPdfGeneratorAppService.cs
@@ -0,0 +1,10 @@
+using System.Threading.Tasks;
+using Volo.Abp.Application.Services;
+using Volo.Abp.Content;
+
+namespace Volo.Docs.Common.Documents;
+
+public interface IDocumentPdfGeneratorAppService : IApplicationService
+{
+ Task GeneratePdfAsync(DocumentPdfGeneratorInput input);
+}
\ No newline at end of file
diff --git a/modules/docs/src/Volo.Docs.Common.Application/Volo.Docs.Common.Application.csproj b/modules/docs/src/Volo.Docs.Common.Application/Volo.Docs.Common.Application.csproj
new file mode 100644
index 0000000000..d87c22ab09
--- /dev/null
+++ b/modules/docs/src/Volo.Docs.Common.Application/Volo.Docs.Common.Application.csproj
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+ net9.0
+ Volo.Docs.Common.Application
+ Volo.Docs.Common.Application
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/docs/src/Volo.Docs.Common.Application/Volo/Docs/Common/DocsCommonApplicationModule.cs b/modules/docs/src/Volo.Docs.Common.Application/Volo/Docs/Common/DocsCommonApplicationModule.cs
new file mode 100644
index 0000000000..16f93808d2
--- /dev/null
+++ b/modules/docs/src/Volo.Docs.Common.Application/Volo/Docs/Common/DocsCommonApplicationModule.cs
@@ -0,0 +1,25 @@
+using Volo.Abp.Application;
+using Volo.Abp.Authorization;
+using Volo.Abp.AutoMapper;
+using Volo.Abp.Caching;
+using Volo.Abp.Localization;
+using Volo.Abp.Modularity;
+using Volo.Abp.VirtualFileSystem;
+using Volo.Docs.Localization;
+
+namespace Volo.Docs.Common;
+
+[DependsOn(
+ typeof(DocsDomainModule),
+ typeof(DocsCommonApplicationContractsModule),
+ typeof(AbpCachingModule),
+ typeof(AbpAutoMapperModule),
+ typeof(AbpDddApplicationModule)
+)]
+public class DocsCommonApplicationModule : AbpModule
+{
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/modules/docs/src/Volo.Docs.Common.Application/Volo/Docs/Common/DocsDocumentPdfContainer.cs b/modules/docs/src/Volo.Docs.Common.Application/Volo/Docs/Common/DocsDocumentPdfContainer.cs
new file mode 100644
index 0000000000..e8b7f3c3fb
--- /dev/null
+++ b/modules/docs/src/Volo.Docs.Common.Application/Volo/Docs/Common/DocsDocumentPdfContainer.cs
@@ -0,0 +1,9 @@
+using Volo.Abp.BlobStoring;
+
+namespace Volo.Docs.Common;
+
+[BlobContainerName("docs-document-pdf")]
+public class DocsDocumentPdfContainer
+{
+
+}
\ No newline at end of file
diff --git a/modules/docs/src/Volo.Docs.Common.Application/Volo/Docs/Common/Documents/DocumentPdfGeneratorAppService.cs b/modules/docs/src/Volo.Docs.Common.Application/Volo/Docs/Common/Documents/DocumentPdfGeneratorAppService.cs
new file mode 100644
index 0000000000..860f647a4f
--- /dev/null
+++ b/modules/docs/src/Volo.Docs.Common.Application/Volo/Docs/Common/Documents/DocumentPdfGeneratorAppService.cs
@@ -0,0 +1,88 @@
+using System.Threading.Tasks;
+using Volo.Abp;
+using Volo.Abp.Application.Services;
+using Volo.Abp.BlobStoring;
+using Volo.Abp.Content;
+using Volo.Docs.Documents;
+using Volo.Docs.Projects;
+using Volo.Extensions;
+
+namespace Volo.Docs.Common.Documents;
+
+public class DocumentPdfGeneratorAppService : ApplicationService, IDocumentPdfGeneratorAppService
+{
+ protected IBlobContainer BlobContainer { get; }
+ protected IProjectRepository ProjectRepository { get; }
+ protected IDocumentSourceFactory DocumentStoreFactory { get; }
+ protected IDocumentRepository DocumentRepository { get; }
+
+ public DocumentPdfGeneratorAppService(
+ IBlobContainer blobContainer,
+ IProjectRepository projectRepository,
+ IDocumentSourceFactory documentStoreFactory,
+ IDocumentRepository documentRepository)
+ {
+ BlobContainer = blobContainer;
+ ProjectRepository = projectRepository;
+ DocumentStoreFactory = documentStoreFactory;
+ DocumentRepository = documentRepository;
+ }
+
+ public virtual async Task GeneratePdfAsync(DocumentPdfGeneratorInput input)
+ {
+ var project = await ProjectRepository.GetAsync(input.ProjectId);
+ var fileName = CalculateDocumentPdfFileName(project.Name, input.Version, input.LanguageCode);
+ var stream = await BlobContainer.GetOrNullAsync(fileName);
+
+ if(stream != null)
+ {
+ return new RemoteStreamContent(stream, fileName , "application/pdf");
+ }
+
+ var documentStore = DocumentStoreFactory.Create(project.DocumentStoreType);
+ var navigation = await GetNavigationAsync(documentStore, project, input.LanguageCode, input.Version);
+
+
+ }
+
+ protected virtual string CalculateDocumentPdfFileName(string projectName, string version, string languageCode)
+ {
+ return $"{projectName.ToLower()}-{version.ToLower()}--{languageCode.ToLower()}";
+ }
+
+ private async Task GetNavigationAsync(
+ IDocumentSource documentStore,
+ Project project,
+ string languageCode,
+ string version)
+ {
+ var navigationDocument = await GetDocumentAsync(documentStore, project, project.NavigationDocumentName, languageCode, version);
+
+ if (!DocsJsonSerializerHelper.TryDeserialize(navigationDocument.Content, out var navigation))
+ {
+ throw new UserFriendlyException($"Cannot validate navigation file '{project.NavigationDocumentName}' for the project {project.Name}.");
+ }
+
+ return navigation;
+ }
+
+ private async Task GetDocumentAsync(
+ IDocumentSource documentStore,
+ Project project,
+ string documentName,
+ string languageCode,
+ string version)
+ {
+ version = string.IsNullOrWhiteSpace(version) ? project.LatestVersionBranchName : version;
+ var document = await DocumentRepository.FindAsync(project.Id, documentName, version, languageCode);
+
+ if (document != null)
+ {
+ return document;
+ }
+
+ document = await documentStore.GetDocumentAsync(project, documentName, languageCode, version);
+
+ return document;
+ }
+}
\ No newline at end of file
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 015f711850..9b270297a7 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
@@ -24,6 +24,9 @@ namespace Volo.Docs.Documents
[JsonPropertyName("keywords")]
public string[] Keywords { get; set; }
+
+ [JsonPropertyName("ignoreOnDownload")]
+ public bool IgnoreOnDownload { get; set; }
public bool IsLeaf => !HasChildItems;