Browse Source

Export ABP documentation as PDF

pull/22430/head
liangshiwei 1 year ago
parent
commit
41704ce265
  1. 92
      modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20250411070040_initial.Designer.cs
  2. 61
      modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20250411070040_initial.cs
  3. 88
      modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/VoloDocsDbContextModelSnapshot.cs
  4. 1
      modules/docs/app/VoloDocs.EntityFrameworkCore/VoloDocs.EntityFrameworkCore.csproj
  5. 2
      modules/docs/app/VoloDocs.EntityFrameworkCore/VoloDocsDbContext.cs
  6. 4
      modules/docs/app/VoloDocs.EntityFrameworkCore/VoloDocsEntityFrameworkCoreModule.cs
  7. 2
      modules/docs/app/VoloDocs.Migrator/appsettings.json
  8. 2
      modules/docs/app/VoloDocs.Web/Pages/Index.cshtml.cs
  9. 14
      modules/docs/app/VoloDocs.Web/VoloDocsWebModule.cs
  10. 5
      modules/docs/app/VoloDocs.Web/appsettings.json
  11. 12
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Projects/DeletePdfFileInput.cs
  12. 2
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Projects/IProjectAdminAppService.cs
  13. 13
      modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Projects/ProjectAdminAppService.cs
  14. 8
      modules/docs/src/Volo.Docs.Admin.HttpApi.Client/ClientProxies/Volo/Docs/Admin/ProjectsAdminClientProxy.Generated.cs
  15. 1109
      modules/docs/src/Volo.Docs.Admin.HttpApi.Client/ClientProxies/docs-admin-generate-proxy.json
  16. 7
      modules/docs/src/Volo.Docs.Admin.HttpApi/Volo/Docs/Admin/ProjectsAdminController.cs
  17. 47
      modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/GeneratePdf.cshtml
  18. 62
      modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/GeneratePdf.cshtml.cs
  19. 2
      modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/Index.cshtml
  20. 67
      modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/generatePdf.js
  21. 16
      modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/index.js
  22. 8
      modules/docs/src/Volo.Docs.Admin.Web/wwwroot/client-proxies/docs-admin-proxy.js
  23. 68
      modules/docs/src/Volo.Docs.Admin.Web/wwwroot/client-proxies/docs-common-proxy.js
  24. 1
      modules/docs/src/Volo.Docs.Application.Contracts/Volo.Docs.Application.Contracts.csproj
  25. 2
      modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/DocsApplicationContractsModule.cs
  26. 1
      modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/DocumentWithDetailsDto.cs
  27. 1
      modules/docs/src/Volo.Docs.Application/Volo.Docs.Application.csproj
  28. 3
      modules/docs/src/Volo.Docs.Application/Volo/Docs/DocsApplicationAutoMapperProfile.cs
  29. 2
      modules/docs/src/Volo.Docs.Application/Volo/Docs/DocsApplicationModule.cs
  30. 1
      modules/docs/src/Volo.Docs.Application/Volo/Docs/Documents/DocumentAppService.cs
  31. 3
      modules/docs/src/Volo.Docs.Common.Application.Contracts/Volo/Docs/Common/Projects/IProjectAppService.cs
  32. 2
      modules/docs/src/Volo.Docs.Common.Application.Contracts/Volo/Docs/Common/Projects/ProjectDto.cs
  33. 2
      modules/docs/src/Volo.Docs.Common.Application.Contracts/Volo/Docs/Common/Projects/VersionInfoDto.cs
  34. 6
      modules/docs/src/Volo.Docs.Common.Application/Volo.Docs.Common.Application.csproj
  35. 14
      modules/docs/src/Volo.Docs.Common.Application/Volo/Docs/Common/DocsCommonAppServiceBase.cs
  36. 15
      modules/docs/src/Volo.Docs.Common.Application/Volo/Docs/Common/DocsCommonApplicationAutoMapperProfile.cs
  37. 14
      modules/docs/src/Volo.Docs.Common.Application/Volo/Docs/Common/DocsCommonApplicationModule.cs
  38. 18
      modules/docs/src/Volo.Docs.Common.Application/Volo/Docs/Common/Documents/DocumentPdfGeneratorAppService.cs
  39. 7
      modules/docs/src/Volo.Docs.Common.Application/Volo/Docs/Common/Projects/ProjectAppService.cs
  40. 28
      modules/docs/src/Volo.Docs.Common.HttpApi.Client/ClientProxies/Volo/Docs/Documents/DocsDocumentPdfGeneratorClientProxy.Generated.cs
  41. 7
      modules/docs/src/Volo.Docs.Common.HttpApi.Client/ClientProxies/Volo/Docs/Documents/DocsDocumentPdfGeneratorClientProxy.cs
  42. 2
      modules/docs/src/Volo.Docs.Common.HttpApi.Client/ClientProxies/Volo/Docs/Projects/DocsProjectClientProxy.Generated.cs
  43. 0
      modules/docs/src/Volo.Docs.Common.HttpApi.Client/ClientProxies/Volo/Docs/Projects/DocsProjectClientProxy.cs
  44. 420
      modules/docs/src/Volo.Docs.Common.HttpApi.Client/ClientProxies/docs-common-generate-proxy.json
  45. 25
      modules/docs/src/Volo.Docs.Common.HttpApi.Client/Volo/Docs/DocsCommonHttpApiClientModule.cs
  46. 6
      modules/docs/src/Volo.Docs.Common.HttpApi/Volo/Docs/Projects/DocsProjectController.cs
  47. 2
      modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/Projects/ProjectConsts.cs
  48. 8
      modules/docs/src/Volo.Docs.Domain/Volo.Docs.Domain.csproj
  49. 7
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/DocsDomainModule.cs
  50. 50
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Pdf/BlobDocumentPdfFileStore.cs
  51. 11
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Pdf/DocsDocumentPdfCacheItem.cs
  52. 2
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Pdf/DocsDocumentPdfContainer.cs
  53. 26
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Pdf/DocsDocumentPdfGeneratorOptions.cs
  54. 150
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Pdf/DocumentPdfGenerator.cs
  55. 12
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Pdf/IDocumentPdfFileStore.cs
  56. 5
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Pdf/IDocumentPdfGenerator.cs
  57. 10
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Pdf/IPdfRenderer.cs
  58. 4
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Pdf/IText/HtmlIdTagWorkerFactory.cs
  59. 73
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Pdf/IText/ITextPdfRenderer.cs
  60. 16
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Pdf/PdfDocumentNode.cs
  61. 329
      modules/docs/src/Volo.Docs.HttpApi.Client/ClientProxies/docs-generate-proxy.json
  62. 2
      modules/docs/src/Volo.Docs.Web/HtmlConverting/IDocumentToHtmlConverter.cs
  63. 6
      modules/docs/src/Volo.Docs.Web/Markdown/MarkdownDocumentToHtmlConverter.cs
  64. 2
      modules/docs/src/Volo.Docs.Web/Pages/Documents/Index.cshtml.cs
  65. 9
      modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml
  66. 15
      modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml.cs
  67. 14
      modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/index.css
  68. 53
      modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/index.scss
  69. 5
      modules/docs/src/Volo.Docs.Web/Pages/Documents/Search.cshtml.cs
  70. 68
      modules/docs/src/Volo.Docs.Web/wwwroot/client-proxies/docs-common-proxy.js
  71. 35
      modules/docs/src/Volo.Docs.Web/wwwroot/client-proxies/docs-proxy.js
  72. 2
      modules/docs/test/Volo.Docs.Application.Tests/Volo/Docs/ApplicationService_Tests.cs

92
modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20241231072012_Initial.Designer.cs → modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20250411070040_initial.Designer.cs

