Browse Source

Abstract PDF Generation and add Manage PDF files feature

pull/22430/head
liangshiwei 9 months ago
parent
commit
056502c0fd
  1. 1376
      modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20250425082043_add_pdf_file.Designer.cs
  2. 44
      modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20250425082043_add_pdf_file.cs
  3. 41
      modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/VoloDocsDbContextModelSnapshot.cs
  4. 4
      modules/docs/app/VoloDocs.Web/VoloDocsWebModule.cs
  5. 1
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/DocsAdminPermissionDefinitionProvider.cs
  6. 1
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/DocsAdminPermissions.cs
  7. 5
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/ar.json
  8. 5
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/cs.json
  9. 5
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/de-DE.json
  10. 5
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/de.json
  11. 5
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/el.json
  12. 5
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/en-GB.json
  13. 5
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/en.json
  14. 5
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/es.json
  15. 5
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/fi.json
  16. 5
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/fr.json
  17. 5
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/hi.json
  18. 5
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/hr.json
  19. 5
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/hu.json
  20. 5
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/is.json
  21. 5
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/it.json
  22. 5
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/nl.json
  23. 5
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/pl-PL.json
  24. 5
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/pt-BR.json
  25. 5
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/ro-RO.json
  26. 5
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/ru.json
  27. 5
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/sk.json
  28. 5
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/sl.json
  29. 5
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/sv.json
  30. 5
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/tr.json
  31. 5
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/vi.json
  32. 5
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/zh-Hans.json
  33. 5
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/zh-Hant.json
  34. 9
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Projects/GetPdfFilesInput.cs
  35. 2
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Projects/IProjectAdminAppService.cs
  36. 14
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Projects/ProjectPdfFileDto.cs
  37. 1
      modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/DocsAdminApplicationAutoMapperProfile.cs
  38. 32
      modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Projects/ProjectAdminAppService.cs
  39. 8
      modules/docs/src/Volo.Docs.Admin.HttpApi.Client/ClientProxies/Volo/Docs/Admin/ProjectsAdminClientProxy.Generated.cs
  40. 90
      modules/docs/src/Volo.Docs.Admin.HttpApi.Client/ClientProxies/docs-admin-generate-proxy.json
  41. 7
      modules/docs/src/Volo.Docs.Admin.HttpApi/Volo/Docs/Admin/ProjectsAdminController.cs
  42. 2
      modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/GeneratePdf.cshtml
  43. 1
      modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/Index.cshtml
  44. 41
      modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/ManagePdfFiles.cshtml
  45. 14
      modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/ManagePdfFiles.cshtml.cs
  46. 54
      modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/generatePdf.js
  47. 12
      modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/index.js
  48. 94
      modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/managePdfFiles.js
  49. 7
      modules/docs/src/Volo.Docs.Admin.Web/wwwroot/client-proxies/docs-admin-proxy.js
  50. 2
      modules/docs/src/Volo.Docs.Common.Application.Contracts/Volo/Docs/Common/DocsCommonPermissionDefinitionProvider.cs
  51. 2
      modules/docs/src/Volo.Docs.Common.Application.Contracts/Volo/Docs/Common/DocsCommonPermissions.cs
  52. 15
      modules/docs/src/Volo.Docs.Common.Application/Volo/Docs/Common/Documents/DocumentPdfGeneratorAppService.cs
  53. 4
      modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/Documents/DocumentParams.cs
  54. 4
      modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/HtmlConverting/DocumentToHtmlConverterFactory.cs
  55. 14
      modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/HtmlConverting/DocumentToHtmlConverterOptions.cs
  56. 9
      modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/HtmlConverting/IDocumentToHtmlConverter.cs
  57. 6
      modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/HtmlConverting/IDocumentToHtmlConverterFactory.cs
  58. 1
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/DocsDomainConsts.cs
  59. 8
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/DocsDomainModule.cs
  60. 50
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Pdf/BlobDocumentPdfFileStore.cs
  61. 11
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Pdf/DocsDocumentPdfCacheItem.cs
  62. 10
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Pdf/IPdfRenderer.cs
  63. 2
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/IProjectRepository.cs
  64. 64
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/BlobProjectPdfFileStore.cs
  65. 4
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/DocsProjectPdfContainer.cs
  66. 7
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/DocsProjectPdfGeneratorOptions.cs
  67. 10
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/IHtmlToPdfRenderer.cs
  68. 5
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/IProjectPdfFileStore.cs
  69. 5
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/IProjectPdfGenerator.cs
  70. 7
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/IText/HtmlIdTagWorkerFactory.cs
  71. 26
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/IText/ITextHtmlToPdfRenderer.cs
  72. 13
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/Markdown/AnchorLinkRenderer.cs
  73. 10
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/Markdown/AnchorLinkResolverExtension.cs
  74. 73
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/Markdown/MarkdigPdfDocumentToHtmlConverter.cs
  75. 7
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/PdfDocument.cs
  76. 13
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/PdfDocumentToHtmlConverterContext.cs
  77. 292
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/ProjectPdfGenerator.cs
  78. 23
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Project.cs
  79. 38
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/ProjectPdfFile.cs
  80. 2
      modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/EntityFrameworkCore/DocsDbContext.cs
  81. 15
      modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/EntityFrameworkCore/DocsDbContextModelBuilderExtensions.cs
  82. 6
      modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/EntityFrameworkCore/DocsEfCoreQueryableExtensions.cs
  83. 2
      modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/EntityFrameworkCore/IDocsDbContext.cs
  84. 14
      modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/Projects/EfCoreProjectRepository.cs
  85. 7
      modules/docs/src/Volo.Docs.MongoDB/Volo/Docs/Projects/MongoProjectRepository.cs
  86. 26
      modules/docs/src/Volo.Docs.Web/HtmlConverting/DocumentToHtmlConverterContext.cs
  87. 15
      modules/docs/src/Volo.Docs.Web/HtmlConverting/DocumentToHtmlConverterOptions.cs
  88. 10
      modules/docs/src/Volo.Docs.Web/HtmlConverting/IDocumentToHtmlConverter.cs
  89. 7
      modules/docs/src/Volo.Docs.Web/HtmlConverting/IDocumentToHtmlConverterFactory.cs
  90. 19
      modules/docs/src/Volo.Docs.Web/Markdown/MarkdownDocumentToHtmlConverter.cs
  91. 32
      modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml.cs

1376
modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20250425082043_add_pdf_file.Designer.cs

File diff suppressed because it is too large

44
modules/docs/app/VoloDocs.EntityFrameworkCore/Migrations/20250425082043_add_pdf_file.cs

@ -0,0 +1,44 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Migrations
{
/// <inheritdoc />
public partial class add_pdf_file : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "DocsProjectPdfFiles",
columns: table => new
{
ProjectId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
FileName = table.Column<string>(type: "nvarchar(450)", nullable: false),
Version = table.Column<string>(type: "nvarchar(max)", nullable: true),
LanguageCode = table.Column<string>(type: "nvarchar(max)", nullable: true),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_DocsProjectPdfFiles", x => new { x.ProjectId, x.FileName });
table.ForeignKey(
name: "FK_DocsProjectPdfFiles_DocsProjects_ProjectId",
column: x => x.ProjectId,
principalTable: "DocsProjects",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "DocsProjectPdfFiles");
}
}
}

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

@ -1193,6 +1193,33 @@ namespace Migrations
b.ToTable("DocsProjects", (string)null);
});
modelBuilder.Entity("Volo.Docs.Projects.ProjectPdfFile", b =>
{
b.Property<Guid>("ProjectId")
.HasColumnType("uniqueidentifier");
b.Property<string>("FileName")
.HasColumnType("nvarchar(450)");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<string>("LanguageCode")
.HasColumnType("nvarchar(max)");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<string>("Version")
.HasColumnType("nvarchar(max)");
b.HasKey("ProjectId", "FileName");
b.ToTable("DocsProjectPdfFiles", (string)null);
});
modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlob", b =>
{
b.HasOne("Volo.Abp.BlobStoring.Database.DatabaseBlobContainer", null)
@ -1299,6 +1326,15 @@ namespace Migrations
.IsRequired();
});
modelBuilder.Entity("Volo.Docs.Projects.ProjectPdfFile", b =>
{
b.HasOne("Volo.Docs.Projects.Project", null)
.WithMany("PdfFiles")
.HasForeignKey("ProjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b =>
{
b.Navigation("Claims");
@ -1326,6 +1362,11 @@ namespace Migrations
{
b.Navigation("Contributors");
});
modelBuilder.Entity("Volo.Docs.Projects.Project", b =>
{
b.Navigation("PdfFiles");
});
#pragma warning restore 612, 618
}
}

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

@ -39,7 +39,7 @@ 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;
using Volo.Docs.Projects.Pdf;
namespace VoloDocs.Web
{
@ -175,7 +175,7 @@ namespace VoloDocs.Web
options.GoogleSearchEngineId = "77c7266532da1427f";
});
Configure<DocsDocumentPdfGeneratorOptions>(options =>
Configure<DocsProjectPdfGeneratorOptions>(options =>
{
options.BaseUrl = configuration["App:selfUrl"];
options.IndexPagePath = "Index.md";

1
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/DocsAdminPermissionDefinitionProvider.cs

@ -14,6 +14,7 @@ namespace Volo.Docs.Admin
projects.AddChild(DocsAdminPermissions.Projects.Update, L("Permission:Edit"));
projects.AddChild(DocsAdminPermissions.Projects.Delete, L("Permission:Delete"));
projects.AddChild(DocsAdminPermissions.Projects.Create, L("Permission:Create"));
projects.AddChild(DocsAdminPermissions.Projects.ManagePdfFiles, L("Permission:ManagePdfFiles"));
group.AddPermission(DocsAdminPermissions.Documents.Default, L("Permission:Documents"));
}

1
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/DocsAdminPermissions.cs

@ -12,6 +12,7 @@ namespace Volo.Docs.Admin
public const string Delete = Default + ".Delete";
public const string Update = Default + ".Update";
public const string Create = Default + ".Create";
public const string ManagePdfFiles = Default + ".ManagePdfFiles";
}
public static class Documents

5
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/ar.json

@ -63,6 +63,9 @@
"Language": "اللغة",
"ForceToGenerateNewPdf": "قوة لتوليد PDF جديد",
"PdfGeneratedSuccessfully": "تم توليد PDF بنجاح",
"GenerateAndDownloadPdf": "توليد وتحميل PDF"
"GenerateAndDownloadPdf": "توليد وتحميل PDF",
"PdfFileDeletionWarningMessage": "هل أنت متأكد أنك تريد حذف ملف PDF \"{0}\"?",
"ManagePdfFiles": "إدارة ملفات PDF",
"Permission:ManagePdfFiles": "إدارة ملفات PDF"
}
}

5
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/cs.json

@ -63,6 +63,9 @@
"Language": "Jazyk",
"ForceToGenerateNewPdf": "Vynutit generování nového PDF",
"PdfGeneratedSuccessfully": "PDF generován úspěšně",
"GenerateAndDownloadPdf": "Generovat a stáhnout PDF"
"GenerateAndDownloadPdf": "Generovat a stáhnout PDF",
"PdfFileDeletionWarningMessage": "Opravdu chcete odstranit soubor PDF \"{0}\"?",
"ManagePdfFiles": "Správa PDF souborů",
"Permission:ManagePdfFiles": "Správa PDF souborů"
}
}

5
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/de-DE.json

@ -62,6 +62,9 @@
"Language": "Sprache",
"ForceToGenerateNewPdf": "Neues PDF erzwingen",
"PdfGeneratedSuccessfully": "PDF erfolgreich generiert",
"GenerateAndDownloadPdf": "PDF generieren und herunterladen"
"GenerateAndDownloadPdf": "PDF generieren und herunterladen",
"PdfFileDeletionWarningMessage": "Sind Sie sicher, dass Sie das PDF-Datei \"{0}\" löschen wollen?",
"ManagePdfFiles": "PDF-Dateien verwalten",
"Permission:ManagePdfFiles": "PDF-Dateien verwalten"
}
}

5
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/de.json

