diff --git a/.github/workflows/maven-build.yml b/.github/workflows/maven-build.yml index 47e8c0c8..203de009 100644 --- a/.github/workflows/maven-build.yml +++ b/.github/workflows/maven-build.yml @@ -24,7 +24,7 @@ jobs: with: fetch-depth: 0 - name: ๐Ÿงฑ Set up JDK and Maven - uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b # v4 + uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4 with: distribution: adopt java-version: 17 @@ -37,7 +37,7 @@ jobs: run: echo "cache_key=${{ runner.os }}-mvn-${{ hashFiles('**/pom.xml') }}-${{ github.sha }}" >> $GITHUB_OUTPUT - name: ๐Ÿ’พ Prepare cache using cache key id: prepare-cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4 with: path: | /home/runner/.m2 @@ -116,14 +116,14 @@ jobs: COM_SONATYPE_CENTRAL_POLARION_OPENSOURCE_GPG_PASSPHRASE: ${{ secrets.COM_SONATYPE_CENTRAL_POLARION_OPENSOURCE_GPG_PASSPHRASE }} steps: - name: ๐Ÿงฑ Set up JDK and Maven - uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b # v4 + uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4 with: distribution: adopt java-version: 17 gpg-private-key: ${{ secrets.COM_SONATYPE_CENTRAL_POLARION_OPENSOURCE_GPG_PRIVATE_KEY }} - name: ๐Ÿ’พ Restore cache using cache key id: restore-cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4 with: path: | /home/runner/.m2 @@ -146,13 +146,13 @@ jobs: GITHUB_TOKEN: ${{ github.token }} steps: - name: ๐Ÿงฑ Set up JDK and Maven - uses: actions/setup-java@8df1039502a15bceb9433410b1a100fbe190c53b # v4 + uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4 with: distribution: adopt java-version: 17 - name: ๐Ÿ’พ Restore cache using cache key id: restore-cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4 with: path: | /home/runner/.m2 diff --git a/.github/workflows/openapi-validation.yml b/.github/workflows/openapi-validation.yml index d912b9cb..d6d71392 100644 --- a/.github/workflows/openapi-validation.yml +++ b/.github/workflows/openapi-validation.yml @@ -1,16 +1,18 @@ --- name: openapi-validate -on: [push] +on: + push: + workflow_dispatch: jobs: check-openapi-definition: runs-on: ubuntu-latest name: Swagger Editor Validator Remote steps: - name: ๐Ÿ“„ Checkout the repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: fetch-depth: 0 - name: ๐Ÿงช Validate OpenAPI definition - uses: char0n/swagger-editor-validate@fe35d650e9fc674505607519d0d12ab4de5d415d # v1 + uses: swaggerexpert/swagger-editor-validate@e8e51dbc8c18e87f96b082b18a6a7cbd3c44abd8 # v1.4.2 with: definition-file: docs/openapi.json diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f690f681..d1a91ba1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -35,7 +35,7 @@ repos: language: pygrep types: [text] - repo: https://github.com/zricethezav/gitleaks - rev: v8.21.2 + rev: v8.22.1 hooks: - id: gitleaks - repo: https://github.com/grigoriev/pre-commit-check-git-user @@ -44,6 +44,6 @@ repos: - id: check-git-config-user-email args: [--templates, ^\S+\.\S+@sbb\.ch$] - repo: https://github.com/commitizen-tools/commitizen - rev: v4.0.0 + rev: v4.1.0 hooks: - id: commitizen diff --git a/CHANGELOG.md b/CHANGELOG.md index e5c5bec0..7cfa71b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,25 @@ # Changelog +## [8.0.0](https://github.com/SchweizerischeBundesbahnen/ch.sbb.polarion.extension.pdf-exporter/compare/v7.5.0...v8.0.0) (2025-01-08) + + +### โš  BREAKING CHANGES + +* style package weights don't work for bulk export widget ([#324](https://github.com/SchweizerischeBundesbahnen/ch.sbb.polarion.extension.pdf-exporter/issues/324)) +* support only Polarion 2410 ([#315](https://github.com/SchweizerischeBundesbahnen/ch.sbb.polarion.extension.pdf-exporter/issues/315)) + +### Features + +* refactor the object model and cover with unit tests ([#313](https://github.com/SchweizerischeBundesbahnen/ch.sbb.polarion.extension.pdf-exporter/issues/313)) ([dd955b0](https://github.com/SchweizerischeBundesbahnen/ch.sbb.polarion.extension.pdf-exporter/commit/dd955b0fb0d8577434a98d207921d4ef401762c5)) +* support only Polarion 2410 ([#315](https://github.com/SchweizerischeBundesbahnen/ch.sbb.polarion.extension.pdf-exporter/issues/315)) ([3725e4e](https://github.com/SchweizerischeBundesbahnen/ch.sbb.polarion.extension.pdf-exporter/commit/3725e4eb6b67adb36a803164dbec2c5091c59f46)), closes [#299](https://github.com/SchweizerischeBundesbahnen/ch.sbb.polarion.extension.pdf-exporter/issues/299) +* support WeasyPrint Service v63.1.0 ([#319](https://github.com/SchweizerischeBundesbahnen/ch.sbb.polarion.extension.pdf-exporter/issues/319)) ([a2ebcfa](https://github.com/SchweizerischeBundesbahnen/ch.sbb.polarion.extension.pdf-exporter/commit/a2ebcfac5e4ef9b325b8a85d5600d6903368f9cf)), closes [#300](https://github.com/SchweizerischeBundesbahnen/ch.sbb.polarion.extension.pdf-exporter/issues/300) + + +### Bug Fixes + +* **deps:** update dependency net.bytebuddy:byte-buddy to v1.15.11 ([#322](https://github.com/SchweizerischeBundesbahnen/ch.sbb.polarion.extension.pdf-exporter/issues/322)) ([77309b0](https://github.com/SchweizerischeBundesbahnen/ch.sbb.polarion.extension.pdf-exporter/commit/77309b01d2915b2f963d3fb9487a29e3309429e8)) +* style package weights don't work for bulk export widget ([#324](https://github.com/SchweizerischeBundesbahnen/ch.sbb.polarion.extension.pdf-exporter/issues/324)) ([3e7abb9](https://github.com/SchweizerischeBundesbahnen/ch.sbb.polarion.extension.pdf-exporter/commit/3e7abb92ef5379dbed30b57f114e19d918a346fc)), closes [#317](https://github.com/SchweizerischeBundesbahnen/ch.sbb.polarion.extension.pdf-exporter/issues/317) + ## [7.5.0](https://github.com/SchweizerischeBundesbahnen/ch.sbb.polarion.extension.pdf-exporter/compare/v7.4.0...v7.5.0) (2024-12-10) diff --git a/README.md b/README.md index c30f9d06..c319d39f 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,16 @@ First of all you need to inject appropriate JavaScript code into Polarion: 5. Save changes by clicking ๐Ÿ’พ Save Then open a project, its Live Report you wish to export, and click "Expand Tools" on top of the page. -As a result report's toolbar will appear. Click "Edit" button in a toolbar, as a result the report will be switched into an edit mode. Add an empty region on top of the report, place cursor there, choose "Generic" tag on "Widgets" sidebar on right hand side of the page, find "Export to PDF Button" widget there and click it to add to the report. Then save a report clicking ๐Ÿ’พ in a toolbar and then return to a view mode clicking "Back" button. When you click "Export to PDF" button just added to the report, PDF Exporter view will be opened in a popup and you will be able to proceed with exporting the report to PDF. Be aware that in report's context limited set of properties are available for configuration in PDF popup, the rest of them are relevant only in Live Document context. +As a result report's toolbar will appear. Click "Edit" button in a toolbar, as a result the report will be switched into an edit mode. Add an empty region on top of the report, place cursor there, choose "PDF Export" tag on "Widgets" sidebar on right hand side of the page, find "Export to PDF Button" widget there and click it to add to the report. Then save a report clicking ๐Ÿ’พ in a toolbar and then return to a view mode clicking "Back" button. When you click "Export to PDF" button just added to the report, PDF Exporter view will be opened in a popup and you will be able to proceed with exporting the report to PDF. Be aware that in report's context limited set of properties are available for configuration in PDF popup, the rest of them are relevant only in Live Document context. + +### Bulk PDF Export +There is a possibility to export multiple documents in one run. This can be achieved with help of special widget. + +Open a project where you wish do bulk export. Open its Default Space in Documents & Pages and create Live Report with name "Bulk PDF Export". Newly created report will be opened in edit mode. Change its title to "Bulk PDF Export" or whatever value you wish, then place cursor in a region you wish the widget to appear, choose "PDF Export" tag on "Widgets" sidebar on right hand side of the page, find "Bulk PDF Export" widget there and click it to add to the report. Then save a report clicking ๐Ÿ’พ in a toolbar and then return to a view mode clicking "Back" button. + +As a result you will see a table listing all Live Documents in a current project (with pagination). This widget is an extension of standard Polarion table with a data set and can be configured the similar way, for example you can specify a Lucene query to narrow list of displaying documents, change set of columns or modify soring rule. + +When you are ready with filtering, sorting etc. of documents you can select multiple documents to be exported, ticking appropriate checkboxes and click Export to PDF button, a popup with export configuration will be opened. Select appropriate configuration and click Export button, new popup will be opened showing progress of exporting. When export is finished click Close button to hide popup. Also, you can click Stop button when export is in progress, in this case document which is exporting at the moment will finish and the rest which were pending will be cancelled. ### Configuring logs @@ -232,7 +241,8 @@ All good so far. ### Upgrade from version 7.x.x to 8.0.0 -In version 8.0.0 support for Polarion 2404 and older has been dropped. This extensions supports only Polarion 2410. +In version 8.0.0 support for Polarion 2404 and older has been dropped. This extension supports only Polarion 2410. +Recommended version of WeasyPrint Service is 63.1.0. ### Upgrade from version 6.x.x to 7.0.0 diff --git a/docs/openapi.json b/docs/openapi.json index 6b4a1939..c6af57d6 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -900,11 +900,6 @@ "description": "Images deleted successfully" } }, - "security": [ - { - "bearerAuth": [] - } - ], "summary": "Deletes images in SVN linked to specified cover page within specified scope (global or certain project)", "tags": [ "Settings" @@ -919,11 +914,6 @@ "description": "Template names retrieved successfully" } }, - "security": [ - { - "bearerAuth": [] - } - ], "summary": "Get list of cover page predefined template names", "tags": [ "Settings" @@ -955,11 +945,6 @@ "description": "Template persisted successfully" } }, - "security": [ - { - "bearerAuth": [] - } - ], "summary": "Persist content of cover page predefined template", "tags": [ "Settings" @@ -1005,11 +990,6 @@ "description": "Localization file downloaded successfully" } }, - "security": [ - { - "bearerAuth": [] - } - ], "summary": "Downloads localization values by name of localization settings", "tags": [ "Settings" @@ -1054,11 +1034,6 @@ "description": "Localization file uploaded successfully" } }, - "security": [ - { - "bearerAuth": [] - } - ], "summary": "Uploads localization values", "tags": [ "Settings" @@ -1066,42 +1041,28 @@ } }, "/api/settings/style-package/suitable-names": { - "get": { + "post": { "operationId": "getSuitableStylePackageNames", - "parameters": [ - { - "in": "query", - "name": "projectId", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "spaceId", - "schema": { - "type": "string" + "requestBody": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/DocIdentifier" + }, + "type": "array" + } } }, - { - "in": "query", - "name": "documentName", - "schema": { - "type": "string" - } - } - ], + "description": "List of document identifiers", + "required": true + }, "responses": { "200": { "description": "Successfully retrieved the list of style package names" } }, - "security": [ - { - "bearerAuth": [] - } - ], - "summary": "Get list of style packages suitable for a document", + "summary": "Get list of style packages suitable for the specified list of documents (sorted by weight)", "tags": [ "Settings" ] @@ -1124,11 +1085,6 @@ "description": "Successfully retrieved the list of style package names with the weight information" } }, - "security": [ - { - "bearerAuth": [] - } - ], "summary": "Get full list of available style packages for the specific scope with the weight information", "tags": [ "Settings" @@ -1158,11 +1114,6 @@ "description": "default response" } }, - "security": [ - { - "bearerAuth": [] - } - ], "summary": "Update weight information for the provided style packages", "tags": [ "Settings" @@ -1665,6 +1616,24 @@ }, "type": "object" }, + "DocIdentifier": { + "description": "Unique document identifier data", + "properties": { + "documentName": { + "description": "Document name", + "type": "string" + }, + "projectId": { + "description": "Project ID", + "type": "string" + }, + "spaceId": { + "description": "Space ID", + "type": "string" + } + }, + "type": "object" + }, "DocumentCollectionEntry": { "description": "Details about document from collection", "properties": { diff --git a/pom.xml b/pom.xml index ca4b1356..9b1e4716 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ ch.sbb.polarion.extension.pdf-exporter - 7.5.1-SNAPSHOT + 8.0.1-SNAPSHOT jar Polarion ALM extension to convert Documents to PDF files @@ -46,7 +46,7 @@ 3.0.3 1.47.0 - 1.15.10 + 1.15.11 1.7.36 1.20.4 diff --git a/src/main/java/ch/sbb/polarion/extension/pdf_exporter/PdfExporterFormExtension.java b/src/main/java/ch/sbb/polarion/extension/pdf_exporter/PdfExporterFormExtension.java index 463f143e..76120296 100644 --- a/src/main/java/ch/sbb/polarion/extension/pdf_exporter/PdfExporterFormExtension.java +++ b/src/main/java/ch/sbb/polarion/extension/pdf_exporter/PdfExporterFormExtension.java @@ -9,6 +9,7 @@ import ch.sbb.polarion.extension.pdf_exporter.rest.model.conversion.DocumentType; import ch.sbb.polarion.extension.pdf_exporter.rest.model.conversion.ExportParams; import ch.sbb.polarion.extension.pdf_exporter.rest.model.settings.localization.Language; +import ch.sbb.polarion.extension.pdf_exporter.rest.model.settings.stylepackage.DocIdentifier; import ch.sbb.polarion.extension.pdf_exporter.rest.model.settings.stylepackage.StylePackageModel; import ch.sbb.polarion.extension.pdf_exporter.service.PdfExporterPolarionService; import ch.sbb.polarion.extension.pdf_exporter.service.PolarionBaselineExecutor; @@ -173,7 +174,7 @@ private Collection getSuitableStylePackages(@NotNull IModule module } else { documentName = locationPath; } - return polarionService.getSuitableStylePackages(module.getProject().getId(), spaceId, documentName); + return polarionService.getSuitableStylePackages(List.of(new DocIdentifier(module.getProject().getId(), spaceId, documentName))); } private Collection getSettingNames(@NotNull String featureName, @NotNull String scope) { diff --git a/src/main/java/ch/sbb/polarion/extension/pdf_exporter/rest/controller/SettingsApiController.java b/src/main/java/ch/sbb/polarion/extension/pdf_exporter/rest/controller/SettingsApiController.java index e270b41a..9c109fc4 100644 --- a/src/main/java/ch/sbb/polarion/extension/pdf_exporter/rest/controller/SettingsApiController.java +++ b/src/main/java/ch/sbb/polarion/extension/pdf_exporter/rest/controller/SettingsApiController.java @@ -3,6 +3,7 @@ import ch.sbb.polarion.extension.generic.rest.filter.Secured; import ch.sbb.polarion.extension.generic.service.PolarionService; import ch.sbb.polarion.extension.generic.settings.SettingName; +import ch.sbb.polarion.extension.pdf_exporter.rest.model.settings.stylepackage.DocIdentifier; import ch.sbb.polarion.extension.pdf_exporter.rest.model.settings.stylepackage.StylePackageWeightInfo; import org.glassfish.jersey.media.multipart.FormDataBodyPart; @@ -44,8 +45,8 @@ public void deleteImages(String coverPageName, String scope) { } @Override - public Collection getSuitableStylePackageNames(String projectId, String spaceId, String documentName) { - return polarionService.callPrivileged(() -> super.getSuitableStylePackageNames(projectId, spaceId, documentName)); + public Collection getSuitableStylePackageNames(List docIdentifiers) { + return polarionService.callPrivileged(() -> super.getSuitableStylePackageNames(docIdentifiers)); } @Override diff --git a/src/main/java/ch/sbb/polarion/extension/pdf_exporter/rest/controller/SettingsInternalController.java b/src/main/java/ch/sbb/polarion/extension/pdf_exporter/rest/controller/SettingsInternalController.java index 50d939b5..52c4dcda 100644 --- a/src/main/java/ch/sbb/polarion/extension/pdf_exporter/rest/controller/SettingsInternalController.java +++ b/src/main/java/ch/sbb/polarion/extension/pdf_exporter/rest/controller/SettingsInternalController.java @@ -4,6 +4,7 @@ import ch.sbb.polarion.extension.generic.settings.SettingName; import ch.sbb.polarion.extension.pdf_exporter.rest.model.settings.coverpage.CoverPageModel; import ch.sbb.polarion.extension.pdf_exporter.rest.model.settings.localization.LocalizationModel; +import ch.sbb.polarion.extension.pdf_exporter.rest.model.settings.stylepackage.DocIdentifier; import ch.sbb.polarion.extension.pdf_exporter.rest.model.settings.stylepackage.StylePackageWeightInfo; import ch.sbb.polarion.extension.pdf_exporter.service.PdfExporterPolarionService; import ch.sbb.polarion.extension.pdf_exporter.settings.CoverPageSettings; @@ -16,7 +17,6 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.parameters.RequestBody; import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; import net.sf.okapi.lib.xliff2.reader.XLIFFReader; import org.glassfish.jersey.media.multipart.FormDataBodyPart; @@ -46,7 +46,6 @@ @Hidden @Path("/internal") @Tag(name = "Settings", description = "Operations related to PDF-exporter settings management") -@SecurityRequirement(name = "bearerAuth") public class SettingsInternalController { private final PdfExporterPolarionService pdfExporterPolarionService = new PdfExporterPolarionService(); @@ -145,20 +144,27 @@ public void deleteImages(@PathParam("name") String coverPageName, @QueryParam("s new CoverPageSettings().deleteCoverPageImages(coverPageName, scope); } - @GET + @POST @Path("/settings/style-package/suitable-names") + @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) @Tag(name = "Settings") - @Operation(summary = "Get list of style packages suitable for a document", + @Operation(summary = "Get list of style packages suitable for the specified list of documents (sorted by weight)", + requestBody = @RequestBody(description = "List of document identifiers", + required = true, + content = @Content(array = @ArraySchema(schema = @Schema(implementation = DocIdentifier.class)), + mediaType = MediaType.APPLICATION_JSON + ) + ), responses = { @ApiResponse(responseCode = "200", description = "Successfully retrieved the list of style package names") } ) - public Collection getSuitableStylePackageNames(@QueryParam("projectId") String projectId, @QueryParam("spaceId") String spaceId, @QueryParam("documentName") String documentName) { - if (spaceId == null || documentName == null) { - throw new BadRequestException("Parameters 'spaceId' and 'documentName' are required'"); + public Collection getSuitableStylePackageNames(List docIdentifiers) { + if (docIdentifiers.isEmpty()) { + throw new BadRequestException("At least one document identifier required"); } - return pdfExporterPolarionService.getSuitableStylePackages(projectId, spaceId, documentName); + return pdfExporterPolarionService.getSuitableStylePackages(docIdentifiers); } @GET diff --git a/src/main/java/ch/sbb/polarion/extension/pdf_exporter/rest/model/settings/stylepackage/DocIdentifier.java b/src/main/java/ch/sbb/polarion/extension/pdf_exporter/rest/model/settings/stylepackage/DocIdentifier.java new file mode 100644 index 00000000..1d90ef3d --- /dev/null +++ b/src/main/java/ch/sbb/polarion/extension/pdf_exporter/rest/model/settings/stylepackage/DocIdentifier.java @@ -0,0 +1,31 @@ +package ch.sbb.polarion.extension.pdf_exporter.rest.model.settings.stylepackage; + +import com.fasterxml.jackson.annotation.JsonInclude; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.jetbrains.annotations.NotNull; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@ToString +@EqualsAndHashCode(callSuper = false) +@JsonInclude(JsonInclude.Include.NON_NULL) +@Schema(description = "Unique document identifier data") +public class DocIdentifier { + + @Schema(description = "Project ID") + private @NotNull String projectId; + + @Schema(description = "Space ID") + private @NotNull String spaceId; + + @Schema(description = "Document name") + private @NotNull String documentName; +} diff --git a/src/main/java/ch/sbb/polarion/extension/pdf_exporter/rest/model/settings/stylepackage/StylePackageWeightInfo.java b/src/main/java/ch/sbb/polarion/extension/pdf_exporter/rest/model/settings/stylepackage/StylePackageWeightInfo.java index 6d59b8e3..c106aa59 100644 --- a/src/main/java/ch/sbb/polarion/extension/pdf_exporter/rest/model/settings/stylepackage/StylePackageWeightInfo.java +++ b/src/main/java/ch/sbb/polarion/extension/pdf_exporter/rest/model/settings/stylepackage/StylePackageWeightInfo.java @@ -18,19 +18,13 @@ @JsonInclude(JsonInclude.Include.NON_NULL) public class StylePackageWeightInfo { - @Schema( - description = "The name of the setting" - ) + @Schema(description = "The name of the setting") private String name; - @Schema( - description = "The scope of the setting" - ) + @Schema(description = "The scope of the setting") private String scope; - @Schema( - description = "The weight of the setting" - ) + @Schema(description = "The weight of the setting") private Float weight; } diff --git a/src/main/java/ch/sbb/polarion/extension/pdf_exporter/service/PdfExporterPolarionService.java b/src/main/java/ch/sbb/polarion/extension/pdf_exporter/service/PdfExporterPolarionService.java index 9ce868be..f97365c7 100644 --- a/src/main/java/ch/sbb/polarion/extension/pdf_exporter/service/PdfExporterPolarionService.java +++ b/src/main/java/ch/sbb/polarion/extension/pdf_exporter/service/PdfExporterPolarionService.java @@ -1,12 +1,14 @@ package ch.sbb.polarion.extension.pdf_exporter.service; import ch.sbb.polarion.extension.generic.service.PolarionService; +import ch.sbb.polarion.extension.generic.settings.GenericNamedSettings; import ch.sbb.polarion.extension.generic.settings.NamedSettingsRegistry; import ch.sbb.polarion.extension.generic.settings.SettingId; import ch.sbb.polarion.extension.generic.settings.SettingName; import ch.sbb.polarion.extension.generic.util.ScopeUtils; import ch.sbb.polarion.extension.pdf_exporter.rest.model.attachments.TestRunAttachment; import ch.sbb.polarion.extension.pdf_exporter.rest.model.collections.DocumentCollectionEntry; +import ch.sbb.polarion.extension.pdf_exporter.rest.model.settings.stylepackage.DocIdentifier; import ch.sbb.polarion.extension.pdf_exporter.rest.model.settings.stylepackage.StylePackageModel; import ch.sbb.polarion.extension.pdf_exporter.rest.model.settings.stylepackage.StylePackageWeightInfo; import ch.sbb.polarion.extension.pdf_exporter.settings.StylePackageSettings; @@ -100,10 +102,16 @@ public void updateStylePackagesWeights(@NotNull List wei } } - public Collection getSuitableStylePackages(@Nullable String projectId, @NotNull String spaceId, @NotNull String documentName) { + public Collection getSuitableStylePackages(@NotNull List docIdentifiers) { + if (docIdentifiers.isEmpty()) { + return new ArrayList<>(); + } StylePackageSettings stylePackageSettings = (StylePackageSettings) NamedSettingsRegistry.INSTANCE.getByFeatureName(StylePackageSettings.FEATURE_NAME); - Collection stylePackageNames = stylePackageSettings.readNames(ScopeUtils.getScopeFromProject(projectId)); - List names = stylePackageNames.stream().filter(stylePackageName -> isStylePackageSuitable(projectId, spaceId, documentName, stylePackageSettings, stylePackageName)).toList(); + // if user mixes items from different projects then we can use only 'default'-level style packages + String stylePackageScope = ScopeUtils.getScopeFromProject(docIdentifiers.stream().map(DocIdentifier::getProjectId).distinct().count() == 1 ? docIdentifiers.get(0).getProjectId() : GenericNamedSettings.DEFAULT_SCOPE); + Collection stylePackageNames = stylePackageSettings.readNames(stylePackageScope); + List names = stylePackageNames.stream().filter(stylePackageName -> docIdentifiers.stream().allMatch( + i -> isStylePackageSuitable(i.getProjectId(), i.getSpaceId(), i.getDocumentName(), stylePackageSettings, stylePackageScope, stylePackageName))).toList(); Map weightsMap = new HashMap<>(); names.forEach(name -> weightsMap.put(name, stylePackageSettings.read(name.getScope(), SettingId.fromName(name.getName()), null).getWeight())); return names.stream().sorted((o1, o2) -> { @@ -114,8 +122,8 @@ public Collection getSuitableStylePackages(@Nullable String project @SuppressWarnings("unchecked") private boolean isStylePackageSuitable(@Nullable String projectId, @NotNull String spaceId, @NotNull String documentName, - @NotNull StylePackageSettings stylePackageSettings, @NotNull SettingName stylePackageName) { - StylePackageModel model = stylePackageSettings.read(ScopeUtils.getScopeFromProject(projectId), SettingId.fromName(stylePackageName.getName()), null); + @NotNull StylePackageSettings stylePackageSettings, @NotNull String stylePackageScope, @NotNull SettingName stylePackageName) { + StylePackageModel model = stylePackageSettings.read(stylePackageScope, SettingId.fromName(stylePackageName.getName()), null); if (StringUtils.isEmpty(model.getMatchingQuery())) { return true; @@ -190,15 +198,13 @@ private boolean sameDocument(@Nullable String projectId, @NotNull String spaceId .toList(); for (IModule module : modules) { - String[] locationParts = module.getModuleLocation().getLocationPath().split("/"); - if (locationParts.length == 2) { - result.add(new DocumentCollectionEntry( - module.getProjectId(), - locationParts[0], - locationParts[1], - module.getRevision() - )); - } + DocumentCollectionEntry documentCollectionEntry = new DocumentCollectionEntry( + module.getProjectId(), + module.getModuleFolder(), + module.getModuleName(), + module.getRevision() + ); + result.add(documentCollectionEntry); } return result; diff --git a/src/main/java/ch/sbb/polarion/extension/pdf_exporter/widgets/BulkPdfExportWidgetRenderer.java b/src/main/java/ch/sbb/polarion/extension/pdf_exporter/widgets/BulkPdfExportWidgetRenderer.java index e6615126..40875b0e 100644 --- a/src/main/java/ch/sbb/polarion/extension/pdf_exporter/widgets/BulkPdfExportWidgetRenderer.java +++ b/src/main/java/ch/sbb/polarion/extension/pdf_exporter/widgets/BulkPdfExportWidgetRenderer.java @@ -1,6 +1,7 @@ package ch.sbb.polarion.extension.pdf_exporter.widgets; import ch.sbb.polarion.extension.pdf_exporter.rest.model.conversion.DocumentType; +import com.polarion.alm.projects.model.IUniqueObject; import com.polarion.alm.server.api.model.rp.widget.AbstractWidgetRenderer; import com.polarion.alm.server.api.model.rp.widget.BottomQueryLinksBuilder; import com.polarion.alm.shared.api.model.ModelObject; @@ -164,6 +165,7 @@ private void renderItem(@NotNull HtmlContentBuilder builder, @NotNull ModelObjec checkbox.attributes() .byName("type", "checkbox") .byName("data-type", item.getOldApi().getPrototype().getName()) + .byName("data-project", getProject(item)) .byName("data-space", getSpace(item)) .byName("data-id", getValue(item, "id")) .className("export-item"); @@ -193,6 +195,10 @@ private String getSpace(@NotNull ModelObject item) { } } + private String getProject(@NotNull ModelObject item) { + return ((IUniqueObject) item.getOldApi()).getProjectId(); + } + private String getValue(@NotNull ModelObject item, @NotNull String fieldName) { Object fieldValue = item.getOldApi().getValue(fieldName); return fieldValue == null ? "" : fieldValue.toString(); diff --git a/src/main/resources/versions.properties b/src/main/resources/versions.properties index b29fee41..f775d5f4 100644 --- a/src/main/resources/versions.properties +++ b/src/main/resources/versions.properties @@ -1 +1 @@ -weasyprint-service.version=62.4.6 \ No newline at end of file +weasyprint-service.version=63.1.0 diff --git a/src/main/resources/webapp/pdf-exporter/js/bulk-pdf-exporter.js b/src/main/resources/webapp/pdf-exporter/js/bulk-pdf-exporter.js index a7370866..d67d9ee3 100644 --- a/src/main/resources/webapp/pdf-exporter/js/bulk-pdf-exporter.js +++ b/src/main/resources/webapp/pdf-exporter/js/bulk-pdf-exporter.js @@ -65,6 +65,7 @@ const BulkPdfExporter = { const div = document.createElement("div"); div.className = "export-item paused"; div.dataset["type"] = selectedCheckbox.dataset["type"]; + div.dataset["project"] = selectedCheckbox.dataset["project"]; div.dataset["space"] = selectedCheckbox.dataset["space"]; div.dataset["id"] = selectedCheckbox.dataset["id"]; @@ -211,6 +212,7 @@ const BulkPdfExporter = { currentItem.classList.add("in-progress"); const documentType = this.getDocumentType(currentItem.dataset["type"]); + this.exportParams["projectId"] = `${currentItem.dataset["project"]}`; this.exportParams["documentType"] = documentType; const documentId = currentItem.dataset["id"]; if (documentType === ExportParams.DocumentType.TEST_RUN) { diff --git a/src/main/resources/webapp/pdf-exporter/js/pdf-exporter.js b/src/main/resources/webapp/pdf-exporter/js/pdf-exporter.js index 19368060..243894e8 100644 --- a/src/main/resources/webapp/pdf-exporter/js/pdf-exporter.js +++ b/src/main/resources/webapp/pdf-exporter/js/pdf-exporter.js @@ -141,11 +141,13 @@ const PdfExporter = { }); }, - loadSettingNames: function ({setting, scope, selectElement, customUrl}) { + loadSettingNames: function ({setting, scope, selectElement, customUrl, customMethod, customBody, customContentType}) { return new Promise((resolve, reject) => { this.callAsync({ - method: "GET", + method: customMethod ? customMethod : "GET", url: customUrl ? customUrl : `/polarion/pdf-exporter/rest/internal/settings/${setting}/names?scope=${scope}`, + body: customBody ? customBody : undefined, + contentType: customContentType ? customContentType : undefined, responseType: "json", }).then(({response}) => { selectElement.innerHTML = ""; // Clear previously loaded content @@ -248,20 +250,26 @@ const PdfExporter = { }, loadStylePackages: function (exportContext) { - let stylePackagesUrl; + let stylePackagesUrl = `/polarion/pdf-exporter/rest/internal/settings/style-package/suitable-names`; + const docIdentifiers = []; if (exportContext.getExportType() === ExportParams.ExportType.BULK) { - stylePackagesUrl = `/polarion/pdf-exporter/rest/internal/settings/style-package/names?scope=${exportContext.getScope()}`; + this.exportContext.bulkExportWidget.querySelectorAll('input[type="checkbox"]:not(.export-all):checked').forEach((selectedCheckbox) => { + docIdentifiers.push({ + projectId: selectedCheckbox.dataset["project"], spaceId: selectedCheckbox.dataset["space"], documentName: selectedCheckbox.dataset["id"] + }); + }); } else { - stylePackagesUrl = `/polarion/pdf-exporter/rest/internal/settings/style-package/suitable-names` - + `?spaceId=${exportContext.getSpaceId()}&documentName=${exportContext.getDocumentName()}`; - if (exportContext.getProjectId()) { - stylePackagesUrl += `&projectId=${exportContext.getProjectId()}` - } + docIdentifiers.push({ + projectId: `${exportContext.getProjectId()}`, spaceId: `${exportContext.getSpaceId()}`, documentName: `${exportContext.getDocumentName()}` + }); } return this.loadSettingNames({ customUrl: stylePackagesUrl, - selectElement: document.getElementById("popup-style-package-select") + selectElement: document.getElementById("popup-style-package-select"), + customMethod: 'POST', + customBody: JSON.stringify(docIdentifiers), + customContentType: 'application/json' }).then(() => { const stylePackageSelect = document.getElementById("popup-style-package-select"); const valueToPreselect = SbbCommon.getCookie(SELECTED_STYLE_PACKAGE_COOKIE); diff --git a/src/test/java/ch/sbb/polarion/extension/pdf_exporter/service/PdfExporterPolarionServiceTest.java b/src/test/java/ch/sbb/polarion/extension/pdf_exporter/service/PdfExporterPolarionServiceTest.java index 213e6145..617921b0 100644 --- a/src/test/java/ch/sbb/polarion/extension/pdf_exporter/service/PdfExporterPolarionServiceTest.java +++ b/src/test/java/ch/sbb/polarion/extension/pdf_exporter/service/PdfExporterPolarionServiceTest.java @@ -7,6 +7,7 @@ import ch.sbb.polarion.extension.generic.util.ScopeUtils; import ch.sbb.polarion.extension.pdf_exporter.rest.model.attachments.TestRunAttachment; import ch.sbb.polarion.extension.pdf_exporter.rest.model.collections.DocumentCollectionEntry; +import ch.sbb.polarion.extension.pdf_exporter.rest.model.settings.stylepackage.DocIdentifier; import ch.sbb.polarion.extension.pdf_exporter.rest.model.settings.stylepackage.StylePackageModel; import ch.sbb.polarion.extension.pdf_exporter.rest.model.settings.stylepackage.StylePackageWeightInfo; import ch.sbb.polarion.extension.pdf_exporter.settings.StylePackageSettings; @@ -25,7 +26,6 @@ import com.polarion.platform.IPlatformService; import com.polarion.platform.security.ISecurityService; import com.polarion.platform.service.repository.IRepositoryService; -import com.polarion.subterra.base.location.ILocation; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.MockedConstruction; @@ -134,9 +134,21 @@ void testUpdateStylePackagesWeights() { @Test void testGetSuitableStylePackages() { + Collection result = service.getSuitableStylePackages(List.of()); + assertNotNull(result); + assertTrue(result.isEmpty()); + String projectId = "someProjectId"; String spaceId = "someSpaceId"; String documentName = "documentName"; + Collection defaultSettingNames = List.of( + SettingName.builder().id("d1").name("default1").scope("").build(), + SettingName.builder().id("d2").name("default2").scope("").build() + ); + StylePackageModel defaultMockModel1 = mock(StylePackageModel.class); + when(defaultMockModel1.getWeight()).thenReturn(10f); + StylePackageModel defaultMockModel2 = mock(StylePackageModel.class); + when(defaultMockModel2.getWeight()).thenReturn(16f); Collection settingNames = List.of( SettingName.builder().id("id1").name("name1").scope("project/someProjectId/").build(), SettingName.builder().id("id4").name("name4").scope("project/someProjectId/").build(), @@ -155,18 +167,26 @@ void testGetSuitableStylePackages() { StylePackageModel mockModel5 = mock(StylePackageModel.class); when(mockModel5.getWeight()).thenReturn(50.0f); + when(stylePackageSettings.readNames("")).thenReturn(defaultSettingNames); when(stylePackageSettings.readNames(ScopeUtils.getScopeFromProject(projectId))).thenReturn(settingNames); + when(stylePackageSettings.read(eq(""), eq(SettingId.fromName("default1")), isNull())).thenReturn(defaultMockModel1); + when(stylePackageSettings.read(eq(""), eq(SettingId.fromName("default2")), isNull())).thenReturn(defaultMockModel2); when(stylePackageSettings.read(eq("project/someProjectId/"), eq(SettingId.fromName("name1")), isNull())).thenReturn(mockModel1); when(stylePackageSettings.read(eq("project/someProjectId/"), eq(SettingId.fromName("name2")), isNull())).thenReturn(mockModel2); when(stylePackageSettings.read(eq("project/someProjectId/"), eq(SettingId.fromName("name3")), isNull())).thenReturn(mockModel3); when(stylePackageSettings.read(eq("project/someProjectId/"), eq(SettingId.fromName("name4")), isNull())).thenReturn(mockModel4); when(stylePackageSettings.read(eq("project/someProjectId/"), eq(SettingId.fromName("name5")), isNull())).thenReturn(mockModel5); - Collection result = service.getSuitableStylePackages(projectId, spaceId, documentName); + result = service.getSuitableStylePackages(List.of(new DocIdentifier(projectId, spaceId, documentName))); assertNotNull(result); assertEquals(5, result.size()); assertEquals(List.of("name2", "name3", "name4", "name5", "name1"), result.stream().map(SettingName::getName).toList()); + + result = service.getSuitableStylePackages(List.of(new DocIdentifier(projectId, spaceId, documentName), new DocIdentifier("anotherProject", spaceId, documentName))); + assertNotNull(result); + assertEquals(2, result.size()); + assertEquals(List.of("default2", "default1"), result.stream().map(SettingName::getName).toList()); } @Test @@ -248,18 +268,16 @@ void testGetDocumentsFromCollection() { IModule mockModule1 = mock(IModule.class); IModule mockModule2 = mock(IModule.class); - ILocation mockLocation1 = mock(ILocation.class); - ILocation mockLocation2 = mock(ILocation.class); - - when(mockModule1.getModuleLocation()).thenReturn(mockLocation1); + when(mockModule1.getProjectId()).thenReturn(projectId); + when(mockModule1.getModuleFolder()).thenReturn("space 1"); + when(mockModule1.getModuleName()).thenReturn("test Module1"); when(mockModule1.getRevision()).thenReturn("1"); - when(mockModule2.getModuleLocation()).thenReturn(mockLocation2); + when(mockModule2.getProjectId()).thenReturn(projectId); + when(mockModule2.getModuleFolder()).thenReturn("_default"); + when(mockModule2.getModuleName()).thenReturn("test Module2"); when(mockModule2.getRevision()).thenReturn("2"); - when(mockLocation1.getLocationPath()).thenReturn("space 1/test Module1"); - when(mockLocation2.getLocationPath()).thenReturn("_default/test Module2"); - IBaselineCollectionElement mockElement1 = mock(IBaselineCollectionElement.class); IBaselineCollectionElement mockElement2 = mock(IBaselineCollectionElement.class); diff --git a/src/test/resources/weasyprint/png/testConverterComplexWithTitle_page_0.png b/src/test/resources/weasyprint/png/testConverterComplexWithTitle_page_0.png index 91ab2383..2fc48e78 100644 Binary files a/src/test/resources/weasyprint/png/testConverterComplexWithTitle_page_0.png and b/src/test/resources/weasyprint/png/testConverterComplexWithTitle_page_0.png differ diff --git a/src/test/resources/weasyprint/png/testConverterComplexWithTitle_page_1.png b/src/test/resources/weasyprint/png/testConverterComplexWithTitle_page_1.png index 2ba6ba64..5bad7e7c 100644 Binary files a/src/test/resources/weasyprint/png/testConverterComplexWithTitle_page_1.png and b/src/test/resources/weasyprint/png/testConverterComplexWithTitle_page_1.png differ diff --git a/src/test/resources/weasyprint/png/testConverterComplexWithTitle_page_2.png b/src/test/resources/weasyprint/png/testConverterComplexWithTitle_page_2.png index 0952ae4b..03cee31b 100644 Binary files a/src/test/resources/weasyprint/png/testConverterComplexWithTitle_page_2.png and b/src/test/resources/weasyprint/png/testConverterComplexWithTitle_page_2.png differ diff --git a/src/test/resources/weasyprint/png/testConverterComplexWithTitle_page_3.png b/src/test/resources/weasyprint/png/testConverterComplexWithTitle_page_3.png index 355a0ec9..a98d224c 100644 Binary files a/src/test/resources/weasyprint/png/testConverterComplexWithTitle_page_3.png and b/src/test/resources/weasyprint/png/testConverterComplexWithTitle_page_3.png differ diff --git a/src/test/resources/weasyprint/png/testConverterSimple_page_0.png b/src/test/resources/weasyprint/png/testConverterSimple_page_0.png index 728d2f57..316e1ca6 100644 Binary files a/src/test/resources/weasyprint/png/testConverterSimple_page_0.png and b/src/test/resources/weasyprint/png/testConverterSimple_page_0.png differ diff --git a/src/test/resources/weasyprint/png/testConverterSpecialSymbols_page_0.png b/src/test/resources/weasyprint/png/testConverterSpecialSymbols_page_0.png index 1e77ab26..20ae817e 100644 Binary files a/src/test/resources/weasyprint/png/testConverterSpecialSymbols_page_0.png and b/src/test/resources/weasyprint/png/testConverterSpecialSymbols_page_0.png differ diff --git a/src/test/resources/weasyprint/png/testConverterSpecialSymbols_page_1.png b/src/test/resources/weasyprint/png/testConverterSpecialSymbols_page_1.png index 0500372f..5e0c35b9 100644 Binary files a/src/test/resources/weasyprint/png/testConverterSpecialSymbols_page_1.png and b/src/test/resources/weasyprint/png/testConverterSpecialSymbols_page_1.png differ diff --git a/src/test/resources/weasyprint/png/testConverterSpecialSymbols_page_2.png b/src/test/resources/weasyprint/png/testConverterSpecialSymbols_page_2.png index 5c409ba9..94739cad 100644 Binary files a/src/test/resources/weasyprint/png/testConverterSpecialSymbols_page_2.png and b/src/test/resources/weasyprint/png/testConverterSpecialSymbols_page_2.png differ diff --git a/src/test/resources/weasyprint/png/testConverterSvgImage_page_0.png b/src/test/resources/weasyprint/png/testConverterSvgImage_page_0.png index 50e301cd..98f7e87f 100644 Binary files a/src/test/resources/weasyprint/png/testConverterSvgImage_page_0.png and b/src/test/resources/weasyprint/png/testConverterSvgImage_page_0.png differ diff --git a/src/test/resources/weasyprint/png/testConverterWiki_page_0.png b/src/test/resources/weasyprint/png/testConverterWiki_page_0.png index a77b594b..4009b858 100644 Binary files a/src/test/resources/weasyprint/png/testConverterWiki_page_0.png and b/src/test/resources/weasyprint/png/testConverterWiki_page_0.png differ