@ -13,8 +13,8 @@ using VoloDocs.EntityFrameworkCore;
namespace Migrations
{
[DbContext(typeof(VoloDocsDbContext))]
[Migration("20241231072012_Initial")]
partial class Initial
[Migration("20250411070040_initial")]
partial class initial
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -22,11 +22,88 @@ namespace Migrations
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer)
.HasAnnotation("ProductVersion", "9.0.0")
.HasAnnotation("ProductVersion", "9.0.2")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlob", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<Guid>("ContainerId")
.HasColumnType("uniqueidentifier");
b.Property<byte[]>("Content")
.HasMaxLength(2147483647)
.HasColumnType("varbinary(max)");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.HasKey("Id");
b.HasIndex("ContainerId");
b.HasIndex("TenantId", "ContainerId", "Name");
b.ToTable("AbpBlobs", (string)null);
});
modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlobContainer", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.HasKey("Id");
b.HasIndex("TenantId", "Name");
b.ToTable("AbpBlobContainers", (string)null);
});
modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b =>
{
b.Property<Guid>("Id")
@ -1119,6 +1196,15 @@ namespace Migrations
b.ToTable("DocsProjects", (string)null);
});
modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlob", b =>
{
b.HasOne("Volo.Abp.BlobStoring.Database.DatabaseBlobContainer", null)
.WithMany()
.HasForeignKey("ContainerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b =>
{
b.HasOne("Volo.Abp.Identity.IdentityRole", null)

61
modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20241231072012_Initial.cs → modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20250411070040_initial.cs

@ -6,11 +6,26 @@ using Microsoft.EntityFrameworkCore.Migrations;
namespace Migrations
{
/// <inheritdoc />
public partial class Initial : Migration
public partial class initial : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "AbpBlobContainers",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
Name = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: false),
ConcurrencyStamp = table.Column<string>(type: "nvarchar(40)", maxLength: 40, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AbpBlobContainers", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AbpClaimTypes",
columns: table => new
@ -336,6 +351,29 @@ namespace Migrations
table.PrimaryKey("PK_DocsProjects", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AbpBlobs",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
ContainerId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
Name = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
Content = table.Column<byte[]>(type: "varbinary(max)", maxLength: 2147483647, nullable: true),
ExtraProperties = table.Column<string>(type: "nvarchar(max)", nullable: false),
ConcurrencyStamp = table.Column<string>(type: "nvarchar(40)", maxLength: 40, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AbpBlobs", x => x.Id);
table.ForeignKey(
name: "FK_AbpBlobs_AbpBlobContainers_ContainerId",
column: x => x.ContainerId,
principalTable: "AbpBlobContainers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AbpOrganizationUnitRoles",
columns: table => new
@ -520,6 +558,21 @@ namespace Migrations
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_AbpBlobContainers_TenantId_Name",
table: "AbpBlobContainers",
columns: new[] { "TenantId", "Name" });
migrationBuilder.CreateIndex(
name: "IX_AbpBlobs_ContainerId",
table: "AbpBlobs",
column: "ContainerId");
migrationBuilder.CreateIndex(
name: "IX_AbpBlobs_TenantId_ContainerId_Name",
table: "AbpBlobs",
columns: new[] { "TenantId", "ContainerId", "Name" });
migrationBuilder.CreateIndex(
name: "IX_AbpLinkUsers_SourceUserId_SourceTenantId_TargetUserId_TargetTenantId",
table: "AbpLinkUsers",
@ -668,6 +721,9 @@ namespace Migrations
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "AbpBlobs");
migrationBuilder.DropTable(
name: "AbpClaimTypes");
@ -725,6 +781,9 @@ namespace Migrations
migrationBuilder.DropTable(
name: "DocsProjects");
migrationBuilder.DropTable(
name: "AbpBlobContainers");
migrationBuilder.DropTable(
name: "AbpOrganizationUnits");

88
modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/VoloDocsDbContextModelSnapshot.cs

@ -19,11 +19,88 @@ namespace Migrations
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer)
.HasAnnotation("ProductVersion", "9.0.0")
.HasAnnotation("ProductVersion", "9.0.2")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlob", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<Guid>("ContainerId")
.HasColumnType("uniqueidentifier");
b.Property<byte[]>("Content")
.HasMaxLength(2147483647)
.HasColumnType("varbinary(max)");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.HasKey("Id");
b.HasIndex("ContainerId");
b.HasIndex("TenantId", "ContainerId", "Name");
b.ToTable("AbpBlobs", (string)null);
});
modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlobContainer", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("nvarchar(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ExtraProperties");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.HasKey("Id");
b.HasIndex("TenantId", "Name");
b.ToTable("AbpBlobContainers", (string)null);
});
modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b =>
{
b.Property<Guid>("Id")
@ -1116,6 +1193,15 @@ namespace Migrations
b.ToTable("DocsProjects", (string)null);
});
modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlob", b =>
{
b.HasOne("Volo.Abp.BlobStoring.Database.DatabaseBlobContainer", null)
.WithMany()
.HasForeignKey("ContainerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b =>
{
b.HasOne("Volo.Abp.Identity.IdentityRole", null)

1
modules/docs/app/VoloDocs.EntityFrameworkCore/VoloDocs.EntityFrameworkCore.csproj

@ -13,6 +13,7 @@
<ProjectReference Include="..\..\..\..\modules\permission-management\src\Volo.Abp.PermissionManagement.EntityFrameworkCore\Volo.Abp.PermissionManagement.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\..\..\..\modules\setting-management\src\Volo.Abp.SettingManagement.EntityFrameworkCore\Volo.Abp.SettingManagement.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\..\..\..\modules\identity\src\Volo.Abp.Identity.EntityFrameworkCore\Volo.Abp.Identity.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\..\..\..\modules\blob-storing-database\src\Volo.Abp.BlobStoring.Database.EntityFrameworkCore\Volo.Abp.BlobStoring.Database.EntityFrameworkCore.csproj" />
</ItemGroup>
<ItemGroup>

2
modules/docs/app/VoloDocs.EntityFrameworkCore/VoloDocsDbContext.cs

@ -1,4 +1,5 @@
using Microsoft.EntityFrameworkCore;
using Volo.Abp.BlobStoring.Database.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.Identity.EntityFrameworkCore;
using Volo.Abp.PermissionManagement.EntityFrameworkCore;
@ -23,6 +24,7 @@ namespace VoloDocs.EntityFrameworkCore
modelBuilder.ConfigureSettingManagement();
modelBuilder.ConfigureIdentity();
modelBuilder.ConfigureDocs();
modelBuilder.ConfigureBlobStoring();
}
}
}

4
modules/docs/app/VoloDocs.EntityFrameworkCore/VoloDocsEntityFrameworkCoreModule.cs

@ -1,4 +1,5 @@
using Volo.Abp.EntityFrameworkCore.SqlServer;
using Volo.Abp.BlobStoring.Database.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.SqlServer;
using Volo.Abp.Identity.EntityFrameworkCore;
using Volo.Abp.Modularity;
using Volo.Abp.PermissionManagement.EntityFrameworkCore;
@ -12,6 +13,7 @@ namespace VoloDocs.EntityFrameworkCore
typeof(AbpIdentityEntityFrameworkCoreModule),
typeof(AbpPermissionManagementEntityFrameworkCoreModule),
typeof(AbpSettingManagementEntityFrameworkCoreModule),
typeof(BlobStoringDatabaseEntityFrameworkCoreModule),
typeof(AbpEntityFrameworkCoreSqlServerModule))]
public class VoloDocsEntityFrameworkCoreModule : AbpModule
{

2
modules/docs/app/VoloDocs.Migrator/appsettings.json

@ -1,3 +1,3 @@
{
"ConnectionString": "Server=localhost;Database=VoloDocs;Trusted_Connection=True;TrustServerCertificate=True"
"ConnectionString": "Server=localhost;Database=VoloDocs;user=sa;password=admin@123456;TrustServerCertificate=True"
}

2
modules/docs/app/VoloDocs.Web/Pages/Index.cshtml.cs

@ -5,7 +5,7 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Options;
using Volo.Docs;
using Volo.Docs.Projects;
using Volo.Docs.Common.Projects;
namespace VoloDocs.Web.Pages
{

14
modules/docs/app/VoloDocs.Web/VoloDocsWebModule.cs

@ -32,11 +32,14 @@ using Localization.Resources.AbpUi;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Hosting;
using Volo.Abp.Account;
using Volo.Abp.BlobStoring;
using Volo.Abp.BlobStoring.Database;
using Volo.Abp.PermissionManagement.HttpApi;
using Volo.Abp.Validation.Localization;
using Volo.Docs.Documents.FullSearch.Elastic;
using Volo.Abp.Caching.StackExchangeRedis;
using Volo.Docs.Common.Documents;
using Volo.Docs.Documents.Pdf;
namespace VoloDocs.Web
{
@ -83,7 +86,7 @@ namespace VoloDocs.Web
// options.SingleProjectMode.ProjectName = "abp";
// options.MultiLanguageMode = false;
// });
Configure<DocsElasticSearchOptions>(options =>
{
options.Enable = false;
@ -175,6 +178,15 @@ namespace VoloDocs.Web
Configure<DocsDocumentPdfGeneratorOptions>(options =>
{
options.BaseUrl = configuration["App:selfUrl"];
options.CoverPagePath = "Index.md";
});
Configure<AbpBlobStoringOptions>(options =>
{
options.Containers.ConfigureDefault(container =>
{
container.UseDatabase();
});
});
}

5
modules/docs/app/VoloDocs.Web/appsettings.json

@ -1,4 +1,7 @@
{
"App": {
"selfUrl": "https://localhost:5001"
},
"Kestrel": {
"EndPoints": {
"Https": {
@ -9,7 +12,7 @@
"Redis": {
"Configuration": "127.0.0.1"
},
"ConnectionString": "Server=localhost;Database=VoloDocs;Trusted_Connection=True;TrustServerCertificate=True",
"ConnectionString": "Server=localhost;Database=VoloDocs;user=sa;password=admin@123456;TrustServerCertificate=True",
"ElasticSearch": {
"Url": "http://localhost:9200"
},

12
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Projects/DeletePdfFileInput.cs

@ -0,0 +1,12 @@
using System;
namespace Volo.Docs.Admin.Projects;
public class DeletePdfFileInput
{
public Guid Id { get; set; }
public string Version { get; set; }
public string LanguageCode { get; set; }
}

2
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Projects/IProjectAdminAppService.cs

@ -22,5 +22,7 @@ namespace Volo.Docs.Admin.Projects
Task ReindexAllAsync();
Task<List<ProjectWithoutDetailsDto>> GetListWithoutDetailsAsync();
Task DeletePdfFileAsync(DeletePdfFileInput input);
}
}

13
modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Projects/ProjectAdminAppService.cs

@ -5,10 +5,13 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Caching;
using Volo.Abp.Data;
using Volo.Abp.Guids;
using Volo.Docs.Common.Documents;
using Volo.Docs.Documents;
using Volo.Docs.Documents.FullSearch.Elastic;
using Volo.Docs.Documents.Pdf;
using Volo.Docs.Localization;
using Volo.Docs.Projects;
@ -21,12 +24,14 @@ namespace Volo.Docs.Admin.Projects
private readonly IDocumentRepository _documentRepository;
private readonly IDocumentFullSearch _elasticSearchService;
private readonly IGuidGenerator _guidGenerator;
private readonly IDistributedCache<DocsDocumentPdfCacheItem> _documentPdfCache;
public ProjectAdminAppService(
IProjectRepository projectRepository,
IDocumentRepository documentRepository,
IDocumentFullSearch elasticSearchService,
IGuidGenerator guidGenerator)
IGuidGenerator guidGenerator,
IDistributedCache<DocsDocumentPdfCacheItem> documentPdfCache)
{
ObjectMapperContext = typeof(DocsAdminApplicationModule);
LocalizationResource = typeof(DocsResource);
@ -35,6 +40,7 @@ namespace Volo.Docs.Admin.Projects
_documentRepository = documentRepository;
_elasticSearchService = elasticSearchService;
_guidGenerator = guidGenerator;
_documentPdfCache = documentPdfCache;
}
public virtual async Task<PagedResultDto<ProjectDto>> GetListAsync(PagedAndSortedResultRequestDto input)
@ -176,5 +182,10 @@ namespace Volo.Docs.Admin.Projects
var projects = await _projectRepository.GetListWithoutDetailsAsync();
return ObjectMapper.Map<List<ProjectWithoutDetails>, List<ProjectWithoutDetailsDto>>(projects);
}
public virtual async Task DeletePdfFileAsync(DeletePdfFileInput input)
{
await _documentPdfCache.RemoveAsync(DocsDocumentPdfCacheItem.CalculateCacheKey(input.Id, input.Version, input.LanguageCode));
}
}
}

8
modules/docs/src/Volo.Docs.Admin.HttpApi.Client/ClientProxies/Volo/Docs/Admin/ProjectsAdminClientProxy.Generated.cs

@ -68,6 +68,14 @@ public partial class ProjectsAdminClientProxy : ClientProxyBase<IProjectAdminApp
return await RequestAsync<List<ProjectWithoutDetailsDto>>(nameof(GetListWithoutDetailsAsync));
}
public virtual async Task DeletePdfFileAsync(DeletePdfFileInput input)
{
await RequestAsync(nameof(DeletePdfFileAsync), new ClientProxyRequestTypeValue
{
{ typeof(DeletePdfFileInput), input }
});
}
public virtual async Task ReindexAsync(ReindexInput input)
{
await RequestAsync(nameof(ReindexAsync), new ClientProxyRequestTypeValue

1109
modules/docs/src/Volo.Docs.Admin.HttpApi.Client/ClientProxies/docs-admin-generate-proxy.json

File diff suppressed because it is too large

7
modules/docs/src/Volo.Docs.Admin.HttpApi/Volo/Docs/Admin/ProjectsAdminController.cs

@ -70,6 +70,13 @@ namespace Volo.Docs.Admin
return _projectAppService.GetListWithoutDetailsAsync();
}
[HttpDelete]
[Route("DeletePdfFile")]
public Task DeletePdfFileAsync(DeletePdfFileInput input)
{
return _projectAppService.DeletePdfFileAsync(input);
}
[HttpPost]
[Route("Reindex")]
public Task ReindexAsync(ReindexInput input)

47
modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/GeneratePdf.cshtml

@ -0,0 +1,47 @@
@page
@using Microsoft.AspNetCore.Mvc.Localization
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal
@using Volo.Docs.Admin.Pages.Docs.Admin.Projects;
@using Volo.Docs.Localization
@model GeneratePdfModal
@inject IHtmlLocalizer<DocsResource> L
@{
Layout = null;
}
<abp-modal>
<abp-modal-header title="@(L["GeneratePdf"].Value + " - " + Model.ViewModel.ShortName)"></abp-modal-header>
<abp-modal-body>
<input id="ProjectId" name="ProjectId" value="@Model.ViewModel.ProjectId" hidden/>
<input id="ShortName" name="ShortName" value="@Model.ViewModel.ShortName" hidden/>
<div class="mb-3">
<label class="form-label" for="Version">@L["Version"]</label>
<select name="Version" id="Version" class="form-select mt-2">
@foreach (var version in Model.ViewModel.Versions)
{
<option value="@version.Value">@version.Value</option>
}
</select>
</div>
<div class="mb-3">
<label class="form-label" for="Language">@L["Language"]</label>
<select name="Language" id="Language" class="form-select mt-2">
@foreach (var language in Model.ViewModel.Languages)
{
<option value="@language.Value">@language.Value</option>
}
</select>
</div>
<div class="form-check mb-3">
<label class="form-check-label" for="ForceToGenerate">
<input id="ForceToGenerate" type="checkbox" class="form-check-input" />
@L["ForceToGenerateNewPdf"]
</label>
</div>
</abp-modal-body>
<abp-modal-footer buttons="@(AbpModalButtons.None)">
<button type="button" class="btn btn-sm btn-link" data-bs-dismiss="modal">@L["Close"]</button>
<button type="button" class="btn btn-sm btn-outline-primary" id="GeneratePdfBtn" data-busy-text="@L["Generating"].Value"><span>@L["GeneratePdf"]</span></button>
<button type="button" class="btn btn-sm btn-primary" id="GenerateAndDownloadPdfBtn">@L["GenerateAndDownloadPdf"]</button>
</abp-modal-footer>
</abp-modal>

62
modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/GeneratePdf.cshtml.cs

@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Volo.Docs.Admin.Projects;
using Volo.Docs.Common.Documents;
using Volo.Docs.Common.Projects;
using Volo.Docs.Projects;
namespace Volo.Docs.Admin.Pages.Docs.Admin.Projects;
public class GeneratePdfModal : DocsAdminPageModel
{
protected IDocumentPdfGeneratorAppService DocumentPdfGeneratorAppService { get; }
protected IProjectAppService ProjectAppService { get; }
protected IProjectAdminAppService ProjectAdminAppService { get; }
public GeneratePdfViewModel ViewModel { get; set; }
public GeneratePdfModal(
IDocumentPdfGeneratorAppService documentPdfGeneratorAppService,
IProjectAppService projectAppService,
IProjectAdminAppService projectAdminAppService)
{
DocumentPdfGeneratorAppService = documentPdfGeneratorAppService;
ProjectAppService = projectAppService;
ProjectAdminAppService = projectAdminAppService;
}
public virtual async Task<IActionResult> OnGetAsync(Guid id)
{
var project = await ProjectAdminAppService.GetAsync(id);
var versions = await ProjectAppService.GetVersionsAsync(project.ShortName);
if(versions.Items.Count == 0)
{
versions.Items =
[
new VersionInfoDto { Name = ProjectConsts.Latest, DisplayName = ProjectConsts.Latest }
];
}
var languages = await ProjectAppService.GetLanguageListAsync(project.ShortName, versions.Items.FirstOrDefault()?.Name);
ViewModel = new GeneratePdfViewModel
{
ProjectId = id,
ShortName = project.ShortName,
Versions = versions.Items.Select(x => new SelectListItem(x.DisplayName, x.Name)).ToList(),
Languages = languages.Languages.Select(x => new SelectListItem(x.DisplayName, x.Code)).ToList()
};
return Page();
}
public class GeneratePdfViewModel
{
public Guid ProjectId { get; set; }
public string ShortName { get; set; }
public List<SelectListItem> Versions { get; set; }
public List<SelectListItem> Languages { get; set; }
}
}

2
modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/Index.cshtml

@ -20,10 +20,12 @@
@section scripts {
<abp-script src="/client-proxies/docs-admin-proxy.js" />
<abp-script src="/client-proxies/docs-common-proxy.js" />
<abp-script src="/Pages/Docs/Admin/Projects/index.js" />
<abp-script src="/Pages/Docs/Admin/Projects/create.js" />
<abp-script src="/Pages/Docs/Admin/Projects/edit.js" />
<abp-script src="/Pages/Docs/Admin/Projects/pull.js" />
<abp-script src="/Pages/Docs/Admin/Projects/generatePdf.js" />
}
<abp-card>

67
modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/generatePdf.js

@ -0,0 +1,67 @@
var abp = abp || {};
$(function () {
abp.modals.projectGeneratePdf = function () {
var projectAppService = volo.docs.projects.docsProject;
var pdfGeneratorAppService = volo.docs.documents.docsDocumentPdfGenerator;
var projectAdminAppService = volo.docs.admin.projectsAdmin;
var initModal = function (publicApi, args) {
};
$("#Versions").change(function () {
var shortname = $("#ShortName").val();
var version = $("#Version").val();
projectAppService.getLanguageList(shortname, version).done(function (result) {
$("#Language").empty();
$.each(result.languages, function (index, item) {
$("#Language").append($(`<option value="${item.code}">${item.displayName}</option>`));
});
});
})
$("#GeneratePdfBtn").click(function () {
var $btn = $(this);
$btn.buttonBusy(true);
var input = {
projectId: $("#ProjectId").val(),
version: $("#Version").val(),
languageCode: $("#Language").val(),
}
tryDeletePdfFile(input);
pdfGeneratorAppService.generatePdf(input, {
abpHandleError : false,
error: function (jqXHR) {
if (jqXHR.status === 200) {
abp.message.success("PDF generated successfully.");
$btn.buttonBusy(false);
} else {
abp.ajax.handleErrorStatusCode(jqXHR.status);
}
}
});
})
$("#GenerateAndDownloadPdfBtn").click(function () {
var input = {
projectId: $("#ProjectId").val(),
version: $("#Version").val(),
languageCode: $("#Language").val(),
}
tryDeletePdfFile(input);
window.open(abp.appPath + 'api/docs/documents/pdf' + abp.utils.buildQueryString([{ name: 'projectId', value: input.projectId }, { name: 'version', value: input.version }, { name: 'languageCode', value: input.languageCode }]), '_blank');
})
function tryDeletePdfFile(input) {
var forceToGenerate = $("#ForceToGenerate").is(":checked");
if(forceToGenerate) {
projectAdminAppService.deletePdfFile(input);
}
}
return {
initModal: initModal,
};
};
});

16
modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/index.js

@ -16,6 +16,11 @@ $(function () {
modalClass: 'projectPull',
});
var _generatePdfModal = new abp.ModalManager({
viewUrl: abp.appPath + 'Docs/Admin/Projects/GeneratePdf',
modalClass: 'projectGeneratePdf',
});
var _dataTable = $('#ProjectsTable').DataTable(
abp.libs.datatables.normalizeConfiguration({
processing: true,
@ -117,6 +122,17 @@ $(function () {
});
},
},
{
text: l('GeneratePdf'),
visible: abp.auth.isGranted(
'Docs.Admin.Documents'
),
action: function (data) {
_generatePdfModal.open({
Id: data.record.id,
});
}
}
],
},
},