@ -63,6 +63,9 @@
"Language": "Sprache",
"ForceToGenerateNewPdf": "Neues PDF erzwingen",
"PdfGeneratedSuccessfully": "PDF erfolgreich generiert",
"GenerateAndDownloadPdf": "PDF generieren und herunterladen"
"GenerateAndDownloadPdf": "PDF generieren und herunterladen",
"PdfFileDeletionWarningMessage": "Sind Sie sicher, dass Sie das PDF-Datei \"{0}\" löschen wollen?",
"ManagePdfFiles": "PDF-Dateien verwalten",
"Permission:ManagePdfFiles": "PDF-Dateien verwalten"
}
}

5
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/el.json

@ -62,6 +62,9 @@
"Language": "Γλώσσα",
"ForceToGenerateNewPdf": "Βάλτε το νέο PDF",
"PdfGeneratedSuccessfully": "Το PDF αναπροσαρμοστεί εκ νέου",
"GenerateAndDownloadPdf": "Δημιουργία και λήψη PDF"
"GenerateAndDownloadPdf": "Δημιουργία και λήψη PDF",
"PdfFileDeletionWarningMessage": "Είστε βέβαιοι ότι θέλετε να διαγράψετε το PDF αρχείο \"{0}\";",
"ManagePdfFiles": "Διαχείρηση PDF αρχείων",
"Permission:ManagePdfFiles": "Διαχείρηση PDF αρχείων"
}
}

5
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/en-GB.json

@ -62,6 +62,9 @@
"Language": "Language",
"ForceToGenerateNewPdf": "Force to generate new PDF",
"PdfGeneratedSuccessfully": "PDF generated successfully",
"GenerateAndDownloadPdf": "Generate and download PDF"
"GenerateAndDownloadPdf": "Generate and download PDF",
"PdfFileDeletionWarningMessage": "Are you sure you want to delete the PDF file \"{0}\"?",
"ManagePdfFiles": "Manage PDF files",
"Permission:ManagePdfFiles": "Manage PDF files"
}
}

5
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/en.json

@ -63,6 +63,9 @@
"Language": "Language",
"ForceToGenerateNewPdf": "Force to generate new PDF",
"PdfGeneratedSuccessfully": "PDF generated successfully",
"GenerateAndDownloadPdf": "Generate and download PDF"
"GenerateAndDownloadPdf": "Generate and download PDF",
"PdfFileDeletionWarningMessage": "Are you sure you want to delete the PDF file \"{0}\"?",
"ManagePdfFiles": "Manage PDF files",
"Permission:ManagePdfFiles": "Manage PDF files"
}
}

5
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/es.json

@ -63,6 +63,9 @@
"Language": "Idioma",
"ForceToGenerateNewPdf": "Forzar a generar un nuevo PDF",
"PdfGeneratedSuccessfully": "PDF generado correctamente",
"GenerateAndDownloadPdf": "Generar y descargar PDF"
"GenerateAndDownloadPdf": "Generar y descargar PDF",
"PdfFileDeletionWarningMessage": "¿Está seguro de querer eliminar el archivo PDF \"{0}\"?",
"ManagePdfFiles": "Administrar archivos PDF",
"Permission:ManagePdfFiles": "Administrar archivos PDF"
}
}

5
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/fi.json

@ -63,6 +63,9 @@
"Language": "Kieli",
"ForceToGenerateNewPdf": "Vahvista uusi PDF",
"PdfGeneratedSuccessfully": "PDF generoidaan onnistuneesti",
"GenerateAndDownloadPdf": "Generoi ja lataa PDF"
"GenerateAndDownloadPdf": "Generoi ja lataa PDF",
"PdfFileDeletionWarningMessage": "Oletko varma, että haluat poistaa PDF-tiedoston \"{0}\"?",
"ManagePdfFiles": "Hallitse PDF-tiedostoja",
"Permission:ManagePdfFiles": "Hallitse PDF-tiedostoja"
}
}

5
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/fr.json

@ -63,6 +63,9 @@
"Language": "Langue",
"ForceToGenerateNewPdf": "Forcer à générer un nouveau PDF",
"PdfGeneratedSuccessfully": "PDF généré avec succès",
"GenerateAndDownloadPdf": "Générer et télécharger PDF"
"GenerateAndDownloadPdf": "Générer et télécharger PDF",
"PdfFileDeletionWarningMessage": "Êtes-vous sûr de vouloir supprimer le fichier PDF \"{0}\"?",
"ManagePdfFiles": "Gérer les fichiers PDF",
"Permission:ManagePdfFiles": "Gérer les fichiers PDF"
}
}

5
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/hi.json

@ -63,6 +63,9 @@
"Language": "भाषा",
"ForceToGenerateNewPdf": "नया PDF उत्पन्न करें",
"PdfGeneratedSuccessfully": "PDF सफलतापूर्वक उत्पन्न हो गया",
"GenerateAndDownloadPdf": "PDF उत्पन्न और डाउनलोड करें"
"GenerateAndDownloadPdf": "PDF उत्पन्न और डाउनलोड करें",
"PdfFileDeletionWarningMessage": "क्या आप वाकई \"{0}\" के PDF फ़ाइल को हटाना चाहते हैं?",
"ManagePdfFiles": "PDF फ़ाइलें प्रबंधित करें",
"Permission:ManagePdfFiles": "PDF फ़ाइलें प्रबंधित करें"
}
}

5
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/hr.json

@ -63,6 +63,9 @@
"Language": "Jezik",
"ForceToGenerateNewPdf": "Prisili generiranje novog PDF-a",
"PdfGeneratedSuccessfully": "PDF generiran uspješno",
"GenerateAndDownloadPdf": "Generiraj i preuzmi PDF"
"GenerateAndDownloadPdf": "Generiraj i preuzmi PDF",
"PdfFileDeletionWarningMessage": "Jeste li sigurni da želite izbrisati PDF datoteku \"{0}\"?",
"ManagePdfFiles": "Upravljanje PDF datotekama",
"Permission:ManagePdfFiles": "Upravljanje PDF datotekama"
}
}

5
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/hu.json

@ -64,6 +64,9 @@
"Language": "Nyelv",
"ForceToGenerateNewPdf": "Új PDF generálása",
"PdfGeneratedSuccessfully": "PDF generálás sikeres",
"GenerateAndDownloadPdf": "Generálás és letöltés PDF-ben"
"GenerateAndDownloadPdf": "Generálás és letöltés PDF-ben",
"PdfFileDeletionWarningMessage": "Biztosan törölni szeretné a \"{0}\" PDF fájlt?",
"ManagePdfFiles": "PDF fájlok kezelése",
"Permission:ManagePdfFiles": "PDF fájlok kezelése"
}
}

5
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/is.json

@ -63,6 +63,9 @@
"Language": "Language",
"ForceToGenerateNewPdf": "Force to generate new PDF",
"PdfGeneratedSuccessfully": "PDF generated successfully",
"GenerateAndDownloadPdf": "Generate and download PDF"
"GenerateAndDownloadPdf": "Generate and download PDF",
"PdfFileDeletionWarningMessage": "Are you sure you want to delete the PDF file \"{0}\"?",
"ManagePdfFiles": "Manage PDF files",
"Permission:ManagePdfFiles": "Manage PDF files"
}
}

5
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/it.json

@ -63,6 +63,9 @@
"Language": "Lingua",
"ForceToGenerateNewPdf": "Forza a generare un nuovo PDF",
"PdfGeneratedSuccessfully": "PDF generato con successo",
"GenerateAndDownloadPdf": "Genera e scarica PDF"
"GenerateAndDownloadPdf": "Genera e scarica PDF",
"PdfFileDeletionWarningMessage": "Sei sicuro di voler eliminare il file PDF \"{0}\"?",
"ManagePdfFiles": "Gestione file PDF",
"Permission:ManagePdfFiles": "Gestione file PDF"
}
}

5
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/nl.json

@ -63,6 +63,9 @@
"Language": "Taal",
"ForceToGenerateNewPdf": "Forceer nieuwe PDF te genereren",
"PdfGeneratedSuccessfully": "PDF succesvol gegenereerd",
"GenerateAndDownloadPdf": "PDF genereren en downloaden"
"GenerateAndDownloadPdf": "PDF genereren en downloaden",
"PdfFileDeletionWarningMessage": "Weet u zeker dat u het PDF-bestand \"{0}\" wilt verwijderen?",
"ManagePdfFiles": "PDF-bestanden beheren",
"Permission:ManagePdfFiles": "PDF-bestanden beheren"
}
}

5
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/pl-PL.json

@ -63,6 +63,9 @@
"Language": "Język",
"ForceToGenerateNewPdf": "Wymuś generowanie nowego PDF",
"PdfGeneratedSuccessfully": "PDF został pomyślnie wygenerowany",
"GenerateAndDownloadPdf": "Generuj i pobierz PDF"
"GenerateAndDownloadPdf": "Generuj i pobierz PDF",
"PdfFileDeletionWarningMessage": "Czy na pewno chcesz usunąć plik PDF \"{0}\"?",
"ManagePdfFiles": "Zarządzanie plikami PDF",
"Permission:ManagePdfFiles": "Zarządzanie plikami PDF"
}
}

5
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/pt-BR.json

@ -63,6 +63,9 @@
"Language": "Idioma",
"ForceToGenerateNewPdf": "Forçar para gerar um novo PDF",
"PdfGeneratedSuccessfully": "PDF gerado com sucesso",
"GenerateAndDownloadPdf": "Gerar e baixar PDF"
"GenerateAndDownloadPdf": "Gerar e baixar PDF",
"PdfFileDeletionWarningMessage": "Tem certeza de que deseja excluir o arquivo PDF \"{0}\"?",
"ManagePdfFiles": "Gerenciar arquivos PDF",
"Permission:ManagePdfFiles": "Gerenciar arquivos PDF"
}
}

5
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/ro-RO.json

@ -63,6 +63,9 @@
"Language": "Limba",
"ForceToGenerateNewPdf": "Forțați generarea unui nou PDF",
"PdfGeneratedSuccessfully": "PDF generat cu succes",
"GenerateAndDownloadPdf": "Generare și descărcare PDF"
"GenerateAndDownloadPdf": "Generare și descărcare PDF",
"PdfFileDeletionWarningMessage": "Sunteți sigur(ă) că doriți să ștergeți fișierul PDF \"{0}\"?",
"ManagePdfFiles": "Gestionare fișiere PDF",
"Permission:ManagePdfFiles": "Gestionare fișiere PDF"
}
}

5
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/ru.json

@ -63,6 +63,9 @@
"Language": "Язык",
"ForceToGenerateNewPdf": "Принудительное создание нового PDF",
"PdfGeneratedSuccessfully": "PDF успешно сгенерирован",
"GenerateAndDownloadPdf": "Сгенерировать и скачать PDF"
"GenerateAndDownloadPdf": "Сгенерировать и скачать PDF",
"PdfFileDeletionWarningMessage": "Вы уверены, что хотите удалить файл PDF \"{0}\"?",
"ManagePdfFiles": "Управление файлами PDF",
"Permission:ManagePdfFiles": "Управление файлами PDF"
}
}

5
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/sk.json

@ -63,6 +63,9 @@
"Language": "Jazyk",
"ForceToGenerateNewPdf": "Vynútiť generovanie nového PDF",
"PdfGeneratedSuccessfully": "PDF generovaný úspešne",
"GenerateAndDownloadPdf": "Generovať a stiahnuť PDF"
"GenerateAndDownloadPdf": "Generovať a stiahnuť PDF",
"PdfFileDeletionWarningMessage": "Ste si istý, že chcete odstrániť súbor PDF \"{0}\"?",
"ManagePdfFiles": "Správa PDF súborov",
"Permission:ManagePdfFiles": "Správa PDF súborov"
}
}

5
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/sl.json

@ -63,6 +63,9 @@
"Language": "Jezik",
"ForceToGenerateNewPdf": "Prisili generiranje novega PDF",
"PdfGeneratedSuccessfully": "PDF generiran uspešno",
"GenerateAndDownloadPdf": "Generiraj in prenesi PDF"
"GenerateAndDownloadPdf": "Generiraj in prenesi PDF",
"PdfFileDeletionWarningMessage": "Ali ste prepričani, da želite izbrisati datoteko PDF »{0}«?",
"ManagePdfFiles": "Upravljanje datotek PDF",
"Permission:ManagePdfFiles": "Upravljanje datotek PDF"
}
}