8
modules/docs/src/Volo.Docs.Admin.Web/wwwroot/client-proxies/docs-admin-proxy.js

@ -136,6 +136,14 @@
}, ajaxParams));
};
volo.docs.admin.projectsAdmin.deletePdfFile = function(input, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/docs/admin/projects/DeletePdfFile' + abp.utils.buildQueryString([{ name: 'id', value: input.id }, { name: 'version', value: input.version }, { name: 'languageCode', value: input.languageCode }]) + '',
type: 'DELETE',
dataType: null
}, ajaxParams));
};
volo.docs.admin.projectsAdmin.reindex = function(input, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/docs/admin/projects/Reindex',

68
modules/docs/src/Volo.Docs.Admin.Web/wwwroot/client-proxies/docs-common-proxy.js

@ -0,0 +1,68 @@
/* This file is automatically generated by ABP framework to use MVC Controllers from javascript. */
// module docs-common
(function(){
// controller volo.docs.documents.docsDocumentPdfGenerator
(function(){
abp.utils.createNamespace(window, 'volo.docs.documents.docsDocumentPdfGenerator');
volo.docs.documents.docsDocumentPdfGenerator.generatePdf = function(input, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/docs/documents/pdf' + abp.utils.buildQueryString([{ name: 'projectId', value: input.projectId }, { name: 'version', value: input.version }, { name: 'languageCode', value: input.languageCode }]) + '',
type: 'GET'
}, ajaxParams));
};
})();
// controller volo.docs.projects.docsProject
(function(){
abp.utils.createNamespace(window, 'volo.docs.projects.docsProject');
volo.docs.projects.docsProject.getList = function(ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/docs/projects',
type: 'GET'
}, ajaxParams));
};
volo.docs.projects.docsProject.get = function(shortName, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/docs/projects/' + shortName + '',
type: 'GET'
}, ajaxParams));
};
volo.docs.projects.docsProject.getDefaultLanguageCode = function(shortName, version, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/docs/projects/' + shortName + '/defaultLanguage' + abp.utils.buildQueryString([{ name: 'version', value: version }]) + '',
type: 'GET'
}, { dataType: 'text' }, ajaxParams));
};
volo.docs.projects.docsProject.getVersions = function(shortName, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/docs/projects/' + shortName + '/versions',
type: 'GET'
}, ajaxParams));
};
volo.docs.projects.docsProject.getLanguageList = function(shortName, version, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/docs/projects/' + shortName + '/' + version + '/languageList',
type: 'GET'
}, ajaxParams));
};
})();
})();

1
modules/docs/src/Volo.Docs.Application.Contracts/Volo.Docs.Application.Contracts.csproj

@ -11,6 +11,7 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Docs.Common.Application.Contracts\Volo.Docs.Common.Application.Contracts.csproj" />
<ProjectReference Include="..\Volo.Docs.Domain.Shared\Volo.Docs.Domain.Shared.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.Ddd.Application.Contracts\Volo.Abp.Ddd.Application.Contracts.csproj" />
</ItemGroup>

2
modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/DocsApplicationContractsModule.cs

@ -1,10 +1,12 @@
using Volo.Abp.Application;
using Volo.Abp.Modularity;
using Volo.Docs.Common;
namespace Volo.Docs
{
[DependsOn(
typeof(DocsDomainSharedModule),
typeof(DocsCommonApplicationContractsModule),
typeof(AbpDddApplicationContractsModule)
)]
public class DocsApplicationContractsModule : AbpModule

1
modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Documents/DocumentWithDetailsDto.cs

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using Volo.Docs.Common.Projects;
using Volo.Docs.Projects;
namespace Volo.Docs.Documents

1
modules/docs/src/Volo.Docs.Application/Volo.Docs.Application.csproj

@ -12,6 +12,7 @@
<ItemGroup>
<ProjectReference Include="..\Volo.Docs.Application.Contracts\Volo.Docs.Application.Contracts.csproj" />
<ProjectReference Include="..\Volo.Docs.Common.Application\Volo.Docs.Common.Application.csproj" />
<ProjectReference Include="..\Volo.Docs.Domain\Volo.Docs.Domain.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.AutoMapper\Volo.Abp.AutoMapper.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.Caching\Volo.Abp.Caching.csproj" />

3
modules/docs/src/Volo.Docs.Application/Volo/Docs/DocsApplicationAutoMapperProfile.cs

@ -1,7 +1,8 @@
using AutoMapper;
using Volo.Docs.Documents;
using Volo.Docs.Projects;
using Volo.Abp.AutoMapper;
using Volo.Docs.Common.Projects;
using Volo.Docs.Projects;
namespace Volo.Docs
{

2
modules/docs/src/Volo.Docs.Application/Volo/Docs/DocsApplicationModule.cs

@ -4,6 +4,7 @@ using Volo.Abp.Application;
using Volo.Abp.AutoMapper;
using Volo.Abp.Caching;
using Volo.Abp.Modularity;
using Volo.Docs.Common;
using Volo.Docs.Documents;
namespace Volo.Docs
@ -13,6 +14,7 @@ namespace Volo.Docs
typeof(DocsApplicationContractsModule),
typeof(AbpCachingModule),
typeof(AbpAutoMapperModule),
typeof(DocsCommonApplicationModule),
typeof(AbpDddApplicationModule)
)]
public class DocsApplicationModule : AbpModule

1
modules/docs/src/Volo.Docs.Application/Volo/Docs/Documents/DocumentAppService.cs

@ -14,6 +14,7 @@ using Volo.Abp.Application.Dtos;
using Volo.Abp.Caching;
using Volo.Abp.Data;
using Volo.Docs.Caching;
using Volo.Docs.Common.Projects;
using Volo.Docs.Documents.FullSearch.Elastic;
using Volo.Docs.Projects;
using Volo.Extensions;

3
modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Projects/IProjectAppService.cs → modules/docs/src/Volo.Docs.Common.Application.Contracts/Volo/Docs/Common/Projects/IProjectAppService.cs

@ -2,8 +2,9 @@ using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Docs.Documents;
using Volo.Docs.Projects;
namespace Volo.Docs.Projects
namespace Volo.Docs.Common.Projects
{
public interface IProjectAppService : IApplicationService
{

2
modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Projects/ProjectDto.cs → modules/docs/src/Volo.Docs.Common.Application.Contracts/Volo/Docs/Common/Projects/ProjectDto.cs

@ -2,7 +2,7 @@ using System;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Data;
namespace Volo.Docs.Projects
namespace Volo.Docs.Common.Projects
{
[Serializable]
public class ProjectDto : EntityDto<Guid>, IHasExtraProperties

2
modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/Projects/VersionInfoDto.cs → modules/docs/src/Volo.Docs.Common.Application.Contracts/Volo/Docs/Common/Projects/VersionInfoDto.cs

@ -1,4 +1,4 @@
namespace Volo.Docs.Projects
namespace Volo.Docs.Common.Projects
{
public class VersionInfoDto
{

6
modules/docs/src/Volo.Docs.Common.Application/Volo.Docs.Common.Application.csproj

@ -16,11 +16,5 @@
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.AutoMapper\Volo.Abp.AutoMapper.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.Caching\Volo.Abp.Caching.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.Ddd.Application\Volo.Abp.Ddd.Application.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.BlobStoring\Volo.Abp.BlobStoring.csproj" />
<PackageReference Include="itext.bouncy-castle-adapter" />
<PackageReference Include="itext7" />
<PackageReference Include="itext7.pdfhtml" />
<PackageReference Include="Markdig.Signed" />
<PackageReference Include="Scriban" />
</ItemGroup>
</Project>

14
modules/docs/src/Volo.Docs.Common.Application/Volo/Docs/Common/DocsCommonAppServiceBase.cs

@ -0,0 +1,14 @@
using Volo.Abp.Application.Services;
using Volo.Docs.Localization;
namespace Volo.Docs.Common
{
public abstract class DocsCommonAppServiceBase : ApplicationService
{
protected DocsCommonAppServiceBase()
{
ObjectMapperContext = typeof(DocsCommonApplicationModule);
LocalizationResource = typeof(DocsResource);
}
}
}

15
modules/docs/src/Volo.Docs.Common.Application/Volo/Docs/Common/DocsCommonApplicationAutoMapperProfile.cs

@ -0,0 +1,15 @@
using AutoMapper;
using Volo.Docs.Common.Projects;
using Volo.Docs.Projects;
namespace Volo.Docs.Common
{
public class DocsCommonApplicationAutoMapperProfile : Profile
{
public DocsCommonApplicationAutoMapperProfile()
{
CreateMap<Project, ProjectDto>();
CreateMap<VersionInfo, VersionInfoDto>();
}
}
}

14
modules/docs/src/Volo.Docs.Common.Application/Volo/Docs/Common/DocsCommonApplicationModule.cs

@ -1,18 +1,13 @@
using Microsoft.Extensions.DependencyInjection;
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)
)]
@ -20,6 +15,11 @@ public class DocsCommonApplicationModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAutoMapperObjectMapper<DocsCommonApplicationModule>();
Configure<AbpAutoMapperOptions>(options =>
{
options.AddProfile<DocsCommonApplicationAutoMapperProfile>(validate: true);
});
}
}

18
modules/docs/src/Volo.Docs.Common.Application/Volo/Docs/Common/Documents/DocumentPdfGeneratorAppService.cs

@ -1,7 +1,9 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Application.Services;
using Volo.Abp.BlobStoring;
using Volo.Abp.Content;
using Volo.Docs.Documents.Pdf;
using Volo.Docs.Projects;
namespace Volo.Docs.Common.Documents;
@ -9,20 +11,20 @@ namespace Volo.Docs.Common.Documents;
[Authorize(DocsCommonPermissions.Documents.PdfGeneration)]
public class DocumentPdfGeneratorAppService : ApplicationService, IDocumentPdfGeneratorAppService
{
private readonly IDocumentPdfGenerator _documentPdfGenerator;
private readonly IProjectRepository _projectRepository;
protected IDocumentPdfGenerator DocumentPdfGenerator { get; }
protected IProjectRepository ProjectRepository { get; }
public DocumentPdfGeneratorAppService(
IDocumentPdfGenerator documentPdfGenerator,
IProjectRepository projectRepository)
{
_documentPdfGenerator = documentPdfGenerator;
_projectRepository = projectRepository;
DocumentPdfGenerator = documentPdfGenerator;
ProjectRepository = projectRepository;
}
public async Task<IRemoteStreamContent> GeneratePdfAsync(DocumentPdfGeneratorInput input)
public virtual async Task<IRemoteStreamContent> GeneratePdfAsync(DocumentPdfGeneratorInput input)
{
var project = await _projectRepository.GetAsync(input.ProjectId);
return await _documentPdfGenerator.GenerateAsync(project, input.Version, input.LanguageCode);
var project = await ProjectRepository.GetAsync(input.ProjectId);
return await DocumentPdfGenerator.GenerateAsync(project, input.Version, input.LanguageCode);
}
}

7
modules/docs/src/Volo.Docs.Application/Volo/Docs/Projects/ProjectAppService.cs → modules/docs/src/Volo.Docs.Common.Application/Volo/Docs/Common/Projects/ProjectAppService.cs

@ -4,16 +4,15 @@ using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Distributed;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Caching;
using Volo.Abp.Data;
using Volo.Abp.Guids;
using Volo.Docs.Caching;
using Volo.Docs.Documents;
using Volo.Docs.Projects;
namespace Volo.Docs.Projects
namespace Volo.Docs.Common.Projects
{
public class ProjectAppService : DocsAppServiceBase, IProjectAppService
public class ProjectAppService : DocsCommonAppServiceBase, IProjectAppService
{
private readonly IProjectRepository _projectRepository;
private readonly IDistributedCache<List<VersionInfo>> _versionCache;

28
modules/docs/src/Volo.Docs.Common.HttpApi.Client/ClientProxies/Volo/Docs/Documents/DocsDocumentPdfGeneratorClientProxy.Generated.cs

@ -0,0 +1,28 @@
// This file is automatically generated by ABP framework to use MVC Controllers from CSharp
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Content;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http.Client;
using Volo.Abp.Http.Client.ClientProxying;
using Volo.Abp.Http.Modeling;
using Volo.Docs.Common.Documents;
// ReSharper disable once CheckNamespace
namespace Volo.Docs.Documents;
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IDocumentPdfGeneratorAppService), typeof(DocsDocumentPdfGeneratorClientProxy))]
public partial class DocsDocumentPdfGeneratorClientProxy : ClientProxyBase<IDocumentPdfGeneratorAppService>, IDocumentPdfGeneratorAppService
{
public virtual async Task<IRemoteStreamContent> GeneratePdfAsync(DocumentPdfGeneratorInput input)
{
return await RequestAsync<IRemoteStreamContent>(nameof(GeneratePdfAsync), new ClientProxyRequestTypeValue
{
{ typeof(DocumentPdfGeneratorInput), input }
});
}
}

7
modules/docs/src/Volo.Docs.Common.HttpApi.Client/ClientProxies/Volo/Docs/Documents/DocsDocumentPdfGeneratorClientProxy.cs

@ -0,0 +1,7 @@
// This file is part of DocsDocumentPdfGeneratorClientProxy, you can customize it here
// ReSharper disable once CheckNamespace
namespace Volo.Docs.Documents;
public partial class DocsDocumentPdfGeneratorClientProxy
{
}

2
modules/docs/src/Volo.Docs.HttpApi.Client/ClientProxies/Volo/Docs/Projects/DocsProjectClientProxy.Generated.cs → modules/docs/src/Volo.Docs.Common.HttpApi.Client/ClientProxies/Volo/Docs/Projects/DocsProjectClientProxy.Generated.cs

@ -8,8 +8,8 @@ using Volo.Abp.DependencyInjection;
using Volo.Abp.Http.Client;
using Volo.Abp.Http.Client.ClientProxying;
using Volo.Abp.Http.Modeling;
using Volo.Docs.Common.Projects;
using Volo.Docs.Documents;
using Volo.Docs.Projects;
// ReSharper disable once CheckNamespace
namespace Volo.Docs.Projects;

0
modules/docs/src/Volo.Docs.HttpApi.Client/ClientProxies/Volo/Docs/Projects/DocsProjectClientProxy.cs → modules/docs/src/Volo.Docs.Common.HttpApi.Client/ClientProxies/Volo/Docs/Projects/DocsProjectClientProxy.cs

420
modules/docs/src/Volo.Docs.Common.HttpApi.Client/ClientProxies/docs-common-generate-proxy.json

@ -0,0 +1,420 @@
{
"modules": {
"docs-common": {
"rootPath": "docs-common",
"remoteServiceName": "AbpDocsCommon",
"controllers": {
"Volo.Docs.Documents.DocsDocumentPdfGeneratorController": {
"controllerName": "DocsDocumentPdfGenerator",
"controllerGroupName": "Document",
"isRemoteService": true,
"isIntegrationService": false,
"apiVersion": null,
"type": "Volo.Docs.Documents.DocsDocumentPdfGeneratorController",
"interfaces": [
{
"type": "Volo.Docs.Common.Documents.IDocumentPdfGeneratorAppService",
"name": "IDocumentPdfGeneratorAppService",
"methods": [
{
"name": "GeneratePdfAsync",
"parametersOnMethod": [
{
"name": "input",
"typeAsString": "Volo.Docs.Common.Documents.DocumentPdfGeneratorInput, Volo.Docs.Common.Application.Contracts",
"type": "Volo.Docs.Common.Documents.DocumentPdfGeneratorInput",
"typeSimple": "Volo.Docs.Common.Documents.DocumentPdfGeneratorInput",
"isOptional": false,
"defaultValue": null
}
],
"returnValue": {
"type": "Volo.Abp.Content.IRemoteStreamContent",
"typeSimple": "Volo.Abp.Content.IRemoteStreamContent"
}
}
]
}
],
"actions": {
"GeneratePdfAsyncByInput": {
"uniqueName": "GeneratePdfAsyncByInput",
"name": "GeneratePdfAsync",
"httpMethod": "GET",
"url": "api/docs/documents/pdf",
"supportedVersions": [],
"parametersOnMethod": [
{
"name": "input",
"typeAsString": "Volo.Docs.Common.Documents.DocumentPdfGeneratorInput, Volo.Docs.Common.Application.Contracts",
"type": "Volo.Docs.Common.Documents.DocumentPdfGeneratorInput",
"typeSimple": "Volo.Docs.Common.Documents.DocumentPdfGeneratorInput",
"isOptional": false,
"defaultValue": null
}
],
"parameters": [
{
"nameOnMethod": "input",
"name": "ProjectId",
"jsonName": null,
"type": "System.Guid",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null,
"constraintTypes": null,
"bindingSourceId": "ModelBinding",
"descriptorName": "input"
},
{
"nameOnMethod": "input",
"name": "Version",
"jsonName": null,
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null,
"constraintTypes": null,
"bindingSourceId": "ModelBinding",
"descriptorName": "input"
},
{
"nameOnMethod": "input",
"name": "LanguageCode",
"jsonName": null,
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null,
"constraintTypes": null,
"bindingSourceId": "ModelBinding",
"descriptorName": "input"
}
],
"returnValue": {
"type": "Volo.Abp.Content.IRemoteStreamContent",
"typeSimple": "Volo.Abp.Content.IRemoteStreamContent"
},
"allowAnonymous": null,
"implementFrom": "Volo.Docs.Common.Documents.IDocumentPdfGeneratorAppService"
}
}
},
"Volo.Docs.Projects.DocsProjectController": {
"controllerName": "DocsProject",
"controllerGroupName": "Project",
"isRemoteService": true,
"isIntegrationService": false,
"apiVersion": null,
"type": "Volo.Docs.Projects.DocsProjectController",
"interfaces": [
{
"type": "Volo.Docs.Common.Projects.IProjectAppService",
"name": "IProjectAppService",
"methods": [
{
"name": "GetListAsync",
"parametersOnMethod": [],
"returnValue": {
"type": "Volo.Abp.Application.Dtos.ListResultDto<Volo.Docs.Common.Projects.ProjectDto>",
"typeSimple": "Volo.Abp.Application.Dtos.ListResultDto<Volo.Docs.Common.Projects.ProjectDto>"
}
},
{
"name": "GetAsync",
"parametersOnMethod": [
{
"name": "shortName",
"typeAsString": "System.String, System.Private.CoreLib",
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null
}
],
"returnValue": {
"type": "Volo.Docs.Common.Projects.ProjectDto",
"typeSimple": "Volo.Docs.Common.Projects.ProjectDto"
}
},
{
"name": "GetVersionsAsync",
"parametersOnMethod": [
{
"name": "shortName",
"typeAsString": "System.String, System.Private.CoreLib",
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null
}
],
"returnValue": {
"type": "Volo.Abp.Application.Dtos.ListResultDto<Volo.Docs.Common.Projects.VersionInfoDto>",
"typeSimple": "Volo.Abp.Application.Dtos.ListResultDto<Volo.Docs.Common.Projects.VersionInfoDto>"
}
},
{
"name": "GetDefaultLanguageCodeAsync",
"parametersOnMethod": [
{
"name": "shortName",
"typeAsString": "System.String, System.Private.CoreLib",
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null
},
{
"name": "version",
"typeAsString": "System.String, System.Private.CoreLib",
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null
}
],
"returnValue": {
"type": "System.String",
"typeSimple": "string"
}
},
{
"name": "GetLanguageListAsync",
"parametersOnMethod": [
{
"name": "shortName",
"typeAsString": "System.String, System.Private.CoreLib",
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null
},
{
"name": "version",
"typeAsString": "System.String, System.Private.CoreLib",
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null
}
],
"returnValue": {
"type": "Volo.Docs.Documents.LanguageConfig",
"typeSimple": "Volo.Docs.Documents.LanguageConfig"
}
}
]
}
],
"actions": {
"GetListAsync": {
"uniqueName": "GetListAsync",
"name": "GetListAsync",
"httpMethod": "GET",
"url": "api/docs/projects",
"supportedVersions": [],
"parametersOnMethod": [],
"parameters": [],
"returnValue": {
"type": "Volo.Abp.Application.Dtos.ListResultDto<Volo.Docs.Common.Projects.ProjectDto>",
"typeSimple": "Volo.Abp.Application.Dtos.ListResultDto<Volo.Docs.Common.Projects.ProjectDto>"
},
"allowAnonymous": null,
"implementFrom": "Volo.Docs.Common.Projects.IProjectAppService"
},
"GetAsyncByShortName": {
"uniqueName": "GetAsyncByShortName",
"name": "GetAsync",
"httpMethod": "GET",
"url": "api/docs/projects/{shortName}",
"supportedVersions": [],
"parametersOnMethod": [
{
"name": "shortName",
"typeAsString": "System.String, System.Private.CoreLib",
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null
}
],
"parameters": [
{
"nameOnMethod": "shortName",
"name": "shortName",
"jsonName": null,
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null,
"constraintTypes": [],
"bindingSourceId": "Path",
"descriptorName": ""
}
],
"returnValue": {
"type": "Volo.Docs.Common.Projects.ProjectDto",
"typeSimple": "Volo.Docs.Common.Projects.ProjectDto"
},
"allowAnonymous": null,
"implementFrom": "Volo.Docs.Common.Projects.IProjectAppService"
},
"GetDefaultLanguageCodeAsyncByShortNameAndVersion": {
"uniqueName": "GetDefaultLanguageCodeAsyncByShortNameAndVersion",
"name": "GetDefaultLanguageCodeAsync",
"httpMethod": "GET",
"url": "api/docs/projects/{shortName}/defaultLanguage",
"supportedVersions": [],
"parametersOnMethod": [
{
"name": "shortName",
"typeAsString": "System.String, System.Private.CoreLib",
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null
},
{
"name": "version",
"typeAsString": "System.String, System.Private.CoreLib",
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null
}
],
"parameters": [
{
"nameOnMethod": "shortName",
"name": "shortName",
"jsonName": null,
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null,
"constraintTypes": [],
"bindingSourceId": "Path",
"descriptorName": ""
},
{
"nameOnMethod": "version",
"name": "version",
"jsonName": null,
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null,
"constraintTypes": null,
"bindingSourceId": "ModelBinding",
"descriptorName": ""
}
],
"returnValue": {
"type": "System.String",
"typeSimple": "string"
},
"allowAnonymous": null,
"implementFrom": "Volo.Docs.Common.Projects.IProjectAppService"
},
"GetVersionsAsyncByShortName": {
"uniqueName": "GetVersionsAsyncByShortName",
"name": "GetVersionsAsync",
"httpMethod": "GET",
"url": "api/docs/projects/{shortName}/versions",
"supportedVersions": [],
"parametersOnMethod": [
{
"name": "shortName",
"typeAsString": "System.String, System.Private.CoreLib",
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null
}
],
"parameters": [
{
"nameOnMethod": "shortName",
"name": "shortName",
"jsonName": null,
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null,
"constraintTypes": [],
"bindingSourceId": "Path",
"descriptorName": ""
}
],
"returnValue": {
"type": "Volo.Abp.Application.Dtos.ListResultDto<Volo.Docs.Common.Projects.VersionInfoDto>",
"typeSimple": "Volo.Abp.Application.Dtos.ListResultDto<Volo.Docs.Common.Projects.VersionInfoDto>"
},
"allowAnonymous": null,
"implementFrom": "Volo.Docs.Common.Projects.IProjectAppService"
},
"GetLanguageListAsyncByShortNameAndVersion": {
"uniqueName": "GetLanguageListAsyncByShortNameAndVersion",
"name": "GetLanguageListAsync",
"httpMethod": "GET",
"url": "api/docs/projects/{shortName}/{version}/languageList",
"supportedVersions": [],
"parametersOnMethod": [
{
"name": "shortName",
"typeAsString": "System.String, System.Private.CoreLib",
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null
},
{
"name": "version",
"typeAsString": "System.String, System.Private.CoreLib",
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null
}
],
"parameters": [
{
"nameOnMethod": "shortName",
"name": "shortName",
"jsonName": null,
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null,
"constraintTypes": [],
"bindingSourceId": "Path",
"descriptorName": ""
},
{
"nameOnMethod": "version",
"name": "version",
"jsonName": null,
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null,
"constraintTypes": [],
"bindingSourceId": "Path",
"descriptorName": ""
}
],
"returnValue": {
"type": "Volo.Docs.Documents.LanguageConfig",
"typeSimple": "Volo.Docs.Documents.LanguageConfig"
},
"allowAnonymous": null,
"implementFrom": "Volo.Docs.Common.Projects.IProjectAppService"
}
}
}
}
}
},
"types": {}
}