5
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/sv.json

@ -63,6 +63,9 @@
"Language": "Språk",
"ForceToGenerateNewPdf": "Force to generate new PDF",
"PdfGeneratedSuccessfully": "PDF generated successfully",
"GenerateAndDownloadPdf": "Generate and download PDF"
"GenerateAndDownloadPdf": "Generate and download PDF",
"PdfFileDeletionWarningMessage": "Are you sure you want to delete the PDF file \"{0}\"?",
"ManagePdfFiles": "Manage PDF files",
"Permission:ManagePdfFiles": "Manage PDF files"
}
}

5
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/tr.json

@ -63,6 +63,9 @@
"Language": "Dil",
"ForceToGenerateNewPdf": "Yeni bir PDF oluştur",
"PdfGeneratedSuccessfully": "PDF başarıyla oluşturuldu",
"GenerateAndDownloadPdf": "PDF oluştur ve indir"
"GenerateAndDownloadPdf": "PDF oluştur ve indir",
"PdfFileDeletionWarningMessage": "{0} PDF dosyasını silmek istediğinizden emin misiniz?",
"ManagePdfFiles": "PDF dosyalarını yönet",
"Permission:ManagePdfFiles": "PDF dosyalarını yönet"
}
}

5
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/vi.json

@ -63,6 +63,9 @@
"Language": "Ngôn ngữ",
"ForceToGenerateNewPdf": "Tạo PDF mới",
"PdfGeneratedSuccessfully": "PDF đã được tạo thành công",
"GenerateAndDownloadPdf": "Tạo và tải xuống PDF"
"GenerateAndDownloadPdf": "Tạo và tải xuống PDF",
"PdfFileDeletionWarningMessage": "Bạn có chắc chắn muốn xóa tệp PDF \"{0}\" không?",
"ManagePdfFiles": "Quản lý tệp PDF",
"Permission:ManagePdfFiles": "Quản lý tệp PDF"
}
}

5
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/zh-Hans.json

@ -63,6 +63,9 @@
"Language": "语言",
"ForceToGenerateNewPdf": "强制生成新PDF",
"PdfGeneratedSuccessfully": "PDF生成成功",
"GenerateAndDownloadPdf": "生成并下载PDF"
"GenerateAndDownloadPdf": "生成并下载PDF",
"PdfFileDeletionWarningMessage": "你确定要删除PDF文件“{0}”吗?",
"ManagePdfFiles": "管理PDF文件",
"Permission:ManagePdfFiles": "管理PDF文件"
}
}

5
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Localization/Resources/Docs/ApplicationContracts/zh-Hant.json

@ -63,6 +63,9 @@
"Language": "語言",
"ForceToGenerateNewPdf": "强制生成新PDF",
"PdfGeneratedSuccessfully": "PDF生成成功",
"GenerateAndDownloadPdf": "生成并下载PDF"
"GenerateAndDownloadPdf": "生成并下载PDF",
"PdfFileDeletionWarningMessage": "你確定要刪除PDF文件“{0}”嗎?",
"ManagePdfFiles": "管理PDF文件",
"Permission:ManagePdfFiles": "管理PDF文件"
}
}

9
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Projects/GetPdfFilesInput.cs

@ -0,0 +1,9 @@
using System;
using Volo.Abp.Application.Dtos;
namespace Volo.Docs.Admin.Projects;
public class GetPdfFilesInput : PagedAndSortedResultRequestDto
{
public Guid ProjectId { get; set; }
}

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

@ -23,6 +23,8 @@ namespace Volo.Docs.Admin.Projects
Task ReindexAllAsync();
Task<List<ProjectWithoutDetailsDto>> GetListWithoutDetailsAsync();
Task<PagedResultDto<ProjectPdfFileDto>> GetPdfFilesAsync(GetPdfFilesInput input);
Task DeletePdfFileAsync(DeletePdfFileInput input);
}
}

14
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/Projects/ProjectPdfFileDto.cs

@ -0,0 +1,14 @@
using System;
namespace Volo.Docs.Admin.Projects;
[Serializable]
public class ProjectPdfFileDto
{
public virtual Guid ProjectId { get; set; }
public virtual string FileName { get; set; }
public virtual string Version { get; set; }
public virtual string LanguageCode { get; set; }
public virtual DateTime CreationTime { get; set; }
public virtual DateTime? LastModificationTime { get; set; }
}

1
modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/DocsAdminApplicationAutoMapperProfile.cs

@ -16,6 +16,7 @@ namespace Volo.Docs.Admin
CreateMap<DocumentWithoutContent, DocumentDto>();
CreateMap<ProjectWithoutDetails, ProjectWithoutDetailsDto>();
CreateMap<DocumentInfo, DocumentInfoDto>();
CreateMap<ProjectPdfFile, ProjectPdfFileDto>();
}
}
}

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

@ -3,17 +3,16 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Options;
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;
using Volo.Docs.Projects.Pdf;
namespace Volo.Docs.Admin.Projects
{
@ -24,14 +23,14 @@ namespace Volo.Docs.Admin.Projects
private readonly IDocumentRepository _documentRepository;
private readonly IDocumentFullSearch _elasticSearchService;
private readonly IGuidGenerator _guidGenerator;
private readonly IDistributedCache<DocsDocumentPdfCacheItem> _documentPdfCache;
private readonly IOptions<DocsProjectPdfGeneratorOptions> _pdfGeneratorOptions;
public ProjectAdminAppService(
IProjectRepository projectRepository,
IDocumentRepository documentRepository,
IDocumentFullSearch elasticSearchService,
IGuidGenerator guidGenerator,
IDistributedCache<DocsDocumentPdfCacheItem> documentPdfCache)
IGuidGenerator guidGenerator,
IOptions<DocsProjectPdfGeneratorOptions> pdfGeneratorOptions)
{
ObjectMapperContext = typeof(DocsAdminApplicationModule);
LocalizationResource = typeof(DocsResource);
@ -40,7 +39,7 @@ namespace Volo.Docs.Admin.Projects
_documentRepository = documentRepository;
_elasticSearchService = elasticSearchService;
_guidGenerator = guidGenerator;
_documentPdfCache = documentPdfCache;
_pdfGeneratorOptions = pdfGeneratorOptions;
}
public virtual async Task<PagedResultDto<ProjectDto>> GetListAsync(PagedAndSortedResultRequestDto input)
@ -183,9 +182,26 @@ namespace Volo.Docs.Admin.Projects
return ObjectMapper.Map<List<ProjectWithoutDetails>, List<ProjectWithoutDetailsDto>>(projects);
}
[Authorize(DocsAdminPermissions.Projects.ManagePdfFiles)]
public virtual async Task<PagedResultDto<ProjectPdfFileDto>> GetPdfFilesAsync(GetPdfFilesInput input)
{
var project = await _projectRepository.GetAsync(input.ProjectId, includeDetails: true);
var pdfFiles = project.PdfFiles.Skip(input.SkipCount).Take(input.MaxResultCount).ToList();
return new PagedResultDto<ProjectPdfFileDto>(
project.PdfFiles.Count,
ObjectMapper.Map<List<ProjectPdfFile>, List<ProjectPdfFileDto>>(pdfFiles)
);
}
[Authorize(DocsAdminPermissions.Projects.ManagePdfFiles)]
public virtual async Task DeletePdfFileAsync(DeletePdfFileInput input)
{
await _documentPdfCache.RemoveAsync(DocsDocumentPdfCacheItem.CalculateCacheKey(input.ProjectId, input.Version, input.LanguageCode));
var project = await _projectRepository.GetAsync(input.ProjectId, includeDetails: true);
project.RemovePdfFile(_pdfGeneratorOptions.Value.CalculatePdfFileName(project, input.Version, input.LanguageCode));
await _projectRepository.UpdateAsync(project);
}
}
}

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<PagedResultDto<ProjectPdfFileDto>> GetPdfFilesAsync(GetPdfFilesInput input)
{
return await RequestAsync<PagedResultDto<ProjectPdfFileDto>>(nameof(GetPdfFilesAsync), new ClientProxyRequestTypeValue
{
{ typeof(GetPdfFilesInput), input }
});
}
public virtual async Task DeletePdfFileAsync(DeletePdfFileInput input)
{
await RequestAsync(nameof(DeletePdfFileAsync), new ClientProxyRequestTypeValue

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

@ -722,6 +722,23 @@
"typeSimple": "[Volo.Docs.Admin.Projects.ProjectWithoutDetailsDto]"
}
},
{
"name": "GetPdfFilesAsync",
"parametersOnMethod": [
{
"name": "input",
"typeAsString": "Volo.Docs.Admin.Projects.GetPdfFilesInput, Volo.Docs.Admin.Application.Contracts",
"type": "Volo.Docs.Admin.Projects.GetPdfFilesInput",
"typeSimple": "Volo.Docs.Admin.Projects.GetPdfFilesInput",
"isOptional": false,
"defaultValue": null
}
],
"returnValue": {
"type": "Volo.Abp.Application.Dtos.PagedResultDto<Volo.Docs.Admin.Projects.ProjectPdfFileDto>",
"typeSimple": "Volo.Abp.Application.Dtos.PagedResultDto<Volo.Docs.Admin.Projects.ProjectPdfFileDto>"
}
},
{
"name": "DeletePdfFileAsync",
"parametersOnMethod": [
@ -1002,6 +1019,79 @@
"allowAnonymous": null,
"implementFrom": "Volo.Docs.Admin.Projects.IProjectAdminAppService"
},
"GetPdfFilesAsyncByInput": {
"uniqueName": "GetPdfFilesAsyncByInput",
"name": "GetPdfFilesAsync",
"httpMethod": "GET",
"url": "api/docs/admin/projects/PdfFiles",
"supportedVersions": [],
"parametersOnMethod": [
{
"name": "input",
"typeAsString": "Volo.Docs.Admin.Projects.GetPdfFilesInput, Volo.Docs.Admin.Application.Contracts",
"type": "Volo.Docs.Admin.Projects.GetPdfFilesInput",
"typeSimple": "Volo.Docs.Admin.Projects.GetPdfFilesInput",
"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": "Sorting",
"jsonName": null,
"type": "System.String",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null,
"constraintTypes": null,
"bindingSourceId": "ModelBinding",
"descriptorName": "input"
},
{
"nameOnMethod": "input",
"name": "SkipCount",
"jsonName": null,
"type": "System.Int32",
"typeSimple": "number",
"isOptional": false,
"defaultValue": null,
"constraintTypes": null,
"bindingSourceId": "ModelBinding",
"descriptorName": "input"
},
{
"nameOnMethod": "input",
"name": "MaxResultCount",
"jsonName": null,
"type": "System.Int32",
"typeSimple": "number",
"isOptional": false,
"defaultValue": null,
"constraintTypes": null,
"bindingSourceId": "ModelBinding",
"descriptorName": "input"
}
],
"returnValue": {
"type": "Volo.Abp.Application.Dtos.PagedResultDto<Volo.Docs.Admin.Projects.ProjectPdfFileDto>",
"typeSimple": "Volo.Abp.Application.Dtos.PagedResultDto<Volo.Docs.Admin.Projects.ProjectPdfFileDto>"
},
"allowAnonymous": null,
"implementFrom": "Volo.Docs.Admin.Projects.IProjectAdminAppService"
},
"DeletePdfFileAsyncByInput": {
"uniqueName": "DeletePdfFileAsyncByInput",
"name": "DeletePdfFileAsync",

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

@ -70,6 +70,13 @@ namespace Volo.Docs.Admin
return _projectAppService.GetListWithoutDetailsAsync();
}
[HttpGet]
[Route("PdfFiles")]
public Task<PagedResultDto<ProjectPdfFileDto>> GetPdfFilesAsync(GetPdfFilesInput input)
{
return _projectAppService.GetPdfFilesAsync(input);
}
[HttpDelete]
[Route("DeletePdfFile")]
public Task DeletePdfFileAsync(DeletePdfFileInput input)

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

@ -41,7 +41,7 @@
</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-outline-primary" id="GenerateBtn" 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>

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

@ -25,6 +25,7 @@
<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/managePdfFiles.js" />
<abp-script src="/Pages/Docs/Admin/Projects/generatePdf.js" />
}