25
modules/docs/src/Volo.Docs.Common.HttpApi.Client/Volo/Docs/DocsCommonHttpApiClientModule.cs

@ -0,0 +1,25 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Http.Client;
using Volo.Abp.Modularity;
using Volo.Abp.VirtualFileSystem;
using Volo.Docs.Common;
namespace Volo.Docs
{
[DependsOn(
typeof(DocsCommonApplicationContractsModule),
typeof(AbpHttpClientModule)
)]
public class DocsCommonHttpApiClientModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddStaticHttpClientProxies(typeof(DocsCommonApplicationContractsModule).Assembly, DocsCommonRemoteServiceConsts.RemoteServiceName);
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<DocsCommonHttpApiClientModule>();
});
}
}
}

6
modules/docs/src/Volo.Docs.HttpApi/Volo/Docs/Projects/DocsProjectController.cs → modules/docs/src/Volo.Docs.Common.HttpApi/Volo/Docs/Projects/DocsProjectController.cs

@ -5,12 +5,14 @@ using Asp.Versioning;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Docs.Common;
using Volo.Docs.Common.Projects;
using Volo.Docs.Documents;
namespace Volo.Docs.Projects
{
[RemoteService(Name = DocsRemoteServiceConsts.RemoteServiceName)]
[Area(DocsRemoteServiceConsts.ModuleName)]
[RemoteService(Name = DocsCommonRemoteServiceConsts.RemoteServiceName)]
[Area(DocsCommonRemoteServiceConsts.ModuleName)]
[ControllerName("Project")]
[Route("api/docs/projects")]
public class DocsProjectController : AbpControllerBase, IProjectAppService

2
modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/Projects/ProjectConsts.cs

@ -36,5 +36,7 @@
/// Default value: 128
/// </summary>
public static int MaxVersionNameLength { get; set; } = 128;
public static string Latest = "latest";
}
}

8
modules/docs/src/Volo.Docs.Domain/Volo.Docs.Domain.csproj

@ -20,7 +20,12 @@
<PackageReference Include="Microsoft.Extensions.Http" />
<PackageReference Include="Octokit" />
<PackageReference Include="NEST" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" />
<PackageReference Include="itext.bouncy-castle-adapter" />
<PackageReference Include="itext7" />
<PackageReference Include="itext7.pdfhtml" />
<PackageReference Include="Markdig.Signed" />
<PackageReference Include="Scriban" />
</ItemGroup>
<ItemGroup>
@ -28,6 +33,7 @@
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.Ddd.Domain\Volo.Abp.Ddd.Domain.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.Caching\Volo.Abp.Caching.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.AutoMapper\Volo.Abp.AutoMapper.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.BlobStoring\Volo.Abp.BlobStoring.csproj" />
</ItemGroup>
</Project>

7
modules/docs/src/Volo.Docs.Domain/Volo/Docs/DocsDomainModule.cs