41
modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/ManagePdfFiles.cshtml

@ -0,0 +1,41 @@
@page
@using Microsoft.AspNetCore.Mvc.Localization
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal
@using Volo.Docs.Localization
@model Volo.Docs.Admin.Pages.Docs.Admin.Projects.ManagePdfFiles
@inject IHtmlLocalizer<DocsResource> L
@{
Layout = null;
}
<abp-modal size="@(AbpModalSize.Large)">
<abp-modal-header title="@L["ManagePdfFiles"].Value"></abp-modal-header>
<abp-modal-body>
<abp-card>
<abp-card-header>
<abp-row>
<abp-column size-md="_12" class="text-end">
<abp-button button-type="Primary" icon="plus" text="@L["GeneratePdf"].Value" id="GeneratePdfBtn" />
</abp-column>
</abp-row>
</abp-card-header>
<abp-card-body>
<abp-table id="ProjectPdfFilesTable" class="nowrap">
<thead>
<tr>
<th>@L["Actions"]</th>
<th>@L["FileName"]</th>
<th>@L["Version"]</th>
<th>@L["LanguageCode"]</th>
<th>@L["CreationTime"]</th>
<th>@L["LastUpdateTime"]</th>
</tr>
</thead>
</abp-table>
</abp-card-body>
</abp-card>
</abp-modal-body>
<abp-modal-footer buttons="@(AbpModalButtons.Cancel|AbpModalButtons.Save)">
</abp-modal-footer>
</abp-modal>

14
modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/ManagePdfFiles.cshtml.cs

@ -0,0 +1,14 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Volo.Docs.Admin.Pages.Docs.Admin.Projects;
[Authorize(DocsAdminPermissions.Projects.Default)]
public class ManagePdfFiles : DocsAdminPageModel
{
public virtual Task<IActionResult> OnGet()
{
return Task.FromResult<IActionResult>(Page());
}
}

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

@ -1,7 +1,8 @@
var abp = abp || {};
$(function () {
abp.modals.projectGeneratePdf = function () {
var l = abp.localization.getResource('Docs');
var projectAppService = volo.docs.projects.docsProject;
var pdfGeneratorAppService = volo.docs.documents.docsDocumentPdfGenerator;
var projectAdminAppService = volo.docs.admin.projectsAdmin;
@ -21,7 +22,7 @@ $(function () {
});
})
$("#GeneratePdfBtn").click(function () {
$("#GenerateBtn").click(function () {
var $btn = $(this);
$btn.buttonBusy(true);
$("#GenerateAndDownloadPdfBtn").buttonBusy(true);
@ -30,19 +31,28 @@ $(function () {
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);
$("#GenerateAndDownloadPdfBtn").buttonBusy(false);
} else {
abp.ajax.handleErrorStatusCode(jqXHR.status);
function generatePdf(input) {
pdfGeneratorAppService.generatePdf(input, {
abpHandleError : false,
error: function (jqXHR) {
if (jqXHR.status === 200) {
abp.message.success(l('PdfFileDeletedSuccessfully'));
$btn.buttonBusy(false);
$("#GenerateAndDownloadPdfBtn").buttonBusy(false);
} else {
abp.ajax.handleErrorStatusCode(jqXHR.status);
}
}
}
});
});
}
if(shouldForceToGenerate(input)){
projectAdminAppService.deletePdfFile(input).done(() =>{
generatePdf(input);
});
}else{
generatePdf(input);
}
})
$("#GenerateAndDownloadPdfBtn").click(function () {
@ -51,15 +61,17 @@ $(function () {
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');
if(shouldForceToGenerate(input)){
projectAdminAppService.deletePdfFile(input).done(() =>{
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');
});
}else{
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);
}
function shouldForceToGenerate(input) {
return $("#ForceToGenerate").is(":checked");
}
return {

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

@ -16,9 +16,9 @@ $(function () {
modalClass: 'projectPull',
});
var _generatePdfModal = new abp.ModalManager({
viewUrl: abp.appPath + 'Docs/Admin/Projects/GeneratePdf',
modalClass: 'projectGeneratePdf',
var _managePdfFilesModal = new abp.ModalManager({
viewUrl: abp.appPath + 'Docs/Admin/Projects/ManagePdfFiles',
modalClass: 'projectManagePdfFiles',
});
var _dataTable = $('#ProjectsTable').DataTable(
@ -123,13 +123,13 @@ $(function () {
},
},
{
text: l('GeneratePdf'),
text: l('ManagePdfFiles'),
visible: abp.auth.isGranted(
'Docs.Admin.Documents'
),
action: function (data) {
_generatePdfModal.open({
Id: data.record.id,
_managePdfFilesModal.open({
projectId: data.record.id,
});
}
}

94
modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/managePdfFiles.js

@ -0,0 +1,94 @@
var abp = abp || {};
$(function () {
abp.modals.projectManagePdfFiles = function () {
var l = abp.localization.getResource('Docs');
var projectAdminAppService = volo.docs.admin.projectsAdmin;
var _generatePdfModal = new abp.ModalManager({
viewUrl: abp.appPath + 'Docs/Admin/Projects/GeneratePdf',
modalClass: 'projectGeneratePdf',
});
var initModal = function (publicApi, args) {
var _dataTable = $('#ProjectPdfFilesTable').DataTable(
abp.libs.datatables.normalizeConfiguration({
processing: true,
serverSide: true,
scrollX: true,
paging: true,
searching: false,
autoWidth: false,
scrollCollapse: true,
order: [[2, 'desc']],
ajax: abp.libs.datatables.createAjax(
volo.docs.admin.projectsAdmin.getPdfFiles,
{
projectId : args.projectId
}
),
columnDefs: [
{
rowAction: {
items: [
{
text: l('Delete'),
confirmMessage: function (data) {
return l('PdfFileDeletionWarningMessage', { fileName: data.record.fileName });
},
action: function (data) {
projectAdminAppService.deletePdfFile({
projectId: data.record.projectId,
version: data.record.version,
languageCode: data.record.languageCode
}).then(() => {
_dataTable.ajax.reloadEx();
abp.notify.success(l('PdfGeneratedSuccessfully'));
})
},
}
],
},
},
{
target: 1,
data: 'fileName',
},
{
target: 2,
data: 'version',
},
{
target: 3,
data: 'languageCode',
},
{
target: 4,
data: `creationTime`,
dataFormat: "datetime"
},
{
target: 5,
data: `lastModificationTime`,
dataFormat: "datetime"
}
],
})
);
$('#GeneratePdfBtn').click(function () {
_generatePdfModal.open({
Id: args.projectId,
});
});
_generatePdfModal.onClose(function () {
_dataTable.ajax.reloadEx();
});
};
return {
initModal: initModal,
};
};
});

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

@ -136,6 +136,13 @@
}, ajaxParams));
};
volo.docs.admin.projectsAdmin.getPdfFiles = function(input, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/docs/admin/projects/PdfFiles' + abp.utils.buildQueryString([{ name: 'projectId', value: input.projectId }, { name: 'sorting', value: input.sorting }, { name: 'skipCount', value: input.skipCount }, { name: 'maxResultCount', value: input.maxResultCount }]) + '',
type: 'GET'
}, 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: 'projectId', value: input.projectId }, { name: 'version', value: input.version }, { name: 'languageCode', value: input.languageCode }]) + '',

2
modules/docs/src/Volo.Docs.Common.Application.Contracts/Volo/Docs/Common/DocsCommonPermissionDefinitionProvider.cs

@ -10,7 +10,7 @@ namespace Volo.Docs.Common
{
var group = context.AddGroup(DocsCommonPermissions.GroupName, L("Permission:DocumentManagement.Common"));
group.AddPermission(DocsCommonPermissions.Documents.PdfGeneration, L("Permission:PdfGeneration"));
group.AddPermission(DocsCommonPermissions.Projects.PdfGeneration, L("Permission:PdfGeneration"));
}
private static LocalizableString L(string name)

2
modules/docs/src/Volo.Docs.Common.Application.Contracts/Volo/Docs/Common/DocsCommonPermissions.cs

@ -6,7 +6,7 @@ namespace Volo.Docs.Common
{
public const string GroupName = "Docs.Common";
public static class Documents
public static class Projects
{
public const string PdfGeneration = GroupName + ".PdfGeneration";
}

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

@ -1,30 +1,29 @@
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;
using Volo.Docs.Projects.Pdf;
namespace Volo.Docs.Common.Documents;
[Authorize(DocsCommonPermissions.Documents.PdfGeneration)]
[Authorize(DocsCommonPermissions.Projects.PdfGeneration)]
public class DocumentPdfGeneratorAppService : ApplicationService, IDocumentPdfGeneratorAppService
{
protected IDocumentPdfGenerator DocumentPdfGenerator { get; }
protected IProjectPdfGenerator ProjectPdfGenerator { get; }
protected IProjectRepository ProjectRepository { get; }
public DocumentPdfGeneratorAppService(
IDocumentPdfGenerator documentPdfGenerator,
IProjectPdfGenerator projectPdfGenerator,
IProjectRepository projectRepository)
{
DocumentPdfGenerator = documentPdfGenerator;
ProjectPdfGenerator = projectPdfGenerator;
ProjectRepository = projectRepository;
}
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, includeDetails: true);
return await ProjectPdfGenerator.GenerateAsync(project, input.Version, input.LanguageCode);
}
}

4
modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/Documents/DocumentParams.cs

@ -5,7 +5,7 @@ namespace Volo.Docs.Documents;
public class DocumentParams
{
[JsonPropertyName("Parameters")]
[JsonPropertyName("parameters")]
public List<DocumentParameter> Parameters { get; set; } = new();
@ -18,6 +18,6 @@ public class DocumentParams
public string DisplayName { get; set; }
[JsonPropertyName("values")]
public List<Dictionary<string, string>> Values { get; set; }
public Dictionary<string, string> Values { get; set; }
}
}

4
modules/docs/src/Volo.Docs.Web/HtmlConverting/DocumentToHtmlConverterFactory.cs → modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/HtmlConverting/DocumentToHtmlConverterFactory.cs

@ -19,7 +19,7 @@ namespace Volo.Docs.HtmlConverting
Options = options.Value;
}
public virtual IDocumentToHtmlConverter Create(string format)
public virtual IDocumentToHtmlConverter<TContext> Create<TContext>(string format)
{
var serviceType = Options.Converters.GetOrDefault(format);
if (serviceType == null)
@ -27,7 +27,7 @@ namespace Volo.Docs.HtmlConverting
throw new ApplicationException($"Unknown document format: {format}");
}
return (IDocumentToHtmlConverter)ServiceProvider.GetRequiredService(serviceType);
return (IDocumentToHtmlConverter<TContext>)ServiceProvider.GetRequiredService(serviceType);
}
}
}

14
modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/HtmlConverting/DocumentToHtmlConverterOptions.cs

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
namespace Volo.Docs.HtmlConverting;
public class DocumentToHtmlConverterOptions
{
public Dictionary<string, Type> Converters { get; set; }
public DocumentToHtmlConverterOptions()
{
Converters = new Dictionary<string, Type>();
}
}

9
modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/HtmlConverting/IDocumentToHtmlConverter.cs

@ -0,0 +1,9 @@
using Volo.Docs.Documents;
using Volo.Docs.HtmlConverting;
namespace Volo.Docs.HtmlConverting;
public interface IDocumentToHtmlConverter<in TContext>
{
string Convert(TContext context);
}

6
modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/HtmlConverting/IDocumentToHtmlConverterFactory.cs

@ -0,0 +1,6 @@
namespace Volo.Docs.HtmlConverting;
public interface IDocumentToHtmlConverterFactory
{
IDocumentToHtmlConverter<TContext> Create<TContext>(string format);
}

1
modules/docs/src/Volo.Docs.Domain/Volo/Docs/DocsDomainConsts.cs

@ -3,5 +3,6 @@
public class DocsDomainConsts
{
public static string LanguageConfigFileName = "docs-langs.json";
public static string PdfDocumentToHtmlConverterPrefix = "pdf-";
}
}

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

@ -17,8 +17,11 @@ using Volo.Docs.Documents.FullSearch.Elastic;
using Volo.Docs.FileSystem.Documents;
using Volo.Docs.GitHub;
using Volo.Docs.GitHub.Documents;
using Volo.Docs.HtmlConverting;
using Volo.Docs.Localization;
using Volo.Docs.Projects;
using Volo.Docs.Projects.Pdf.Markdig;
using Volo.Docs.Projects.Pdf.Markdown;
namespace Volo.Docs
{
@ -79,6 +82,11 @@ namespace Volo.Docs
{
client.Timeout = TimeSpan.FromMilliseconds(15000);
});
Configure<DocumentToHtmlConverterOptions>(options =>
{
options.Converters[DocsDomainConsts.PdfDocumentToHtmlConverterPrefix + MarkdigPdfDocumentToHtmlConverter.Type] = typeof(MarkdigPdfDocumentToHtmlConverter);
});
}
public async override Task OnApplicationInitializationAsync(ApplicationInitializationContext context)

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

@ -1,50 +0,0 @@
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, true);
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

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

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

@ -1,10 +0,0 @@
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);
}

2
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/IProjectRepository.cs