@ -1,10 +1,11 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp;
using Volo.Abp.AutoMapper;
using Volo.Abp.BlobStoring;
using Volo.Abp.Caching;
using Volo.Abp.Domain;
using Volo.Abp.Domain.Entities.Events.Distributed;
using Volo.Abp.Localization;
@ -24,7 +25,9 @@ namespace Volo.Docs
[DependsOn(
typeof(DocsDomainSharedModule),
typeof(AbpDddDomainModule),
typeof(AbpAutoMapperModule)
typeof(AbpAutoMapperModule),
typeof(AbpBlobStoringModule),
typeof(AbpCachingModule)
)]
public class DocsDomainModule : AbpModule
{

50
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Pdf/BlobDocumentPdfFileStore.cs

@ -0,0 +1,50 @@
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Options;
using Volo.Abp.BlobStoring;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Docs.Projects;
namespace Volo.Docs.Documents.Pdf;
public class BlobDocumentPdfFileStore : IDocumentPdfFileStore, ITransientDependency
{
protected IBlobContainer<DocsDocumentPdfContainer> BlobContainer { get; }
protected IDistributedCache<DocsDocumentPdfCacheItem> Cache { get; }
protected IOptions<DocsDocumentPdfGeneratorOptions> Options { get; }
public BlobDocumentPdfFileStore(
IBlobContainer<DocsDocumentPdfContainer> blobContainer,
IDistributedCache<DocsDocumentPdfCacheItem> cache,
IOptions<DocsDocumentPdfGeneratorOptions> options)
{
BlobContainer = blobContainer;
Cache = cache;
Options = options;
}
public virtual async Task SetAsync(Project project, string version, string languageCode, Stream stream)
{
await BlobContainer.SaveAsync(Options.Value.CalculatePdfFileName(project, version, languageCode), stream);
await Cache.SetAsync(DocsDocumentPdfCacheItem.CalculateCacheKey(project.Id, version, languageCode), new DocsDocumentPdfCacheItem(),
new DistributedCacheEntryOptions
{
AbsoluteExpiration = DateTimeOffset.Now.Add(Options.Value.PdfFileCacheExpiration)
});
}
public virtual async Task<Stream> GetOrNullAsync(Project project, string version, string languageCode)
{
var cache = await Cache.GetAsync(DocsDocumentPdfCacheItem.CalculateCacheKey(project.Id, version, languageCode));
if (cache == null)
{
return null;
}
return await BlobContainer.GetOrNullAsync(Options.Value.CalculatePdfFileName(project, version, languageCode));
}
}

11
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Pdf/DocsDocumentPdfCacheItem.cs

@ -0,0 +1,11 @@
using System;
namespace Volo.Docs.Documents.Pdf;
public class DocsDocumentPdfCacheItem
{
public static string CalculateCacheKey(Guid projectId, string version, string languageCode)
{
return $"{projectId}_{version}_{languageCode}";
}
}

2
modules/docs/src/Volo.Docs.Common.Application/Volo/Docs/Common/Documents/DocsDocumentPdfContainer.cs → modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Pdf/DocsDocumentPdfContainer.cs

@ -1,6 +1,6 @@
using Volo.Abp.BlobStoring;
namespace Volo.Docs.Common.Documents;
namespace Volo.Docs.Documents.Pdf;
[BlobContainerName("docs-document-pdf")]
public class DocsDocumentPdfContainer

26
modules/docs/src/Volo.Docs.Common.Application/Volo/Docs/Common/Documents/DocsDocumentPdfGeneratorOptions.cs → modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Pdf/DocsDocumentPdfGeneratorOptions.cs

@ -1,24 +1,38 @@
namespace Volo.Docs.Common.Documents;
using System;
using Volo.Docs.Projects;
namespace Volo.Docs.Documents.Pdf;
public class DocsDocumentPdfGeneratorOptions
{
public const string StylePlaceholder = "{{style-placeholder}}";
public const string ContentPlaceholder = "{{content-placeholder}}";
/// <summary>
/// The HTML layout for the PDF document.
/// </summary>
public string HtmlLayout { get; set; }
public string HtmlStyles { get; set; }
public string BaseUrl { get; set; }
public string CoverPagePath { get; set; }
public TimeSpan PdfFileCacheExpiration { get; set; } = TimeSpan.FromDays(1);
public Func<Project, string, string, string> CalculatePdfFileName { get; set; } = (project, version, languageCode) =>
{
return $"{project.ShortName}_{version}_{languageCode}.pdf";
};
public DocsDocumentPdfGeneratorOptions()
{
HtmlLayout = @"
HtmlLayout = $@"
<!DOCTYPE html>
<head>
<meta charset='utf-8' />
<style>
{{style-placeholder}}
{StylePlaceholder}
</style>
</head>
<body>
{{content-placeholder}}
{ContentPlaceholder}
</body>
</html>";

150
modules/docs/src/Volo.Docs.Common.Application/Volo/Docs/Common/Documents/DocumentPdfGenerator.cs → modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Pdf/DocumentPdfGenerator.cs

@ -4,62 +4,82 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using iText.Html2pdf;
using iText.Kernel.Pdf;
using iText.Kernel.Pdf.Action;
using Markdig;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Volo.Abp;
using Volo.Abp.Content;
using Volo.Docs.Documents;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Threading;
using Volo.Docs.Documents.Pdf.IText;
using Volo.Docs.Documents.Rendering;
using Volo.Docs.Projects;
using Volo.Extensions;
using ITextDocument = iText.Layout.Document;
namespace Volo.Docs.Common.Documents;
namespace Volo.Docs.Documents.Pdf;
public class DocumentPdfGenerator : IDocumentPdfGenerator
public class DocumentPdfGenerator : IDocumentPdfGenerator, ITransientDependency
{
protected IDocumentSourceFactory DocumentStoreFactory { get; }
protected IDocumentRepository DocumentRepository { get; }
protected IDocumentSectionRenderer DocumentSectionRenderer { get; }
protected IOptions<DocsDocumentPdfGeneratorOptions> Options { get; }
protected IOptions<DocsDocumentPdfGeneratorOptions> Options { get; }
protected IDocumentPdfFileStore DocumentPdfFileStore { get; }
protected IPdfRenderer PdfRenderer { get; }
protected ILogger<DocumentPdfGenerator> Logger { get; set; }
protected IDocumentSource DocumentSource { get; set; }
protected DocumentParams DocumentParams { get; set; }
protected List<PdfDocumentNode> AllPdfDocumentNodes { get; set; } = new();
protected List<PdfDocumentNode> AllPdfDocumentNodes { get; set; } = [];
protected MarkdownPipeline MarkdownPipeline { get; } = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build();
private readonly SemaphoreSlim _syncSemaphore;
public DocumentPdfGenerator(
IDocumentSourceFactory documentStoreFactory,
IDocumentRepository documentRepository,
IOptions<DocsDocumentPdfGeneratorOptions> options,
IDocumentSectionRenderer documentSectionRenderer)
IDocumentSectionRenderer documentSectionRenderer,
IDocumentPdfFileStore documentPdfFileStore,
IPdfRenderer pdfRenderer)
{
DocumentStoreFactory = documentStoreFactory;
DocumentRepository = documentRepository;
Options = options;
DocumentSectionRenderer = documentSectionRenderer;
DocumentPdfFileStore = documentPdfFileStore;
PdfRenderer = pdfRenderer;
Logger = NullLogger<DocumentPdfGenerator>.Instance;
_syncSemaphore = new SemaphoreSlim(1, 1);
}
public virtual async Task<IRemoteStreamContent> GenerateAsync(Project project, string version, string languageCode)
{
DocumentSource = DocumentStoreFactory.Create(project.DocumentStoreType);
DocumentParams = await GetDocumentParamsAsync(project, version, languageCode);
var navigation = await GetNavigationAsync(project, version, languageCode);
using (await _syncSemaphore.LockAsync())
{
var fileName = Options.Value.CalculatePdfFileName(project, version, languageCode);
var fileStream = await DocumentPdfFileStore.GetOrNullAsync(project, version, languageCode);
if (fileStream != null)
{
return new RemoteStreamContent(fileStream, fileName, "application/pdf");
}
DocumentSource = DocumentStoreFactory.Create(project.DocumentStoreType);
DocumentParams = await GetDocumentParamsAsync(project, version, languageCode);
var navigation = await GetNavigationAsync(project, version, languageCode);
await SetAllPdfDocumentNodesAsync(navigation.Items, project, version, languageCode);
var htmlContent = await ConvertDocumentsToHtmlAsync(AllPdfDocumentNodes);
await SetAllPdfDocumentNodesAsync(navigation.Items, project, version, languageCode);
var htmlContent = await ConvertDocumentsToHtmlAsync(AllPdfDocumentNodes);
var pdfStream = await GeneratePdfAsync(htmlContent);
var pdfStream = await PdfRenderer.GeneratePdfAsync(htmlContent, AllPdfDocumentNodes);
await DocumentPdfFileStore.SetAsync(project, version, languageCode, pdfStream);
return new RemoteStreamContent(pdfStream, CalculateDocumentPdfFileName(project.ShortName, version, languageCode), "application/pdf");
return new RemoteStreamContent(pdfStream, fileName, "application/pdf");
}
}
protected virtual async Task<string> ConvertDocumentsToHtmlAsync(List<PdfDocumentNode> pdfDocumentNodes)
@ -84,56 +104,6 @@ public class DocumentPdfGenerator : IDocumentPdfGenerator
return contentBuilder.ToString();
}
protected virtual Task<MemoryStream> GeneratePdfAsync(string htmlContent)
{
var pdfStream = new MemoryStream();
var pdfWrite = new PdfWriter(pdfStream);
var pdfDocument = new PdfDocument(pdfWrite);
var textDocument = new ITextDocument(pdfDocument);
pdfWrite.SetCloseStream(false);
var htmlBuilder = new StringBuilder();
htmlBuilder.Append(Options.Value.HtmlLayout);
htmlBuilder.Replace("{{style-placeholder}}", Options.Value.HtmlStyles);
htmlBuilder.Replace("{{content-placeholder}}", htmlContent);
var converter = new ConverterProperties();
var tagWorkerFactory = new HtmlIdTagWorkerFactory(pdfDocument);
converter.SetTagWorkerFactory(tagWorkerFactory);
HtmlConverter.ConvertToDocument(htmlBuilder.ToString(), pdfDocument, converter);
tagWorkerFactory.AddNamedDestinations();
var pdfOutlines = pdfDocument.GetOutlines(false);
BuildPdfOutlines(pdfOutlines, AllPdfDocumentNodes);
textDocument.Close();
pdfStream.Position = 0;
return Task.FromResult(pdfStream);
}
private void BuildPdfOutlines(PdfOutline parentOutline, List<PdfDocumentNode> pdfDocumentNodes)
{
foreach (var pdfDocumentNode in pdfDocumentNodes)
{
if (pdfDocumentNode.IgnoreOnOutline)
{
continue;
}
var outline = parentOutline.AddOutline(pdfDocumentNode.Title);
if (!pdfDocumentNode.Id.IsNullOrWhiteSpace())
{
outline.AddAction(PdfAction.CreateGoTo(pdfDocumentNode.Id));
}
if (pdfDocumentNode.HasChildren)
{
BuildPdfOutlines(outline, pdfDocumentNode.Children);
}
}
}
protected virtual async Task<string> RenderDocumentAsync(PdfDocumentNode pdfDocumentNode)
{
var renderedDocument = await DocumentSectionRenderer.RenderAsync(pdfDocumentNode.Document.Content, pdfDocumentNode.RenderParameters);
@ -164,7 +134,7 @@ public class DocumentPdfGenerator : IDocumentPdfGenerator
var pdfDocumentNode = new PdfDocumentNode
{
Title = navigation.Text,
IgnoreOnOutline = navigation.Path == "index.md"
IgnoreOnOutline = navigation.Path == Options.Value.CoverPagePath
};
if (!navigation.Path.IsNullOrWhiteSpace())
@ -228,11 +198,6 @@ public class DocumentPdfGenerator : IDocumentPdfGenerator
}
}
protected virtual string CalculateDocumentPdfFileName(string projectName, string version, string languageCode)
{
return $"{projectName.ToLower()}-{languageCode.ToLower()}.pdf";
}
private string NormalizeNavigationPath(string path)
{
return !path.EndsWith(".md") ? Path.Combine(path, "index.md") : path;
@ -249,12 +214,14 @@ public class DocumentPdfGenerator : IDocumentPdfGenerator
{
throw new UserFriendlyException($"Cannot validate navigation file '{project.NavigationDocumentName}' for the project {project.Name}.");
}
navigation.Items.Insert(0, new NavigationNode
if (!Options.Value.CoverPagePath.IsNullOrWhiteSpace())
{
Text = "Index",
Path = "index.md"
});
navigation.Items.Insert(0, new NavigationNode
{
Path = Options.Value.CoverPagePath
});
}
return navigation;
}
@ -337,7 +304,7 @@ public class DocumentPdfGenerator : IDocumentPdfGenerator
private string SetDocumentTitle(string content, string title)
{
var titleLine = content.Split('\n').FirstOrDefault(x => x.TrimStart().StartsWith("#"));
var titleLine = content.Split(Environment.NewLine).FirstOrDefault(x => x.TrimStart().StartsWith("#"));
if (titleLine == null)
{
return content;
@ -354,7 +321,7 @@ public class DocumentPdfGenerator : IDocumentPdfGenerator
return title;
}
var titleLine = content.Split('\n').FirstOrDefault(x => x.TrimStart().StartsWith("#"));
var titleLine = content.Split(Environment.NewLine).FirstOrDefault(x => x.TrimStart().StartsWith("#"));
if (titleLine == null)
{
return title;
@ -378,8 +345,14 @@ public class DocumentPdfGenerator : IDocumentPdfGenerator
private string NormalizeHtmlContent(PdfDocumentNode pdfDocumentNode, string htmlContent)
{
htmlContent = $"<div class='page' id='{pdfDocumentNode.Id}'>{htmlContent}</div>";
htmlContent = Regex.Replace(htmlContent, @"(<img\s+[^>]*)src=""([^""]*)""([^>]*>)", delegate (Match match)
htmlContent = ReplaceRelativeImageUrls(pdfDocumentNode, htmlContent);
return htmlContent;
}
private string ReplaceRelativeImageUrls(PdfDocumentNode pdfDocumentNode, string htmlContent)
{
return Regex.Replace(htmlContent, @"(<img\s+[^>]*)src=""([^""]*)""([^>]*>)", delegate (Match match)
{
if (IsExternalLink(match.Groups[2].Value))
{
@ -396,8 +369,6 @@ public class DocumentPdfGenerator : IDocumentPdfGenerator
return match.Groups[1] + " src=\"" + HttpUtility.HtmlEncode(newImageSource) + "\" " + match.Groups[3];
}, RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Multiline);
return htmlContent;
}
private bool IsExternalLink(string link)
@ -405,15 +376,4 @@ public class DocumentPdfGenerator : IDocumentPdfGenerator
return link.StartsWith("http://", StringComparison.OrdinalIgnoreCase) ||
link.StartsWith("https://", StringComparison.OrdinalIgnoreCase);
}
protected class PdfDocumentNode
{
public Document Document { get; set; }
public string Title { get; set; }
public string Id { get; set; }
public List<PdfDocumentNode> Children { get; set; } = [];
public DocumentRenderParameters RenderParameters { get; set; }
public bool HasChildren => Children.Any();
public bool IgnoreOnOutline { get; set; }
}
}

12
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Pdf/IDocumentPdfFileStore.cs

@ -0,0 +1,12 @@
using System.IO;
using System.Threading.Tasks;
using Volo.Docs.Projects;
namespace Volo.Docs.Documents.Pdf;
public interface IDocumentPdfFileStore
{
Task SetAsync(Project project, string version, string languageCode, Stream stream);
Task<Stream> GetOrNullAsync(Project project, string version, string languageCode);
}

5
modules/docs/src/Volo.Docs.Common.Application/Volo/Docs/Common/Documents/IDocumentPdfGenerator.cs → modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Pdf/IDocumentPdfGenerator.cs

@ -1,11 +1,10 @@
using System.Threading.Tasks;
using Volo.Abp.Content;
using Volo.Abp.DependencyInjection;
using Volo.Docs.Projects;
namespace Volo.Docs.Common.Documents;
namespace Volo.Docs.Documents.Pdf;
public interface IDocumentPdfGenerator : ITransientDependency
public interface IDocumentPdfGenerator
{
Task<IRemoteStreamContent> GenerateAsync(Project project, string version, string languageCode);
}

10
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Pdf/IPdfRenderer.cs

@ -0,0 +1,10 @@
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
namespace Volo.Docs.Documents.Pdf;
public interface IPdfRenderer
{
Task<MemoryStream> GeneratePdfAsync(string htmlContent, List<PdfDocumentNode> pdfDocumentNodes);
}

4
modules/docs/src/Volo.Docs.Common.Application/Volo/Docs/Common/Documents/HtmlIdTagWorkerFactory.cs → modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Pdf/IText/HtmlIdTagWorkerFactory.cs

@ -5,9 +5,9 @@ using iText.Kernel.Pdf;
using iText.Kernel.Pdf.Navigation;
using iText.StyledXmlParser.Node;
namespace Volo.Docs.Common.Documents;
namespace Volo.Docs.Documents.Pdf.IText;
public class HtmlIdTagWorkerFactory: DefaultTagWorkerFactory
public class HtmlIdTagWorkerFactory : DefaultTagWorkerFactory
{
private readonly PdfDocument _pdfDocument;
private readonly Dictionary<string, int> _pageDestinations = new();

73
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Pdf/IText/ITextPdfRenderer.cs

@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using iText.Html2pdf;
using iText.Kernel.Pdf;
using iText.Kernel.Pdf.Action;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using ITextDocument = iText.Layout.Document;
namespace Volo.Docs.Documents.Pdf.IText;
public class ITextPdfRenderer : IPdfRenderer ,ITransientDependency
{
protected IOptions<DocsDocumentPdfGeneratorOptions> Options { get; }
public ITextPdfRenderer(IOptions<DocsDocumentPdfGeneratorOptions> options)
{
Options = options;
}
public virtual Task<MemoryStream> GeneratePdfAsync(string htmlContent, List<PdfDocumentNode> pdfDocumentNodes)
{
var pdfStream = new MemoryStream();
var pdfWrite = new PdfWriter(pdfStream);
var pdfDocument = new PdfDocument(pdfWrite);
var textDocument = new ITextDocument(pdfDocument);
pdfWrite.SetCloseStream(false);
var htmlBuilder = new StringBuilder();
htmlBuilder.Append(Options.Value.HtmlLayout);
htmlBuilder.Replace(DocsDocumentPdfGeneratorOptions.StylePlaceholder, Options.Value.HtmlStyles);
htmlBuilder.Replace(DocsDocumentPdfGeneratorOptions.ContentPlaceholder, htmlContent);
var converter = new ConverterProperties();
var tagWorkerFactory = new HtmlIdTagWorkerFactory(pdfDocument);
converter.SetTagWorkerFactory(tagWorkerFactory);
HtmlConverter.ConvertToDocument(htmlBuilder.ToString(), pdfDocument, converter);
tagWorkerFactory.AddNamedDestinations();
var pdfOutlines = pdfDocument.GetOutlines(false);
BuildPdfOutlines(pdfOutlines, pdfDocumentNodes);
textDocument.Close();
pdfStream.Position = 0;
return Task.FromResult(pdfStream);
}
private void BuildPdfOutlines(PdfOutline parentOutline, List<PdfDocumentNode> pdfDocumentNodes)
{
foreach (var pdfDocumentNode in pdfDocumentNodes)
{
if (pdfDocumentNode.IgnoreOnOutline)
{
continue;
}
var outline = parentOutline.AddOutline(pdfDocumentNode.Title);
if (!pdfDocumentNode.Id.IsNullOrWhiteSpace())
{
outline.AddAction(PdfAction.CreateGoTo(pdfDocumentNode.Id));
}
if (pdfDocumentNode.HasChildren)
{
BuildPdfOutlines(outline, pdfDocumentNode.Children);
}
}
}
}

16
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Pdf/PdfDocumentNode.cs

@ -0,0 +1,16 @@
using System.Collections.Generic;
using System.Linq;
using Volo.Docs.Documents.Rendering;
namespace Volo.Docs.Documents.Pdf;
public class PdfDocumentNode
{
public Document Document { get; set; }
public string Title { get; set; }
public string Id { get; set; }
public List<PdfDocumentNode> Children { get; set; } = [];
public DocumentRenderParameters RenderParameters { get; set; }
public bool HasChildren => Children.Any();
public bool IgnoreOnOutline { get; set; }
}

329
modules/docs/src/Volo.Docs.HttpApi.Client/ClientProxies/docs-generate-proxy.json

@ -4,206 +4,95 @@
"rootPath": "docs",
"remoteServiceName": "AbpDocs",
"controllers": {
"Volo.Docs.Projects.DocsProjectController": {
"controllerName": "DocsProject",
"controllerGroupName": "Project",
"Volo.Docs.Areas.Documents.DocumentNavigationController": {
"controllerName": "DocumentNavigation",
"controllerGroupName": "DocumentNavigation",
"isRemoteService": true,
"isIntegrationService": false,
"apiVersion": null,
"type": "Volo.Docs.Projects.DocsProjectController",
"interfaces": [
{
"type": "Volo.Docs.Projects.IProjectAppService",
"name": "IProjectAppService",
"methods": [
{
"name": "GetListAsync",
"parametersOnMethod": [],
"returnValue": {
"type": "Volo.Abp.Application.Dtos.ListResultDto<Volo.Docs.Projects.ProjectDto>",
"typeSimple": "Volo.Abp.Application.Dtos.ListResultDto<Volo.Docs.Projects.ProjectDto>"
}
},
{
"name": "GetAsync",
"parametersOnMethod": [
{
"name": "shortName",
"typeAsString": "System.String, System.Private.CoreLib",
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null
}
],
"returnValue": {
"type": "Volo.Docs.Projects.ProjectDto",
"typeSimple": "Volo.Docs.Projects.ProjectDto"
}
},
{
"name": "GetVersionsAsync",
"parametersOnMethod": [
{
"name": "shortName",
"typeAsString": "System.String, System.Private.CoreLib",
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null
}
],
"returnValue": {
"type": "Volo.Abp.Application.Dtos.ListResultDto<Volo.Docs.Projects.VersionInfoDto>",
"typeSimple": "Volo.Abp.Application.Dtos.ListResultDto<Volo.Docs.Projects.VersionInfoDto>"
}
},
{
"name": "GetDefaultLanguageCodeAsync",
"parametersOnMethod": [
{
"name": "shortName",
"typeAsString": "System.String, System.Private.CoreLib",
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null
},
{
"name": "version",
"typeAsString": "System.String, System.Private.CoreLib",
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null
}
],
"returnValue": {
"type": "System.String",
"typeSimple": "string"
}
},
{
"name": "GetLanguageListAsync",
"parametersOnMethod": [
{
"name": "shortName",
"typeAsString": "System.String, System.Private.CoreLib",
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null
},
{
"name": "version",
"typeAsString": "System.String, System.Private.CoreLib",
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null
}
],
"returnValue": {
"type": "Volo.Docs.Documents.LanguageConfig",
"typeSimple": "Volo.Docs.Documents.LanguageConfig"
}
}
]
}
],
"type": "Volo.Docs.Areas.Documents.DocumentNavigationController",
"interfaces": [],
"actions": {
"GetListAsync": {
"uniqueName": "GetListAsync",
"name": "GetListAsync",
"httpMethod": "GET",
"url": "api/docs/projects",
"supportedVersions": [],
"parametersOnMethod": [],
"parameters": [],
"returnValue": {
"type": "Volo.Abp.Application.Dtos.ListResultDto<Volo.Docs.Projects.ProjectDto>",
"typeSimple": "Volo.Abp.Application.Dtos.ListResultDto<Volo.Docs.Projects.ProjectDto>"
},
"allowAnonymous": null,
"implementFrom": "Volo.Docs.Projects.IProjectAppService"
},
"GetAsyncByShortName": {
"uniqueName": "GetAsyncByShortName",
"name": "GetAsync",
"GetNavigationAsyncByInput": {
"uniqueName": "GetNavigationAsyncByInput",
"name": "GetNavigationAsync",
"httpMethod": "GET",
"url": "api/docs/projects/{shortName}",
"url": "docs/document-navigation",
"supportedVersions": [],
"parametersOnMethod": [
{
"name": "shortName",
"typeAsString": "System.String, System.Private.CoreLib",
"type": "System.String",
"typeSimple": "string",
"name": "input",
"typeAsString": "Volo.Docs.Areas.Models.DocumentNavigation.GetNavigationNodeWithLinkModel, Volo.Docs.Web",
"type": "Volo.Docs.Areas.Models.DocumentNavigation.GetNavigationNodeWithLinkModel",
"typeSimple": "Volo.Docs.Areas.Models.DocumentNavigation.GetNavigationNodeWithLinkModel",
"isOptional": false,
"defaultValue": null
}
],
"parameters": [
{
"nameOnMethod": "shortName",
"name": "shortName",
"nameOnMethod": "input",
"name": "ProjectId",
"jsonName": null,
"type": "System.Guid",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null,
"constraintTypes": null,
"bindingSourceId": "ModelBinding",
"descriptorName": "input"
},
{
"nameOnMethod": "input",
"name": "Version",
"jsonName": null,
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null,
"constraintTypes": [],
"bindingSourceId": "Path",
"descriptorName": ""
}
],
"returnValue": {
"type": "Volo.Docs.Projects.ProjectDto",
"typeSimple": "Volo.Docs.Projects.ProjectDto"
},
"allowAnonymous": null,
"implementFrom": "Volo.Docs.Projects.IProjectAppService"
},
"GetDefaultLanguageCodeAsyncByShortNameAndVersion": {
"uniqueName": "GetDefaultLanguageCodeAsyncByShortNameAndVersion",
"name": "GetDefaultLanguageCodeAsync",
"httpMethod": "GET",
"url": "api/docs/projects/{shortName}/defaultLanguage",
"supportedVersions": [],
"parametersOnMethod": [
"constraintTypes": null,
"bindingSourceId": "ModelBinding",
"descriptorName": "input"
},
{
"name": "shortName",
"typeAsString": "System.String, System.Private.CoreLib",
"nameOnMethod": "input",
"name": "LanguageCode",
"jsonName": null,
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null
"defaultValue": null,
"constraintTypes": null,
"bindingSourceId": "ModelBinding",
"descriptorName": "input"
},
{
"name": "version",
"typeAsString": "System.String, System.Private.CoreLib",
"nameOnMethod": "input",
"name": "ProjectName",
"jsonName": null,
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null
}
],
"parameters": [
"defaultValue": null,
"constraintTypes": null,
"bindingSourceId": "ModelBinding",
"descriptorName": "input"
},
{
"nameOnMethod": "shortName",
"name": "shortName",
"nameOnMethod": "input",
"name": "ProjectFormat",
"jsonName": null,
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null,
"constraintTypes": [],
"bindingSourceId": "Path",
"descriptorName": ""
"constraintTypes": null,
"bindingSourceId": "ModelBinding",
"descriptorName": "input"
},
{
"nameOnMethod": "version",
"name": "version",
"nameOnMethod": "input",
"name": "RouteVersion",
"jsonName": null,
"type": "System.String",
"typeSimple": "string",
@ -211,109 +100,99 @@
"defaultValue": null,
"constraintTypes": null,
"bindingSourceId": "ModelBinding",
"descriptorName": ""
"descriptorName": "input"
}
],
"returnValue": {
"type": "System.String",
"typeSimple": "string"
"type": "Volo.Docs.Documents.NavigationNode",
"typeSimple": "Volo.Docs.Documents.NavigationNode"
},
"allowAnonymous": null,
"implementFrom": "Volo.Docs.Projects.IProjectAppService"
},
"GetVersionsAsyncByShortName": {
"uniqueName": "GetVersionsAsyncByShortName",
"name": "GetVersionsAsync",
"implementFrom": "Volo.Docs.Areas.Documents.DocumentNavigationController"
}
}
},
"Volo.Docs.Areas.Documents.DocumentResourceController": {
"controllerName": "DocumentResource",
"controllerGroupName": "DocumentResource",
"isRemoteService": true,
"isIntegrationService": false,
"apiVersion": null,
"type": "Volo.Docs.Areas.Documents.DocumentResourceController",
"interfaces": [],
"actions": {
"GetResourceByInput": {
"uniqueName": "GetResourceByInput",
"name": "GetResource",
"httpMethod": "GET",
"url": "api/docs/projects/{shortName}/versions",
"url": "document-resources",
"supportedVersions": [],
"parametersOnMethod": [
{
"name": "shortName",
"typeAsString": "System.String, System.Private.CoreLib",
"type": "System.String",
"typeSimple": "string",
"name": "input",
"typeAsString": "Volo.Docs.Documents.GetDocumentResourceInput, Volo.Docs.Application.Contracts",
"type": "Volo.Docs.Documents.GetDocumentResourceInput",
"typeSimple": "Volo.Docs.Documents.GetDocumentResourceInput",
"isOptional": false,
"defaultValue": null
}
],
"parameters": [
{
"nameOnMethod": "shortName",
"name": "shortName",
"nameOnMethod": "input",
"name": "ProjectId",
"jsonName": null,
"type": "System.String",
"type": "System.Guid",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null,
"constraintTypes": [],
"bindingSourceId": "Path",
"descriptorName": ""
}
],
"returnValue": {
"type": "Volo.Abp.Application.Dtos.ListResultDto<Volo.Docs.Projects.VersionInfoDto>",
"typeSimple": "Volo.Abp.Application.Dtos.ListResultDto<Volo.Docs.Projects.VersionInfoDto>"
},
"allowAnonymous": null,
"implementFrom": "Volo.Docs.Projects.IProjectAppService"
},
"GetLanguageListAsyncByShortNameAndVersion": {
"uniqueName": "GetLanguageListAsyncByShortNameAndVersion",
"name": "GetLanguageListAsync",
"httpMethod": "GET",
"url": "api/docs/projects/{shortName}/{version}/languageList",
"supportedVersions": [],
"parametersOnMethod": [
{
"name": "shortName",
"typeAsString": "System.String, System.Private.CoreLib",
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null
"constraintTypes": null,
"bindingSourceId": "ModelBinding",
"descriptorName": "input"
},
{
"name": "version",
"typeAsString": "System.String, System.Private.CoreLib",
"nameOnMethod": "input",
"name": "Name",
"jsonName": null,
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null
}
],
"parameters": [
"defaultValue": null,
"constraintTypes": null,
"bindingSourceId": "ModelBinding",
"descriptorName": "input"
},
{
"nameOnMethod": "shortName",
"name": "shortName",
"nameOnMethod": "input",
"name": "Version",
"jsonName": null,
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null,
"constraintTypes": [],
"bindingSourceId": "Path",
"descriptorName": ""
"constraintTypes": null,
"bindingSourceId": "ModelBinding",
"descriptorName": "input"
},
{
"nameOnMethod": "version",
"name": "version",
"nameOnMethod": "input",
"name": "LanguageCode",
"jsonName": null,
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null,
"constraintTypes": [],
"bindingSourceId": "Path",
"descriptorName": ""
"constraintTypes": null,
"bindingSourceId": "ModelBinding",
"descriptorName": "input"
}
],
"returnValue": {
"type": "Volo.Docs.Documents.LanguageConfig",
"typeSimple": "Volo.Docs.Documents.LanguageConfig"
"type": "Microsoft.AspNetCore.Mvc.FileResult",
"typeSimple": "Microsoft.AspNetCore.Mvc.FileResult"
},
"allowAnonymous": null,
"implementFrom": "Volo.Docs.Projects.IProjectAppService"
"implementFrom": "Volo.Docs.Areas.Documents.DocumentResourceController"
}
}
},

2
modules/docs/src/Volo.Docs.Web/HtmlConverting/IDocumentToHtmlConverter.cs

@ -1,5 +1,5 @@
using Volo.Docs.Common.Projects;
using Volo.Docs.Documents;
using Volo.Docs.Projects;
namespace Volo.Docs.HtmlConverting
{

6
modules/docs/src/Volo.Docs.Web/Markdown/MarkdownDocumentToHtmlConverter.cs

@ -1,15 +1,11 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Docs.Common.Projects;
using Volo.Docs.Documents;
using Volo.Docs.HtmlConverting;
using Volo.Docs.Pages.Documents.Project;
using Volo.Docs.Projects;
using Volo.Docs.Utils;
namespace Volo.Docs.Markdown

2
modules/docs/src/Volo.Docs.Web/Pages/Documents/Index.cshtml.cs

@ -4,7 +4,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Options;
using Volo.Docs.Projects;
using Volo.Docs.Common.Projects;
namespace Volo.Docs.Pages.Documents
{

9
modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml

@ -71,6 +71,7 @@
<abp-script type="@typeof(PrismjsScriptBundleContributor)"/>
<abp-script type="@typeof(PopperJsScriptBundleContributor)"/>
<abp-script src="/client-proxies/docs-proxy.js"/>
<abp-script src="/client-proxies/docs-common-proxy.js"/>
<abp-script src="/Pages/Documents/Project/bootstrap-toc.js"/>
<abp-script src="/Pages/Documents/Shared/Scripts/vs.js"/>
<abp-script src="/Pages/Documents/Project/index.js"/>
@ -290,6 +291,14 @@
id="sidebar-scroll"
class="nav nav-list">
</ul>
@if (Model.HasDownloadPdfPermission)
{
<div class="docs-sidebar-footer">
<a href="@Url.Content($"~/api/docs/documents/pdf?ProjectId={Model.Project.Id}&Version={(Model.LatestVersionInfo == null || Model.LatestVersionInfo.IsSelected ? DocsAppConsts.Latest : Model.Version)}&LanguageCode={Model.LanguageCode}")" target="_blank" class="download-pdf-btn">
<i class="fa fa-download"></i> @L["DownloadPDF"]
</a>
</div>
}
}
</div>
</div>

15
modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml.cs

@ -7,6 +7,7 @@ using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
@ -15,9 +16,12 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Volo.Abp.Application.Dtos;
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities;
using Volo.Abp.EventBus.Local;
using Volo.Docs.Common;
using Volo.Docs.Common.Projects;
using Volo.Docs.Documents;
using Volo.Docs.Documents.Rendering;
using Volo.Docs.HtmlConverting;
@ -90,6 +94,8 @@ namespace Volo.Docs.Pages.Documents.Project
public bool FullSearchEnabled { get; set; }
public bool IsLatestVersion { get; private set; }
public bool HasDownloadPdfPermission { get; set; }
public DocumentNavigationsDto DocumentNavigationsDto { get; private set; }
@ -99,6 +105,7 @@ namespace Volo.Docs.Pages.Documents.Project
private readonly IProjectAppService _projectAppService;
private readonly IWebDocumentSectionRenderer _webDocumentSectionRenderer;
private readonly DocsUiOptions _uiOptions;
private readonly IPermissionChecker _permissionChecker;
protected IDocsLinkGenerator DocsLinkGenerator => LazyServiceProvider.LazyGetRequiredService<IDocsLinkGenerator>();
@ -109,7 +116,8 @@ namespace Volo.Docs.Pages.Documents.Project
IDocumentToHtmlConverterFactory documentToHtmlConverterFactory,
IProjectAppService projectAppService,
IOptions<DocsUiOptions> options,
IWebDocumentSectionRenderer webDocumentSectionRenderer)
IWebDocumentSectionRenderer webDocumentSectionRenderer,
IPermissionChecker permissionChecker)
{
ObjectMapperContext = typeof(DocsWebModule);
@ -117,6 +125,7 @@ namespace Volo.Docs.Pages.Documents.Project
_documentToHtmlConverterFactory = documentToHtmlConverterFactory;
_projectAppService = projectAppService;
_webDocumentSectionRenderer = webDocumentSectionRenderer;
_permissionChecker = permissionChecker;
_uiOptions = options.Value;
LocalizationResourceType = typeof(DocsResource);
@ -136,7 +145,7 @@ namespace Volo.Docs.Pages.Documents.Project
{
return RedirectPermanent(redirectUrl);
}
return await SetPageAsync();
}
@ -146,7 +155,7 @@ namespace Volo.Docs.Pages.Documents.Project
ShowProjectsCombobox = _uiOptions.ShowProjectsCombobox && !_uiOptions.SingleProjectMode.Enable;
ShowProjectsComboboxLabel = ShowProjectsCombobox && _uiOptions.ShowProjectsComboboxLabel;
FullSearchEnabled = await _documentAppService.FullSearchEnabledAsync();
HasDownloadPdfPermission = await _permissionChecker.IsGrantedAsync(DocsCommonPermissions.Documents.PdfGeneration);
try
{
await SetProjectAsync();

14
modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/index.css

@ -45,4 +45,18 @@ body {
outline: none;
}
.docs-sidebar-footer {
border-block-start: 1px solid #aaa;
border-block-end: 1px solid #aaa;
}
.download-pdf-btn {
text-align: left;
width: 100%;
color: #aaa;
background-color: #0000;
border-color:#0000;
font-size: 14px;
}
/*# sourceMappingURL=Index.css.map */

53
modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/index.scss

@ -6,9 +6,9 @@
}
}
.input-group{
.input-group-text{
background-color: transparent!important;
.input-group {
.input-group-text {
background-color: transparent !important;
}
}
@ -16,10 +16,12 @@
.next-link, .prev-link {
.desc {
color: #0D6EFD;
.fa{
.fa {
font-size: 12px;
}
}
.title {
font-weight: 600;
font-size: 18px;
@ -27,3 +29,46 @@
}
}
}
body {
top: 0px !important;
}
.skiptranslate {
iframe {
display: none !important;
}
}
.google_translate_text {
color: #aaa;
font-family: var(--bs-body-font-family);
font-size: 10pt;
margin-left: 2px;
font-weight: 500;
}
.goog-te-gadget {
white-space: break-spaces !important;
}
.goog-te-combo {
&:focus-visible {
border: 0px;
outline: none;
}
}
.docs-sidebar-footer {
border-block-start: 1px solid #aaa;
border-block-end: 1px solid #aaa;
}
.download-pdf-btn {
text-align: left;
width: 100%;
color: #aaa;
background-color: transparent;
border-color: transparent;
font-size: 14px;
}

5
modules/docs/src/Volo.Docs.Web/Pages/Documents/Search.cshtml.cs

@ -3,18 +3,15 @@ using System.Collections.Generic;
using System.Linq;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using System.Web;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination;
using Volo.Abp.Data;
using Volo.Docs.Common.Projects;
using Volo.Docs.Documents;
using Volo.Docs.GitHub.Documents.Version;
using Volo.Docs.HtmlConverting;
using Volo.Docs.Models;
using Volo.Docs.Projects;
using Volo.Docs.Utils;
namespace Volo.Docs.Pages.Documents
{

68
modules/docs/src/Volo.Docs.Web/wwwroot/client-proxies/docs-common-proxy.js

@ -0,0 +1,68 @@
/* This file is automatically generated by ABP framework to use MVC Controllers from javascript. */
// module docs-common
(function(){
// controller volo.docs.documents.docsDocumentPdfGenerator
(function(){
abp.utils.createNamespace(window, 'volo.docs.documents.docsDocumentPdfGenerator');
volo.docs.documents.docsDocumentPdfGenerator.generatePdf = function(input, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/docs/documents/pdf' + abp.utils.buildQueryString([{ name: 'projectId', value: input.projectId }, { name: 'version', value: input.version }, { name: 'languageCode', value: input.languageCode }]) + '',
type: 'GET'
}, ajaxParams));
};
})();
// controller volo.docs.projects.docsProject
(function(){
abp.utils.createNamespace(window, 'volo.docs.projects.docsProject');
volo.docs.projects.docsProject.getList = function(ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/docs/projects',
type: 'GET'
}, ajaxParams));
};
volo.docs.projects.docsProject.get = function(shortName, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/docs/projects/' + shortName + '',
type: 'GET'
}, ajaxParams));
};
volo.docs.projects.docsProject.getDefaultLanguageCode = function(shortName, version, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/docs/projects/' + shortName + '/defaultLanguage' + abp.utils.buildQueryString([{ name: 'version', value: version }]) + '',
type: 'GET'
}, { dataType: 'text' }, ajaxParams));
};
volo.docs.projects.docsProject.getVersions = function(shortName, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/docs/projects/' + shortName + '/versions',
type: 'GET'
}, ajaxParams));
};
volo.docs.projects.docsProject.getLanguageList = function(shortName, version, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/docs/projects/' + shortName + '/' + version + '/languageList',
type: 'GET'
}, ajaxParams));
};
})();
})();