@ -8,7 +8,7 @@ namespace Volo.Docs.Projects
{
public interface IProjectRepository : IBasicRepository<Project, Guid>
{
Task<List<Project>> GetListAsync(string sorting, int maxResultCount, int skipCount, CancellationToken cancellationToken = default);
Task<List<Project>> GetListAsync(string sorting, int maxResultCount, int skipCount,bool includeDetails = false, CancellationToken cancellationToken = default);
Task<List<ProjectWithoutDetails>> GetListWithoutDetailsAsync(CancellationToken cancellationToken = default);
Task<Project> GetByShortNameAsync(string shortName, CancellationToken cancellationToken = default);

64
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/BlobProjectPdfFileStore.cs

@ -0,0 +1,64 @@
using System.IO;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Volo.Abp.BlobStoring;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Timing;
namespace Volo.Docs.Projects.Pdf;
public class BlobProjectPdfFileStore : IProjectPdfFileStore, ITransientDependency
{
protected IBlobContainer<DocsProjectPdfContainer> BlobContainer { get; }
protected IOptions<DocsProjectPdfGeneratorOptions> Options { get; }
protected IProjectRepository ProjectRepository { get; }
protected IClock Clock { get; }
public BlobProjectPdfFileStore(
IBlobContainer<DocsProjectPdfContainer> blobContainer,
IOptions<DocsProjectPdfGeneratorOptions> options,
IClock clock, IProjectRepository projectRepository)
{
BlobContainer = blobContainer;
Options = options;
Clock = clock;
ProjectRepository = projectRepository;
}
public virtual async Task SetAsync(Project project, string version, string languageCode, Stream stream)
{
var fileName = Options.Value.CalculatePdfFileName(project, version, languageCode);
await BlobContainer.SaveAsync(fileName, stream, true);
var pdfFile = project.FindPdfFile(fileName);
if(pdfFile == null)
{
project.AddPdfFile(project.Id, fileName, version, languageCode);
}
else
{
pdfFile.LastModificationTime = Clock.Now;
}
await ProjectRepository.UpdateAsync(project);
}
public virtual async Task<Stream> GetOrNullAsync(Project project, string version, string languageCode)
{
var fileName = Options.Value.CalculatePdfFileName(project, version, languageCode);
var pdfFile = project.FindPdfFile(fileName);
if (pdfFile == null)
{
return null;
}
var lastModificationTime = pdfFile.LastModificationTime ?? pdfFile.CreationTime;
if(lastModificationTime.Add(Options.Value.PdfFileCacheExpiration) <= Clock.Now)
{
return null;
}
return await BlobContainer.GetOrNullAsync(Options.Value.CalculatePdfFileName(project, version, languageCode));
}
}

4
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Pdf/DocsDocumentPdfContainer.cs → modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/DocsProjectPdfContainer.cs

@ -1,9 +1,9 @@
using Volo.Abp.BlobStoring;
namespace Volo.Docs.Documents.Pdf;
namespace Volo.Docs.Projects.Pdf;
[BlobContainerName("docs-document-pdf")]
public class DocsDocumentPdfContainer
public class DocsProjectPdfContainer
{
}

7
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Pdf/DocsDocumentPdfGeneratorOptions.cs → modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/DocsProjectPdfGeneratorOptions.cs

@ -1,9 +1,8 @@
using System;
using Volo.Docs.Projects;
namespace Volo.Docs.Documents.Pdf;
namespace Volo.Docs.Projects.Pdf;
public class DocsDocumentPdfGeneratorOptions
public class DocsProjectPdfGeneratorOptions
{
public const string StylePlaceholder = "{{style-placeholder}}";
public const string ContentPlaceholder = "{{content-placeholder}}";
@ -41,7 +40,7 @@ public class DocsDocumentPdfGeneratorOptions
/// </summary>
public Func<Project, string, string, string> CalculatePdfFileName { get; set; }
public DocsDocumentPdfGeneratorOptions()
public DocsProjectPdfGeneratorOptions()
{
HtmlLayout = $@"
<!DOCTYPE html>

10
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/IHtmlToPdfRenderer.cs

@ -0,0 +1,10 @@
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
namespace Volo.Docs.Projects.Pdf;
public interface IHtmlToPdfRenderer
{
Task<MemoryStream> RenderAsync(string html, List<PdfDocument> documents);
}

5
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Pdf/IDocumentPdfFileStore.cs → modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/IProjectPdfFileStore.cs

@ -1,10 +1,9 @@
using System.IO;
using System.Threading.Tasks;
using Volo.Docs.Projects;
namespace Volo.Docs.Documents.Pdf;
namespace Volo.Docs.Projects.Pdf;
public interface IDocumentPdfFileStore
public interface IProjectPdfFileStore
{
Task SetAsync(Project project, string version, string languageCode, Stream stream);

5
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Pdf/IDocumentPdfGenerator.cs → modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/IProjectPdfGenerator.cs

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

7
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Pdf/IText/HtmlIdTagWorkerFactory.cs → modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/IText/HtmlIdTagWorkerFactory.cs

@ -1,18 +1,17 @@
using System.Collections.Generic;
using iText.Html2pdf.Attach;
using iText.Html2pdf.Attach.Impl;
using iText.Kernel.Pdf;
using iText.Kernel.Pdf.Navigation;
using iText.StyledXmlParser.Node;
namespace Volo.Docs.Documents.Pdf.IText;
namespace Volo.Docs.Projects.Pdf.IText;
public class HtmlIdTagWorkerFactory : DefaultTagWorkerFactory
{
private readonly PdfDocument _pdfDocument;
private readonly iText.Kernel.Pdf.PdfDocument _pdfDocument;
private readonly Dictionary<string, int> _pageDestinations = new();
public HtmlIdTagWorkerFactory(PdfDocument pdfDocument)
public HtmlIdTagWorkerFactory(iText.Kernel.Pdf.PdfDocument pdfDocument)
{
_pdfDocument = pdfDocument;
}

26
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Pdf/IText/ITextPdfRenderer.cs → modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/IText/ITextHtmlToPdfRenderer.cs

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using iText.Html2pdf;
using iText.Kernel.Pdf;
@ -10,46 +9,41 @@ using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using ITextDocument = iText.Layout.Document;
namespace Volo.Docs.Documents.Pdf.IText;
namespace Volo.Docs.Projects.Pdf.IText;
public class ITextPdfRenderer : IPdfRenderer ,ITransientDependency
public class ITextHtmlToPdfRenderer : IHtmlToPdfRenderer ,ITransientDependency
{
protected IOptions<DocsDocumentPdfGeneratorOptions> Options { get; }
protected IOptions<DocsProjectPdfGeneratorOptions> Options { get; }
public ITextPdfRenderer(IOptions<DocsDocumentPdfGeneratorOptions> options)
public ITextHtmlToPdfRenderer(IOptions<DocsProjectPdfGeneratorOptions> options)
{
Options = options;
}
public virtual async Task<MemoryStream> GeneratePdfAsync(string htmlContent, List<PdfDocumentNode> pdfDocumentNodes)
public virtual Task<MemoryStream> RenderAsync(string html, List<PdfDocument> documents)
{
var pdfStream = new MemoryStream();
var pdfWrite = new PdfWriter(pdfStream);
var pdfDocument = new PdfDocument(pdfWrite);
var pdfDocument = new iText.Kernel.Pdf.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.HtmlStyle);
htmlBuilder.Replace(DocsDocumentPdfGeneratorOptions.ContentPlaceholder, htmlContent);
var converter = new ConverterProperties();
var tagWorkerFactory = new HtmlIdTagWorkerFactory(pdfDocument);
converter.SetTagWorkerFactory(tagWorkerFactory);
HtmlConverter.ConvertToDocument(htmlBuilder.ToString(), pdfDocument, converter);
HtmlConverter.ConvertToDocument(html, pdfDocument, converter);
tagWorkerFactory.AddNamedDestinations();
var pdfOutlines = pdfDocument.GetOutlines(false);
BuildPdfOutlines(pdfOutlines, pdfDocumentNodes);
BuildPdfOutlines(pdfOutlines, documents);
textDocument.Close();
pdfStream.Position = 0;
return pdfStream;
return Task.FromResult(pdfStream);
}
private void BuildPdfOutlines(PdfOutline parentOutline, List<PdfDocumentNode> pdfDocumentNodes)
private void BuildPdfOutlines(PdfOutline parentOutline, List<PdfDocument> pdfDocumentNodes)
{
foreach (var pdfDocumentNode in pdfDocumentNodes)
{

13
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Pdf/Markdig/AnchorLinkRenderer.cs → modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/Markdown/AnchorLinkRenderer.cs

@ -1,22 +1,19 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Markdig.Renderers;
using Markdig.Renderers.Html.Inlines;
using Markdig.Syntax;
using Markdig.Syntax.Inlines;
using Volo.Docs.Utils;
namespace Volo.Docs.Documents.Pdf.Markdig;
namespace Volo.Docs.Projects.Pdf.Markdig;
public class AnchorLinkRenderer : LinkInlineRenderer
{
private readonly PdfDocumentNode _documentNode;
private readonly PdfDocument _document;
public AnchorLinkRenderer(PdfDocumentNode documentNode)
public AnchorLinkRenderer(PdfDocument document)
{
_documentNode = documentNode;
_document = document;
}
protected override void Write(HtmlRenderer renderer, LinkInline link)
@ -27,7 +24,7 @@ public class AnchorLinkRenderer : LinkInlineRenderer
return;
}
var anchor = ResolveRelativeMarkdownPath(_documentNode.Document.Name, link.Url)
var anchor = ResolveRelativeMarkdownPath(_document.Document.Name, link.Url)
.Replace(".md",string.Empty).Replace("/","-").Replace(" ", "-").ToLower();
renderer.Write("<a href=\"#").Write(anchor).Write("\">");

10
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Pdf/Markdig/AnchorLinkResolverExtension.cs → modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/Markdown/AnchorLinkResolverExtension.cs

@ -2,15 +2,15 @@ using Markdig;
using Markdig.Renderers;
using Markdig.Renderers.Html.Inlines;
namespace Volo.Docs.Documents.Pdf.Markdig;
namespace Volo.Docs.Projects.Pdf.Markdig;
public class AnchorLinkResolverExtension : IMarkdownExtension
{
private readonly PdfDocumentNode _documentNode;
private readonly PdfDocument _document;
public AnchorLinkResolverExtension(PdfDocumentNode documentNode)
public AnchorLinkResolverExtension(PdfDocument document)
{
_documentNode = documentNode;
_document = document;
}
public void Setup(MarkdownPipelineBuilder pipeline)
@ -21,7 +21,7 @@ public class AnchorLinkResolverExtension : IMarkdownExtension
{
if (renderer is HtmlRenderer htmlRenderer)
{
htmlRenderer.ObjectRenderers.Replace<LinkInlineRenderer>(new AnchorLinkRenderer(_documentNode));
htmlRenderer.ObjectRenderers.Replace<LinkInlineRenderer>(new AnchorLinkRenderer(_document));
}
}
}

73
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/Markdown/MarkdigPdfDocumentToHtmlConverter.cs

@ -0,0 +1,73 @@
using System;
using System.Text.RegularExpressions;
using System.Web;
using Markdig;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Docs.HtmlConverting;
using Volo.Docs.Projects.Pdf.Markdig;
using Volo.Docs.Utils;
namespace Volo.Docs.Projects.Pdf.Markdown;
public class MarkdigPdfDocumentToHtmlConverter : IDocumentToHtmlConverter<PdfDocumentToHtmlConverterContext>, ITransientDependency
{
public const string Type = "md";
protected IOptions<DocsProjectPdfGeneratorOptions> Options { get; }
public MarkdigPdfDocumentToHtmlConverter(IOptions<DocsProjectPdfGeneratorOptions> options)
{
Options = options;
}
public virtual string Convert(PdfDocumentToHtmlConverterContext converterContext)
{
var htmlContent = global::Markdig.Markdown.ToHtml(NormalizeContent(converterContext.Content), GetPipeline(converterContext.PdfDocument));
return NormalizeHtmlContent(htmlContent, converterContext.PdfDocument);
}
protected virtual MarkdownPipeline GetPipeline(PdfDocument pdfDocument)
{
return new MarkdownPipelineBuilder()
.UseAdvancedExtensions()
.Use(new AnchorLinkResolverExtension(pdfDocument))
.Build();
}
protected virtual string NormalizeContent(string content)
{
return Regex.Replace(content, @"`{3,4}json\s*//\[doc-nav\][\s\S]*?`{3,4}", string.Empty, RegexOptions.IgnoreCase);
}
protected virtual string NormalizeHtmlContent(string htmlContent, PdfDocument pdfDocument)
{
htmlContent = WrapHtmlWithPageDiv(htmlContent, pdfDocument);
return ReplaceRelativeImageUrls(htmlContent, pdfDocument);
}
private string WrapHtmlWithPageDiv(string htmlContent, PdfDocument pdfDocument)
{
return $"<div class='page' id='{pdfDocument.Id}'>{htmlContent}</div>";
}
private string ReplaceRelativeImageUrls(string htmlContent, PdfDocument pdfDocument)
{
return Regex.Replace(htmlContent, @"(<img\s+[^>]*)src=""([^""]*)""([^>]*>)", delegate (Match match)
{
if (UrlHelper.IsExternalLink(match.Groups[2].Value))
{
return match.Value;
}
var rootUrl = UrlHelper.IsExternalLink(pdfDocument.Document.RawRootUrl)
? pdfDocument.Document.RawRootUrl.EnsureEndsWith('/')
: Options.Value.BaseUrl.EnsureEndsWith('/') + pdfDocument.Document.RawRootUrl.TrimStart('/').EnsureEndsWith('/');
var newImageSource = rootUrl +
(pdfDocument.Document.LocalDirectory.IsNullOrEmpty() ? "" : pdfDocument.Document.LocalDirectory.TrimStart('/').EnsureEndsWith('/')) +
match.Groups[2].Value.TrimStart('/');
return match.Groups[1] + " src=\"" + HttpUtility.HtmlEncode(newImageSource) + "\" " + match.Groups[3];
}, RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Multiline);
}
}

7
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Pdf/PdfDocumentNode.cs → modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/PdfDocument.cs

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

13
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/PdfDocumentToHtmlConverterContext.cs

@ -0,0 +1,13 @@
namespace Volo.Docs.Projects.Pdf;
public class PdfDocumentToHtmlConverterContext
{
public string Content { get; set; }
public PdfDocument PdfDocument { get; set; }
public PdfDocumentToHtmlConverterContext(string content, PdfDocument pdfDocument)
{
Content = content;
PdfDocument = pdfDocument;
}
}

292
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/Pdf/DocumentPdfGenerator.cs → modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Pdf/ProjectPdfGenerator.cs

@ -3,119 +3,132 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web;
using Markdig;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Volo.Abp;
using Volo.Abp.Content;
using Volo.Abp.DependencyInjection;
using Volo.Docs.Documents.Pdf.Markdig;
using Volo.Docs.Documents;
using Volo.Docs.Documents.Rendering;
using Volo.Docs.Projects;
using Volo.Docs.Utils;
using Volo.Docs.HtmlConverting;
using Volo.Extensions;
namespace Volo.Docs.Documents.Pdf;
namespace Volo.Docs.Projects.Pdf;
public class DocumentPdfGenerator : IDocumentPdfGenerator, ITransientDependency
public class ProjectPdfGenerator : IProjectPdfGenerator, ITransientDependency
{
protected IDocumentSourceFactory DocumentStoreFactory { get; }
protected IDocumentToHtmlConverterFactory DocumentToHtmlConverterFactory { get; }
protected IDocumentRepository DocumentRepository { get; }
protected IDocumentSectionRenderer DocumentSectionRenderer { get; }
protected IOptions<DocsDocumentPdfGeneratorOptions> Options { get; }
protected IDocumentPdfFileStore DocumentPdfFileStore { get; }
protected IPdfRenderer PdfRenderer { get; }
protected ILogger<DocumentPdfGenerator> Logger { get; set; }
protected IOptions<DocsProjectPdfGeneratorOptions> Options { get; }
protected IProjectPdfFileStore ProjectPdfFileStore { get; }
protected IHtmlToPdfRenderer HtmlToPdfRenderer { get; }
protected ILogger<ProjectPdfGenerator> Logger { get; set; }
protected IDocumentSource DocumentSource { get; set; }
protected DocumentParams DocumentParams { get; set; }
protected List<PdfDocumentNode> AllPdfDocumentNodes { get; } = [];
protected Project Project { get; set; }
protected List<PdfDocument> AllPdfDocuments { get; } = [];
public DocumentPdfGenerator(
public ProjectPdfGenerator(
IDocumentSourceFactory documentStoreFactory,
IDocumentRepository documentRepository,
IOptions<DocsDocumentPdfGeneratorOptions> options,
IOptions<DocsProjectPdfGeneratorOptions> options,
IDocumentSectionRenderer documentSectionRenderer,
IDocumentPdfFileStore documentPdfFileStore,
IPdfRenderer pdfRenderer)
IProjectPdfFileStore projectPdfFileStore,
IHtmlToPdfRenderer htmlToPdfRenderer,
IDocumentToHtmlConverterFactory documentToHtmlConverterFactory)
{
DocumentStoreFactory = documentStoreFactory;
DocumentRepository = documentRepository;
Options = options;
DocumentSectionRenderer = documentSectionRenderer;
DocumentPdfFileStore = documentPdfFileStore;
PdfRenderer = pdfRenderer;
Logger = NullLogger<DocumentPdfGenerator>.Instance;
ProjectPdfFileStore = projectPdfFileStore;
HtmlToPdfRenderer = htmlToPdfRenderer;
DocumentToHtmlConverterFactory = documentToHtmlConverterFactory;
Logger = NullLogger<ProjectPdfGenerator>.Instance;
}
public virtual async Task<IRemoteStreamContent> GenerateAsync(Project project, string version, string languageCode)
{
var fileName = Options.Value.CalculatePdfFileName(project, version, languageCode);
var fileStream = await DocumentPdfFileStore.GetOrNullAsync(project, version, languageCode);
var fileStream = await ProjectPdfFileStore.GetOrNullAsync(project, version, languageCode);
if (fileStream != null)
{
return new RemoteStreamContent(fileStream, fileName, "application/pdf");
}
Project = project;
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 SetAllPdfDocumentsAsync(navigation.Items, project, version, languageCode);
var html = await BuildHtmlAsync();
var pdfStream = await HtmlToPdfRenderer.RenderAsync(html, AllPdfDocuments);
var pdfStream = await PdfRenderer.GeneratePdfAsync(htmlContent, AllPdfDocumentNodes);
await DocumentPdfFileStore.SetAsync(project, version, languageCode, pdfStream);
await ProjectPdfFileStore.SetAsync(project, version, languageCode, pdfStream);
return new RemoteStreamContent(pdfStream, fileName, "application/pdf");
}
protected virtual async Task<string> ConvertDocumentsToHtmlAsync(List<PdfDocumentNode> pdfDocumentNodes)
protected virtual async Task<string> BuildHtmlAsync()
{
var htmlContent = await ConvertDocumentsToHtmlAsync(AllPdfDocuments);
var htmlBuilder = new StringBuilder();
htmlBuilder.Append(Options.Value.HtmlLayout);
htmlBuilder.Replace(DocsProjectPdfGeneratorOptions.StylePlaceholder, Options.Value.HtmlStyle);
htmlBuilder.Replace(DocsProjectPdfGeneratorOptions.ContentPlaceholder, htmlContent);
return htmlBuilder.ToString();
}
protected virtual async Task<string> ConvertDocumentsToHtmlAsync(List<PdfDocument> pdfDocuments)
{
var contentBuilder = new StringBuilder();
foreach (var pdfDocumentNode in pdfDocumentNodes)
foreach (var pdfDocument in pdfDocuments)
{
if (pdfDocumentNode.Document != null)
if (pdfDocument.Document != null)
{
var renderedDocument = await RenderDocumentAsync(pdfDocumentNode);
renderedDocument = NormalizeHtmlContent(pdfDocumentNode, Markdown.ToHtml(renderedDocument, GetMarkdownPipeline(pdfDocumentNode)));
contentBuilder.AppendLine(renderedDocument);
var renderedDocument = await RenderDocumentAsync(pdfDocument);
var documentToHtmlConverter = GetDocumentToHtmlConverter(Project, pdfDocument);
var htmlContent = documentToHtmlConverter.Convert(new PdfDocumentToHtmlConverterContext(renderedDocument, pdfDocument));
contentBuilder.AppendLine(htmlContent);
}
if (pdfDocumentNode.HasChildren)
if (pdfDocument.HasChildren)
{
contentBuilder.AppendLine(await ConvertDocumentsToHtmlAsync(pdfDocumentNode.Children));
contentBuilder.AppendLine(await ConvertDocumentsToHtmlAsync(pdfDocument.Children));
}
}
return contentBuilder.ToString();
}
protected virtual IDocumentToHtmlConverter<PdfDocumentToHtmlConverterContext> GetDocumentToHtmlConverter(Project project, PdfDocument pdfDocument)
{
return DocumentToHtmlConverterFactory.Create<PdfDocumentToHtmlConverterContext>(DocsDomainConsts.PdfDocumentToHtmlConverterPrefix +(pdfDocument.Document.Format ?? project.Format));
}
protected virtual async Task<string> RenderDocumentAsync(PdfDocumentNode pdfDocumentNode)
protected virtual async Task<string> RenderDocumentAsync(PdfDocument pdfDocument)
{
var content = NormalizeMarkdownContent(pdfDocumentNode.Document.Content);
var renderedDocument = await DocumentSectionRenderer.RenderAsync(content, pdfDocumentNode.RenderParameters);
if (pdfDocumentNode.RenderParameters != null)
{
renderedDocument = SetDocumentTitle(renderedDocument, pdfDocumentNode.Title);
}
return renderedDocument;
return await DocumentSectionRenderer.RenderAsync(pdfDocument.Document.Content, pdfDocument.RenderParameters);
}
protected virtual async Task SetAllPdfDocumentNodesAsync(
protected virtual async Task SetAllPdfDocumentsAsync(
List<NavigationNode> navigations,
Project project,
string version,
string languageCode,
PdfDocumentNode parentPdfDocumentNode = null)
PdfDocument parentPdfDocument = null)
{
var groupedCombinationsDocuments = new Dictionary<string, List<PdfDocumentNode>>();
var groupedCombinationsDocuments = new Dictionary<string, List<PdfDocument>>();
foreach (var navigation in navigations)
{
if (navigation.IgnoreOnDownload)
@ -124,7 +137,7 @@ public class DocumentPdfGenerator : IDocumentPdfGenerator, ITransientDependency
}
try
{
var pdfDocumentNode = new PdfDocumentNode
var pdfDocument = new PdfDocument
{
Title = navigation.Text,
IgnoreOnOutline = navigation.Path == Options.Value.IndexPagePath
@ -132,20 +145,19 @@ public class DocumentPdfGenerator : IDocumentPdfGenerator, ITransientDependency
if (!navigation.Path.IsNullOrWhiteSpace() && !navigation.HasChildItems)
{
var path = NormalizeNavigationPath(navigation.Path);
var document = await GetDocumentAsync(project, path, version, languageCode);
var document = await GetDocumentAsync(project, navigation.Path, version, languageCode);
var parameters = await DocumentSectionRenderer.GetAvailableParametersAsync(document.Content);
var parameterCombinations = GenerateAllParameterCombinations(parameters.Keys.ToList(), parameters);
var firstParameterCombination = parameterCombinations.FirstOrDefault();
pdfDocumentNode.Document = document;
pdfDocumentNode.Title = GetDocumentTitle(navigation.Text, document.Content, firstParameterCombination);
pdfDocumentNode.Id = GetDocumentId(path, firstParameterCombination, true);
pdfDocumentNode.RenderParameters = firstParameterCombination;
pdfDocument.Document = document;
pdfDocument.RenderParameters = firstParameterCombination;
pdfDocument.Id = GetDocumentId(document.Name, document.Format ?? project.Format, firstParameterCombination, true);
pdfDocument.Title = GetDocumentTitle(navigation.Text, firstParameterCombination, DocumentParams);
if(parameters.Count <= 1)
{
AddParameterCombinationsDocuments(parentPdfDocumentNode, groupedCombinationsDocuments);
AddParameterCombinationsDocuments(parentPdfDocument, groupedCombinationsDocuments);
}
else
{
@ -158,34 +170,34 @@ public class DocumentPdfGenerator : IDocumentPdfGenerator, ITransientDependency
groupedCombinationsDocuments[key] = [];
}
groupedCombinationsDocuments[key].Add(new PdfDocumentNode
groupedCombinationsDocuments[key].Add(new PdfDocument
{
Document = document,
Title = GetDocumentTitle(navigation.Text, document.Content, parameterCombination),
Id = GetDocumentId(path, parameterCombination, false),
Id = GetDocumentId(document.Name, document.Format ?? project.Format, parameterCombination, false),
Title = GetDocumentTitle(navigation.Text, parameterCombination, DocumentParams),
RenderParameters = parameterCombination
});
}
}
}
if (parentPdfDocumentNode == null)
if (parentPdfDocument == null)
{
AllPdfDocumentNodes.Add(pdfDocumentNode);
AllPdfDocuments.Add(pdfDocument);
}
else
{
parentPdfDocumentNode.Children.Add(pdfDocumentNode);
parentPdfDocument.Children.Add(pdfDocument);
}
if (navigation.HasChildItems)
{
await SetAllPdfDocumentNodesAsync(navigation.Items, project, version, languageCode, pdfDocumentNode);
await SetAllPdfDocumentsAsync(navigation.Items, project, version, languageCode, pdfDocument);
}
if (navigation == navigations.Last())
{
AddParameterCombinationsDocuments(parentPdfDocumentNode, groupedCombinationsDocuments);
AddParameterCombinationsDocuments(parentPdfDocument, groupedCombinationsDocuments);
}
}
catch (Exception e)
@ -195,26 +207,21 @@ public class DocumentPdfGenerator : IDocumentPdfGenerator, ITransientDependency
}
}
private void AddParameterCombinationsDocuments(PdfDocumentNode parentPdfDocumentNode, Dictionary<string,List<PdfDocumentNode>> groupedCombinationsDocuments)
private void AddParameterCombinationsDocuments(PdfDocument parentPdfDocument, Dictionary<string,List<PdfDocument>> groupedCombinationsDocuments)
{
foreach (var combinations in groupedCombinationsDocuments)
{
if (parentPdfDocumentNode == null)
if (parentPdfDocument == null)
{
AllPdfDocumentNodes.AddRange(combinations.Value);
AllPdfDocuments.AddRange(combinations.Value);
}
else
{
parentPdfDocumentNode.Children.AddRange(combinations.Value);
parentPdfDocument.Children.AddRange(combinations.Value);
}
}
}
private string NormalizeNavigationPath(string path)
{
return !path.EndsWith(".md") ? Path.Combine(path, "index.md") : path;
}
private async Task<NavigationNode> GetNavigationAsync(
Project project,
string version,
@ -273,19 +280,38 @@ public class DocumentPdfGenerator : IDocumentPdfGenerator, ITransientDependency
string languageCode)
{
version = string.IsNullOrWhiteSpace(version) ? project.LatestVersionBranchName : version;
var document = await DocumentRepository.FindAsync(project.Id, documentName, version, languageCode);
Document document = null;
if (document != null)
Exception firstException = null;
foreach (var name in GetPossibleNames(documentName, project.Format))
{
return document;
try
{
document = await DocumentRepository.FindAsync(project.Id, documentName, version, languageCode);
if (document != null)
{
break;
}
document = await DocumentSource.GetDocumentAsync(project, name, languageCode, version);
break;
}
catch (Exception ex)
{
firstException ??= ex;
}
}
document = await DocumentSource.GetDocumentAsync(project, documentName, languageCode, version);
if(document == null)
{
throw firstException!;
}
return document;
}
protected virtual List<DocumentRenderParameters> GenerateAllParameterCombinations(List<string> parameterKeys, Dictionary<string, List<string>> parameters)
private List<DocumentRenderParameters> GenerateAllParameterCombinations(List<string> parameterKeys, Dictionary<string, List<string>> parameters)
{
var parameterCombinations = new List<DocumentRenderParameters>();
@ -314,86 +340,86 @@ public class DocumentPdfGenerator : IDocumentPdfGenerator, ITransientDependency
}
}
private string SetDocumentTitle(string content, string title)
private static string GetDocumentId(string documentName, string format, DocumentRenderParameters parameters, bool isFirstCombinationDocument)
{
var titleLine = content.Split(Environment.NewLine).FirstOrDefault(x => x.TrimStart().StartsWith("#"));
if (titleLine == null)
{
return content;
}
var newTitle = $"# {title}";
return content.Replace(titleLine, newTitle);
var id = documentName.Replace("." + format, string.Empty).Replace("/","-").Replace(" ", "-").ToLower();
if (parameters != null && !isFirstCombinationDocument)
{
id = $"{id}{parameters.Select(x => $"{x.Key}_{x.Value}").JoinAsString("-")}";
}
return id;
}
private string GetDocumentTitle(string title, string content, DocumentRenderParameters parameters)
private static string GetDocumentTitle(string title, DocumentRenderParameters parameters, DocumentParams documentParams)
{
if (parameters == null || parameters.Count <= 0)
{
return title;
}
var titleLine = content.Split(Environment.NewLine).FirstOrDefault(x => x.TrimStart().StartsWith("#"));
if (titleLine == null)
var paramValues = parameters.Select(x =>
{
return title;
}
var documentParam = documentParams.Parameters.FirstOrDefault(p => p.Name == x.Key);
return $"{documentParam?.DisplayName ?? x.Key} : {documentParam?.Values[x.Value] ?? x.Value}";
});
var paramValues = parameters.Select(x => $"{DocumentParams.Parameters.FirstOrDefault(p => p.Name == x.Key)?.DisplayName ?? x.Key}: {x.Value}").ToList();
return titleLine.TrimStart('#').Trim() + $" ({string.Join(", ", paramValues)})";
}
private string GetDocumentId(string path, DocumentRenderParameters parameters, bool isFirstCombinationDocument)
{
var id = path.Replace(".md",string.Empty).Replace("/","-").Replace(" ", "-").ToLower();
if (parameters != null && !isFirstCombinationDocument)
{
id = $"{id}{parameters.Select(x => $"{x.Key}_{x.Value}").JoinAsString("-")}";
}
return id;
return title.Trim() + $" ({string.Join(", ", paramValues)})";
}
private string NormalizeHtmlContent(PdfDocumentNode pdfDocumentNode, string htmlContent)
private static List<string> GetPossibleNames(string originalDocumentName, string format)
{
htmlContent = $"<div class='page' id='{pdfDocumentNode.Id}'>{htmlContent}</div>";
htmlContent = ReplaceRelativeImageUrls(pdfDocumentNode, htmlContent);
var extension = Path.GetExtension(originalDocumentName);
if (extension.IsNullOrWhiteSpace())
{
extension = "." + format;
}
if (!extension.Equals("." + format, StringComparison.OrdinalIgnoreCase))
{
return [originalDocumentName];
}
return htmlContent;
}
var lowerCaseIndex = "index." + format;
var titleCaseIndex = "Index." + format;
var indexLength = lowerCaseIndex.Length;
private string ReplaceRelativeImageUrls(PdfDocumentNode pdfDocumentNode, string htmlContent)
{
return Regex.Replace(htmlContent, @"(<img\s+[^>]*)src=""([^""]*)""([^>]*>)", delegate (Match match)
var possibleNames = new List<string> {originalDocumentName};
if (originalDocumentName.EndsWith("/" + lowerCaseIndex, StringComparison.OrdinalIgnoreCase) || originalDocumentName.Equals(lowerCaseIndex, StringComparison.OrdinalIgnoreCase))
{
if (UrlHelper.IsExternalLink(match.Groups[2].Value))
var indexPart = originalDocumentName.Right(indexLength);
var documentNameWithoutIndex = originalDocumentName.Left(originalDocumentName.Length - lowerCaseIndex.Length);
if(indexPart != lowerCaseIndex)
{
return match.Value;
possibleNames.Add(documentNameWithoutIndex + lowerCaseIndex);
}
var rootUrl = UrlHelper.IsExternalLink(pdfDocumentNode.Document.RawRootUrl)
? pdfDocumentNode.Document.RawRootUrl.EnsureEndsWith('/')
: Options.Value.BaseUrl.EnsureEndsWith('/') + pdfDocumentNode.Document.RawRootUrl.TrimStart('/').EnsureEndsWith('/');
var newImageSource = rootUrl +
(pdfDocumentNode.Document.LocalDirectory.IsNullOrEmpty() ? "" : pdfDocumentNode.Document.LocalDirectory.TrimStart('/').EnsureEndsWith('/')) +
match.Groups[2].Value.TrimStart('/');
return match.Groups[1] + " src=\"" + HttpUtility.HtmlEncode(newImageSource) + "\" " + match.Groups[3];
if(indexPart != titleCaseIndex)
{
possibleNames.Add(documentNameWithoutIndex + titleCaseIndex);
}
}
else
{
var documentNameWithoutExtension = RemoveFileExtensionFromPath(originalDocumentName, format).EnsureEndsWith('/');
possibleNames.Add(documentNameWithoutExtension + lowerCaseIndex);
possibleNames.Add(documentNameWithoutExtension + titleCaseIndex);
}
}, RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Multiline);
return possibleNames;
}
private string NormalizeMarkdownContent(string content)
private static string RemoveFileExtensionFromPath(string path, string format)
{
var pattern = @"`{3,4}json\s*//\[doc-nav\][\s\S]*?`{3,4}";
return Regex.Replace(content, pattern, string.Empty, RegexOptions.IgnoreCase);
}
if (path == null)
{
return null;
}
private MarkdownPipeline GetMarkdownPipeline(PdfDocumentNode pdfDocumentNode)
{
return new MarkdownPipelineBuilder()
.UseAdvancedExtensions()
.Use(new AnchorLinkResolverExtension(pdfDocumentNode))
.Build();
return path.EndsWith("." + format)
? path.Left(path.Length - format.Length - 1)
: path;
}
}

23
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Project.cs

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using Volo.Abp;
using Volo.Abp.Domain.Entities;
@ -47,9 +48,12 @@ namespace Volo.Docs.Projects
public virtual string MainWebsiteUrl { get; set; }
public virtual string LatestVersionBranchName { get; set; }
public virtual List<ProjectPdfFile> PdfFiles { get; set; }
protected Project()
{
PdfFiles = new List<ProjectPdfFile>();
}
public Project(
@ -103,5 +107,24 @@ namespace Volo.Docs.Projects
{
ShortName = ShortName.ToLower();
}
public virtual ProjectPdfFile FindPdfFile(string fileName)
{
return PdfFiles.Find(x => x.FileName == fileName);
}
public virtual void AddPdfFile(Guid projectId, string fileName, string version, string languageCode)
{
PdfFiles.Add(new ProjectPdfFile(projectId, fileName, version, languageCode));
}
public virtual void RemovePdfFile(string fileName)
{
var pdfFile = FindPdfFile(fileName);
if (pdfFile != null)
{
PdfFiles.Remove(pdfFile);
}
}
}
}

38
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/ProjectPdfFile.cs

@ -0,0 +1,38 @@
using System;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
namespace Volo.Docs.Projects;
public class ProjectPdfFile : Entity, IHasCreationTime, IHasModificationTime
{
public virtual Guid ProjectId { get; set; }
public virtual string FileName { get; set; }
public virtual string Version { get; set; }
public virtual string LanguageCode { get; set; }
public virtual DateTime CreationTime { get; set; }
public virtual DateTime? LastModificationTime { get; set; }
protected ProjectPdfFile()
{
}
public ProjectPdfFile(Guid projectId, string fileName, string version, string languageCode)
{
ProjectId = projectId;
FileName = fileName;
Version = version;
LanguageCode = languageCode;
}
public override object[] GetKeys()
{
return [ProjectId, FileName];
}
public virtual bool Equals(Guid projectId, string fileName)
{
return ProjectId == projectId && FileName == fileName;
}
}

2
modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/EntityFrameworkCore/DocsDbContext.cs

@ -16,6 +16,8 @@ namespace Volo.Docs.EntityFrameworkCore
public DbSet<Document> Documents { get; set; }
public DbSet<DocumentContributor> DocumentContributors { get; set; }
public DbSet<ProjectPdfFile> ProjectPdfFiles { get; set; }
public DocsDbContext(DbContextOptions<DocsDbContext> options)
: base(options)

15
modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/EntityFrameworkCore/DocsDbContextModelBuilderExtensions.cs

@ -31,6 +31,10 @@ namespace Volo.Docs.EntityFrameworkCore
b.Property(x => x.NavigationDocumentName).IsRequired().HasMaxLength(ProjectConsts.MaxNavigationDocumentNameLength);
b.Property(x => x.ParametersDocumentName).IsRequired().HasMaxLength(ProjectConsts.MaxParametersDocumentNameLength);
b.Property(x => x.LatestVersionBranchName).HasMaxLength(ProjectConsts.MaxLatestVersionBranchNameLength);
b.HasMany(x => x.PdfFiles).WithOne()
.HasForeignKey(x => new { x.ProjectId })
.IsRequired();
b.ApplyObjectExtensionMappings();
});
@ -69,6 +73,17 @@ namespace Volo.Docs.EntityFrameworkCore
b.ApplyObjectExtensionMappings();
});
builder.Entity<ProjectPdfFile>(b =>
{
b.ToTable(AbpDocsDbProperties.DbTablePrefix + "ProjectPdfFiles", AbpDocsDbProperties.DbSchema);
b.ConfigureByConvention();
b.HasKey(x => new { x.ProjectId, x.FileName });
b.ApplyObjectExtensionMappings();
});
builder.TryConfigureObjectExtensions<DocsDbContext>();
}

6
modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/EntityFrameworkCore/DocsEfCoreQueryableExtensions.cs

@ -1,6 +1,7 @@
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Volo.Docs.Documents;
using Volo.Docs.Projects;
namespace Volo.Docs.EntityFrameworkCore
{
@ -10,5 +11,10 @@ namespace Volo.Docs.EntityFrameworkCore
{
return !include ? queryable : queryable.Include(x => x.Contributors);
}
public static IQueryable<Project> IncludeDetails(this IQueryable<Project> queryable, bool include = true)
{
return !include ? queryable : queryable.Include(x => x.PdfFiles);
}
}
}

2
modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/EntityFrameworkCore/IDocsDbContext.cs

@ -16,5 +16,7 @@ namespace Volo.Docs.EntityFrameworkCore
DbSet<Document> Documents { get; }
DbSet<DocumentContributor> DocumentContributors { get; }
DbSet<ProjectPdfFile> ProjectPdfFiles { get; set; }
}
}

14
modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/Projects/EfCoreProjectRepository.cs

@ -19,9 +19,14 @@ namespace Volo.Docs.Projects
{
}
public virtual async Task<List<Project>> GetListAsync(string sorting, int maxResultCount, int skipCount, CancellationToken cancellationToken = default)
public virtual async Task<List<Project>> GetListAsync(
string sorting,
int maxResultCount,
int skipCount,
bool includeDetails = false,
CancellationToken cancellationToken = default)
{
var projects = await (await GetDbSetAsync()).OrderBy(sorting.IsNullOrEmpty() ? "Id desc" : sorting)
var projects = await (await GetDbSetAsync()).IncludeDetails(includeDetails).OrderBy(sorting.IsNullOrEmpty() ? "Id desc" : sorting)
.PageBy(skipCount, maxResultCount)
.ToListAsync(GetCancellationToken(cancellationToken));
@ -64,5 +69,10 @@ namespace Volo.Docs.Projects
{
return shortName.ToLower();
}
public async override Task<IQueryable<Project>> WithDetailsAsync()
{
return (await GetQueryableAsync()).IncludeDetails();
}
}
}