35
modules/docs/src/Volo.Docs.Web/wwwroot/client-proxies/docs-proxy.js

@ -5,43 +5,30 @@
(function(){
// controller volo.docs.projects.docsProject
// controller volo.docs.areas.documents.documentNavigation
(function(){
abp.utils.createNamespace(window, 'volo.docs.projects.docsProject');
abp.utils.createNamespace(window, 'volo.docs.areas.documents.documentNavigation');
volo.docs.projects.docsProject.getList = function(ajaxParams) {
volo.docs.areas.documents.documentNavigation.getNavigation = function(input, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/docs/projects',
url: abp.appPath + 'docs/document-navigation' + abp.utils.buildQueryString([{ name: 'projectId', value: input.projectId }, { name: 'version', value: input.version }, { name: 'languageCode', value: input.languageCode }, { name: 'projectName', value: input.projectName }, { name: 'projectFormat', value: input.projectFormat }, { name: 'routeVersion', value: input.routeVersion }]) + '',
type: 'GET'
}, ajaxParams));
};
volo.docs.projects.docsProject.get = function(shortName, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/docs/projects/' + shortName + '',
type: 'GET'
}, ajaxParams));
};
})();
volo.docs.projects.docsProject.getDefaultLanguageCode = function(shortName, version, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/docs/projects/' + shortName + '/defaultLanguage' + abp.utils.buildQueryString([{ name: 'version', value: version }]) + '',
type: 'GET'
}, { dataType: 'text' }, ajaxParams));
};
// controller volo.docs.areas.documents.documentResource
volo.docs.projects.docsProject.getVersions = function(shortName, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/docs/projects/' + shortName + '/versions',
type: 'GET'
}, ajaxParams));
};
(function(){
abp.utils.createNamespace(window, 'volo.docs.areas.documents.documentResource');
volo.docs.projects.docsProject.getLanguageList = function(shortName, version, ajaxParams) {
volo.docs.areas.documents.documentResource.getResource = function(input, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/docs/projects/' + shortName + '/' + version + '/languageList',
url: abp.appPath + 'document-resources' + abp.utils.buildQueryString([{ name: 'projectId', value: input.projectId }, { name: 'name', value: input.name }, { name: 'version', value: input.version }, { name: 'languageCode', value: input.languageCode }]) + '',
type: 'GET'
}, ajaxParams));
};

2
modules/docs/test/Volo.Docs.Application.Tests/Volo/Docs/ApplicationService_Tests.cs

@ -1,6 +1,6 @@
using System.Threading.Tasks;
using Shouldly;
using Volo.Docs.Projects;
using Volo.Docs.Common.Projects;
using Xunit;
namespace Volo.Docs

Loading…
Cancel
Save