7
modules/docs/src/Volo.Docs.MongoDB/Volo/Docs/Projects/MongoProjectRepository.cs

@ -20,7 +20,12 @@ namespace Volo.Docs.Projects
{
}
public virtual async Task<List<Project>> GetListAsync(string sorting, int maxResultCount, int skipCount, CancellationToken cancellationToken = default)
public virtual async Task<List<Project>> GetListAsync(
string sorting,
int maxResultCount,
int skipCount,
bool includeDetails = false,
CancellationToken cancellationToken = default)
{
var projects = await (await GetQueryableAsync(cancellationToken)).OrderBy(sorting.IsNullOrEmpty() ? "Id desc" : sorting).PageBy(skipCount, maxResultCount)
.ToListAsync(GetCancellationToken(cancellationToken));

26
modules/docs/src/Volo.Docs.Web/HtmlConverting/DocumentToHtmlConverterContext.cs

@ -0,0 +1,26 @@
using Volo.Docs.Common.Projects;
using Volo.Docs.Documents;
namespace Volo.Docs.HtmlConverting;
public class DocumentToHtmlConverterContext
{
public ProjectDto Project { get; set; }
public DocumentWithDetailsDto Document { get; set; }
public string Version { get; set; }
public string LanguageCode { get; set; }
public string ProjectShortName { get; set; }
public DocumentToHtmlConverterContext(ProjectDto project,
DocumentWithDetailsDto document,
string version,
string languageCode,
string projectShortName = null)
{
Project = project;
Document = document;
Version = version;
LanguageCode = languageCode;
ProjectShortName = projectShortName;
}
}

15
modules/docs/src/Volo.Docs.Web/HtmlConverting/DocumentToHtmlConverterOptions.cs

@ -1,15 +0,0 @@
using System;
using System.Collections.Generic;
namespace Volo.Docs.HtmlConverting
{
public class DocumentToHtmlConverterOptions
{
public Dictionary<string, Type> Converters { get; set; }
public DocumentToHtmlConverterOptions()
{
Converters = new Dictionary<string, Type>();
}
}
}

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

@ -1,10 +0,0 @@
using Volo.Docs.Common.Projects;
using Volo.Docs.Documents;
namespace Volo.Docs.HtmlConverting
{
public interface IDocumentToHtmlConverter
{
string Convert(ProjectDto project, DocumentWithDetailsDto document, string version, string languageCode, string projectShortName = null);
}
}

7
modules/docs/src/Volo.Docs.Web/HtmlConverting/IDocumentToHtmlConverterFactory.cs

@ -1,7 +0,0 @@
namespace Volo.Docs.HtmlConverting
{
public interface IDocumentToHtmlConverterFactory
{
IDocumentToHtmlConverter Create(string format);
}
}

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

@ -10,7 +10,7 @@ using Volo.Docs.Utils;
namespace Volo.Docs.Markdown
{
public class MarkdownDocumentToHtmlConverter : IDocumentToHtmlConverter, ITransientDependency
public class MarkdownDocumentToHtmlConverter : IDocumentToHtmlConverter<DocumentToHtmlConverterContext>, ITransientDependency
{
public const string Type = "md";
@ -30,20 +30,19 @@ namespace Volo.Docs.Markdown
private const string MarkdownLinkRegExp = @"\[(.*?)\]\(((.*?)(\?(.*?))*?)\)";
private const string AnchorLinkRegExp = @"<a[^>]+href=\""(.*?)\""[^>]*>(.*)?</a>";
public virtual string Convert(ProjectDto project, DocumentWithDetailsDto document, string version,
string languageCode, string projectShortName = null)
public virtual string Convert(DocumentToHtmlConverterContext context)
{
if (document.Content.IsNullOrEmpty())
if (context.Document.Content.IsNullOrEmpty())
{
return document.Content;
return context.Document.Content;
}
var content = NormalizeLinks(
document.Content,
_uiOptions.SingleProjectMode.Enable ? projectShortName : projectShortName ?? project.ShortName,
version,
document.LocalDirectory,
!_uiOptions.MultiLanguageMode ? languageCode : languageCode ?? document.LanguageCode
context.Document.Content,
_uiOptions.SingleProjectMode.Enable ? context.ProjectShortName : context.ProjectShortName ?? context.Project.ShortName,
context.Version,
context.Document.LocalDirectory,
!_uiOptions.MultiLanguageMode ? context.LanguageCode : context.LanguageCode ?? context.Document.LanguageCode
);
var html = _markdownConverter.ConvertToHtml(content);

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

@ -7,14 +7,11 @@ 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;
using Microsoft.Extensions.Localization;
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;
@ -155,7 +152,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);
HasDownloadPdfPermission = await _permissionChecker.IsGrantedAsync(DocsCommonPermissions.Projects.PdfGeneration);
try
{
await SetProjectAsync();
@ -586,8 +583,8 @@ namespace Volo.Docs.Pages.Documents.Project
DocumentNavigationsDto = new DocumentNavigationsDto();
}
var converter = _documentToHtmlConverterFactory.Create(Document.Format ?? Project.Format);
var content = converter.Convert(Project, Document, GetSpecificVersionOrLatest(), LanguageCode, ProjectName);
var converter = _documentToHtmlConverterFactory.Create<DocumentToHtmlConverterContext>(Document.Format ?? Project.Format);
var content = converter.Convert(new DocumentToHtmlConverterContext(Project, Document, GetSpecificVersionOrLatest(), LanguageCode, ProjectName));
content = HtmlNormalizer.ReplaceImageSources(
content,
@ -782,8 +779,6 @@ namespace Volo.Docs.Pages.Documents.Project
}
var availableParameters = await _webDocumentSectionRenderer.GetAvailableParametersAsync(Document.Content);
var parameterCombinations = new List<Dictionary<string, string>>();
GenerateCombinations(0, availableParameters.Keys.ToList(),availableParameters,new Dictionary<string, string>(), parameterCombinations);
DocumentPreferences = new DocumentParametersDto
{
@ -829,27 +824,6 @@ namespace Volo.Docs.Pages.Documents.Project
AlternativeOptionLinkQueries = CollectAlternativeOptionLinksRecursively();
}
void GenerateCombinations(
int keyIndex,
List<string> parameterKeys ,
Dictionary<string,List<string>> parameters,
Dictionary<string, string> currentCombination,
List<Dictionary<string, string>> parameterCombinations )
{
if (keyIndex == parameterKeys.Count)
{
parameterCombinations.Add(new Dictionary<string, string>(currentCombination));
return;
}
var currentKey = parameterKeys[keyIndex];
foreach (var value in parameters[currentKey])
{
currentCombination[currentKey] = value;
GenerateCombinations(keyIndex + 1,parameterKeys, parameters, currentCombination,parameterCombinations);
}
}
private List<string> CollectAlternativeOptionLinksRecursively(int index = 0)
{

Loading…
Cancel
Save