Skip to content

Commit

Permalink
Merge branch 'main' into feature/auto_assign_style_packages
Browse files Browse the repository at this point in the history
  • Loading branch information
grigoriev authored Sep 2, 2024
2 parents 68378cd + 4da2ee2 commit 6073100
Show file tree
Hide file tree
Showing 38 changed files with 534 additions and 820 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,10 +171,10 @@ ch.sbb.polarion.extension.pdf-exporter.internalizeExternalCss=true
## Extension Configuration

1. On the top of the project's navigation pane click ⚙ (Actions) ➙ 🔧 Administration. Project's administration page will be opened.
2. On the administration's navigation pane select `PDF Export`. There are 5 sub-menus with different configuration options for PDF Exporter.
3. For 5 of these options (Cover page, Header and Footer, Localization, Webhooks and Filename template) `Quick Help` section available with option short description. For the rest 2
2. On the administration's navigation pane select `PDF Export`. There are expandable sub-menus with different configuration options for PDF Exporter.
3. For some of these options (Cover page, Header and Footer, Localization, Webhooks and Filename template) `Quick Help` section available with option short description. For the rest
(Style package, Stylesheets) there's no `Quick Help` section as their content is self-evident.
4. To change configuration of PDF exporter extension just edit corresponding section and press `Save` button.
4. To change configuration of PDF Exporter extension just edit corresponding section and press `Save` button.

## Usage

Expand Down
8 changes: 2 additions & 6 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>ch.sbb.polarion.extensions</groupId>
<artifactId>ch.sbb.polarion.extension.generic</artifactId>
<version>6.7.0</version>
<version>7.0.0</version>
</parent>

<artifactId>ch.sbb.polarion.extension.pdf-exporter</artifactId>
Expand Down Expand Up @@ -59,6 +59,7 @@

<maven-jar-plugin.Extension-Context>pdf-exporter</maven-jar-plugin.Extension-Context>
<maven-jar-plugin.Automatic-Module-Name>ch.sbb.polarion.extension.pdf_exporter</maven-jar-plugin.Automatic-Module-Name>
<maven-jar-plugin.Discover-Base-Package>ch.sbb.polarion.extension.pdf.exporter</maven-jar-plugin.Discover-Base-Package>
<web.app.name>${maven-jar-plugin.Extension-Context}</web.app.name>

<!--suppress UnresolvedMavenProperty -->
Expand Down Expand Up @@ -109,11 +110,6 @@
<artifactId>byte-buddy</artifactId>
<version>${byte-buddy.version}</version>
</dependency>
<dependency>
<groupId>com.google.re2j</groupId>
<artifactId>re2j</artifactId>
<version>${re2j.version}</version>
</dependency>

<!-- Test dependencies -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package ch.sbb.polarion.extension.pdf.exporter;

import ch.sbb.polarion.extension.generic.GenericUiServlet;
import ch.sbb.polarion.extension.generic.properties.CurrentExtensionConfiguration;
import ch.sbb.polarion.extension.pdf.exporter.properties.PdfExporterExtensionConfiguration;

import java.io.Serial;

Expand All @@ -13,6 +11,5 @@ public class PdfExporterAdminUiServlet extends GenericUiServlet {

public PdfExporterAdminUiServlet() {
super("pdf-exporter-admin");
CurrentExtensionConfiguration.getInstance().setExtensionConfiguration(PdfExporterExtensionConfiguration.getInstance());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ private String adjustWebhooks(String scope, String form, StylePackageModel style
Collection<SettingName> webhooksNames = getSettingNames(WebhooksSettings.FEATURE_NAME, scope);
boolean noHooks = StringUtils.isEmpty(stylePackage.getWebhooks());
String webhooksOptions = generateSelectOptions(webhooksNames, noHooks ? NamedSettings.DEFAULT_NAME : stylePackage.getWebhooks());
form = form.replace("{WEBHOOKS_DISPLAY}", PdfExporterExtensionConfiguration.getInstance().areWebhooksEnabled() ? "" : "hidden");
form = form.replace("{WEBHOOKS_DISPLAY}", PdfExporterExtensionConfiguration.getInstance().getWebhooksEnabled() ? "" : "hidden");
form = form.replace("{WEBHOOKS_OPTIONS}", webhooksOptions);
form = form.replace("{WEBHOOKS_SELECTOR_DISPLAY}", noHooks ? "none" : "inline-block");
return form.replace("{WEBHOOKS_SELECTED}", noHooks ? "" : "checked");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import ch.sbb.polarion.extension.pdf.exporter.util.PdfExporterFileResourceProvider;
import ch.sbb.polarion.extension.pdf.exporter.util.PdfTemplateProcessor;
import ch.sbb.polarion.extension.pdf.exporter.util.html.HtmlLinksHelper;
import ch.sbb.polarion.extension.pdf.exporter.util.regex.RegexMatcher;
import ch.sbb.polarion.extension.generic.regex.RegexMatcher;
import ch.sbb.polarion.extension.pdf.exporter.weasyprint.WeasyPrintOptions;
import ch.sbb.polarion.extension.pdf.exporter.weasyprint.service.WeasyPrintServiceConnector;
import lombok.Getter;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@
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.headerfooter.HeaderFooterModel;
import ch.sbb.polarion.extension.pdf.exporter.rest.model.settings.hooks.WebhooksModel;
import ch.sbb.polarion.extension.pdf.exporter.rest.model.settings.webhooks.AuthType;
import ch.sbb.polarion.extension.pdf.exporter.rest.model.settings.webhooks.WebhookConfig;
import ch.sbb.polarion.extension.pdf.exporter.rest.model.settings.webhooks.WebhooksModel;
import ch.sbb.polarion.extension.pdf.exporter.service.PdfExporterPolarionService;
import ch.sbb.polarion.extension.pdf.exporter.settings.CssSettings;
import ch.sbb.polarion.extension.pdf.exporter.settings.HeaderFooterSettings;
import ch.sbb.polarion.extension.pdf.exporter.settings.WebhooksSettings;
import ch.sbb.polarion.extension.pdf.exporter.settings.LocalizationSettings;
import ch.sbb.polarion.extension.pdf.exporter.settings.WebhooksSettings;
import ch.sbb.polarion.extension.pdf.exporter.util.EnumValuesProvider;
import ch.sbb.polarion.extension.pdf.exporter.util.HtmlLogger;
import ch.sbb.polarion.extension.pdf.exporter.util.HtmlProcessor;
Expand All @@ -27,11 +29,12 @@
import ch.sbb.polarion.extension.pdf.exporter.util.placeholder.PlaceholderProcessor;
import ch.sbb.polarion.extension.pdf.exporter.util.velocity.VelocityEvaluator;
import ch.sbb.polarion.extension.pdf.exporter.weasyprint.WeasyPrintOptions;
import com.fasterxml.jackson.databind.ObjectMapper;
import ch.sbb.polarion.extension.pdf.exporter.weasyprint.service.WeasyPrintServiceConnector;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.polarion.alm.tracker.model.ITrackerProject;
import com.polarion.core.util.StringUtils;
import com.polarion.core.util.logging.Logger;
import com.polarion.platform.internal.security.UserAccountVault;
import lombok.AllArgsConstructor;
import org.glassfish.jersey.media.multipart.FormDataBodyPart;
import org.glassfish.jersey.media.multipart.FormDataMultiPart;
Expand All @@ -43,12 +46,15 @@
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
Expand Down Expand Up @@ -148,39 +154,43 @@ public byte[] convertToPdf(@NotNull ExportParams exportParams, @Nullable ExportM

private @NotNull String applyWebhooks(@NotNull ExportParams exportParams, @NotNull String htmlContent) {
// Skip webhooks processing among other if this functionality is not enabled by system administrator
if (!PdfExporterExtensionConfiguration.getInstance().areWebhooksEnabled() || exportParams.getWebhooks() == null) {
if (!PdfExporterExtensionConfiguration.getInstance().getWebhooksEnabled() || exportParams.getWebhooks() == null) {
return htmlContent;
}

WebhooksModel webhooksModel = new WebhooksSettings().load(exportParams.getProjectId(), SettingId.fromName(exportParams.getWebhooks()));
String result = htmlContent;
for (String webhook : webhooksModel.getWebhooks()) {
result = applyWebhook(webhook, exportParams, result);
for (WebhookConfig webhookConfig : webhooksModel.getWebhookConfigs()) {
result = applyWebhook(webhookConfig, exportParams, result);
}
return result;
}

private @NotNull String applyWebhook(@NotNull String webhook, @NotNull ExportParams exportParams, @NotNull String htmlContent) {
private @NotNull String applyWebhook(@NotNull WebhookConfig webhookConfig, @NotNull ExportParams exportParams, @NotNull String htmlContent) {
Client client = null;
try {
client = ClientBuilder.newClient();
WebTarget webTarget = client.target(webhook).register(MultiPartFeature.class);
WebTarget webTarget = client.target(webhookConfig.getUrl()).register(MultiPartFeature.class);

FormDataMultiPart multipart = new FormDataMultiPart();
multipart.bodyPart(new FormDataBodyPart("exportParams", new ObjectMapper().writeValueAsString(exportParams), MediaType.APPLICATION_JSON_TYPE));
multipart.bodyPart(new FormDataBodyPart("html", htmlContent.getBytes(StandardCharsets.UTF_8), MediaType.APPLICATION_OCTET_STREAM_TYPE));

try (Response response = webTarget.request(MediaType.TEXT_PLAIN).post(Entity.entity(multipart, multipart.getMediaType()))) {
Invocation.Builder requestBuilder = webTarget.request(MediaType.TEXT_PLAIN);

addAuthHeader(webhookConfig, requestBuilder);

try (Response response = requestBuilder.post(Entity.entity(multipart, multipart.getMediaType()))) {
if (response.getStatus() == Response.Status.OK.getStatusCode()) {
try (InputStream inputStream = response.readEntity(InputStream.class)) {
return new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
}
} else {
logger.error(String.format("Could not get proper response from webhook [%s]: response status %s", webhook, response.getStatus()));
logger.error(String.format("Could not get proper response from webhook [%s]: response status %s", webhookConfig.getUrl(), response.getStatus()));
}
}
} catch (Exception e) {
logger.error(String.format("Could not get response from webhook [%s]", webhook), e);
logger.error(String.format("Could not get response from webhook [%s]", webhookConfig.getUrl()), e);
} finally {
if (client != null) {
client.close();
Expand All @@ -191,6 +201,31 @@ public byte[] convertToPdf(@NotNull ExportParams exportParams, @Nullable ExportM
return htmlContent;
}

private static void addAuthHeader(@NotNull WebhookConfig webhookConfig, @NotNull Invocation.Builder requestBuilder) {
if (webhookConfig.getAuthType() == null || webhookConfig.getAuthTokenName() == null) {
return;
}

String authInfoFromUserAccountVault = getAuthInfoFromUserAccountVault(webhookConfig.getAuthType(), webhookConfig.getAuthTokenName());
if (authInfoFromUserAccountVault == null) {
return;
}

requestBuilder.header(HttpHeaders.AUTHORIZATION, webhookConfig.getAuthType().getAuthHeaderPrefix() + " " + authInfoFromUserAccountVault);
}

private static @Nullable String getAuthInfoFromUserAccountVault(@NotNull AuthType authType, @NotNull String authTokenName) {
@NotNull UserAccountVault.Credentials credentials = UserAccountVault.getInstance().getCredentialsForKey(authTokenName);

return switch (authType) {
case BASIC_AUTH -> {
String authInfo = credentials.getUser() + ":" + credentials.getPassword();
yield Base64.getEncoder().encodeToString(authInfo.getBytes());
}
case BEARER_TOKEN -> credentials.getPassword();
};
}

@VisibleForTesting
byte[] generatePdf(
LiveDocHelper.DocumentData documentData,
Expand Down Expand Up @@ -219,8 +254,8 @@ String postProcessDocumentContent(@NotNull ExportParams exportParams, @Nullable
@NotNull
@VisibleForTesting
String composeHtml(@NotNull String documentName,
@NotNull HtmlData htmlData,
@NotNull ExportParams exportParams) {
@NotNull HtmlData htmlData,
@NotNull ExportParams exportParams) {
String content = htmlData.headerFooterContent
+ "<div class='content'>" + htmlData.documentContent + "</div>";
return pdfTemplateProcessor.processUsing(exportParams, documentName, htmlData.cssContent, content);
Expand All @@ -236,9 +271,9 @@ String getCssContent(
String listStyles = new PdfExporterListStyleProvider(exportParams.getNumberedListStyles()).getStyle();
String css = pdfStyles
+ (exportParams.getHeadersColor() != null ?
" h1, h2, h3, h4, h5, h6, .content .title {"
+ " color: " + exportParams.getHeadersColor() + ";"
+ " }"
" h1, h2, h3, h4, h5, h6, .content .title {" +
" color: " + exportParams.getHeadersColor() + ";" +
" }"
: "")
+ listStyles;

Expand Down Expand Up @@ -279,5 +314,6 @@ private String appendWikiCss(String css) {
return css + System.lineSeparator() + ScopeUtils.getFileContent("default/wiki.css");
}

record HtmlData(String cssContent, String documentContent, String headerFooterContent) {}
record HtmlData(String cssContent, String documentContent, String headerFooterContent) {
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package ch.sbb.polarion.extension.pdf.exporter.properties;

import ch.sbb.polarion.extension.generic.properties.CurrentExtensionConfiguration;
import ch.sbb.polarion.extension.generic.properties.ExtensionConfiguration;
import ch.sbb.polarion.extension.generic.util.Discoverable;
import com.polarion.core.config.impl.SystemValueReader;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.List;

@Discoverable
public class PdfExporterExtensionConfiguration extends ExtensionConfiguration {

public static final String WEASYPRINT_SERVICE = "weasyprint.service";
Expand All @@ -28,7 +31,7 @@ public Boolean getInternalizeExternalCss() {
}

@NotNull
public Boolean areWebhooksEnabled() {
public Boolean getWebhooksEnabled() {
return SystemValueReader.getInstance().readBoolean(getPropertyPrefix() + WEBHOOKS_ENABLED, false);
}

Expand All @@ -42,16 +45,7 @@ public Boolean areWebhooksEnabled() {
return supportedProperties;
}

public PdfExporterExtensionConfiguration() {
super();
}

public static PdfExporterExtensionConfiguration getInstance() {
return PdfExporterExtensionConfigurationHolder.INSTANCE;
return (PdfExporterExtensionConfiguration) CurrentExtensionConfiguration.getInstance().getExtensionConfiguration();
}

private static class PdfExporterExtensionConfigurationHolder {
private static final PdfExporterExtensionConfiguration INSTANCE = new PdfExporterExtensionConfiguration();
}

}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package ch.sbb.polarion.extension.pdf.exporter.rest.controller;

import ch.sbb.polarion.extension.generic.configuration.ConfigurationStatus;
import ch.sbb.polarion.extension.generic.rest.filter.Secured;
import ch.sbb.polarion.extension.generic.service.PolarionService;
import ch.sbb.polarion.extension.pdf.exporter.rest.model.configuration.ConfigurationStatus;
import org.jetbrains.annotations.NotNull;

import javax.ws.rs.Path;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
package ch.sbb.polarion.extension.pdf.exporter.rest.controller;

import ch.sbb.polarion.extension.pdf.exporter.rest.model.configuration.ConfigurationStatus;
import ch.sbb.polarion.extension.pdf.exporter.util.configuration.ConfigurationStatusUtils;
import ch.sbb.polarion.extension.generic.configuration.ConfigurationStatus;
import ch.sbb.polarion.extension.generic.configuration.ConfigurationStatusProvider;
import ch.sbb.polarion.extension.pdf.exporter.util.configuration.CORSStatusProvider;
import ch.sbb.polarion.extension.pdf.exporter.util.configuration.DefaultSettingsStatusProvider;
import ch.sbb.polarion.extension.pdf.exporter.util.configuration.DleToolbarStatusProvider;
import ch.sbb.polarion.extension.pdf.exporter.util.configuration.DocumentPropertiesPaneStatusProvider;
import ch.sbb.polarion.extension.pdf.exporter.util.configuration.LiveReportMainHeadStatusProvider;
import ch.sbb.polarion.extension.pdf.exporter.util.configuration.WeasyPrintStatusProvider;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Operation;
import org.jetbrains.annotations.NotNull;
Expand All @@ -23,46 +29,46 @@ public class ConfigurationInternalController {
@Path("/configuration/default-settings")
@Operation(summary = "Checks default settings configuration")
public @NotNull ConfigurationStatus checkDefaultSettings(@QueryParam("scope") @DefaultValue("") String scope) {
return ConfigurationStatusUtils.getSettingsStatus(scope);
return new DefaultSettingsStatusProvider().getStatus(ConfigurationStatusProvider.Context.builder().scope(scope).build());
}

@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/configuration/document-properties-pane-config")
@Operation(summary = "Checks document properties pane configuration")
public @NotNull ConfigurationStatus checkDocumentPropertiesPaneConfig(@QueryParam("scope") @DefaultValue("") String scope) {
return ConfigurationStatusUtils.getDocumentPropertiesPaneStatus(scope);
return new DocumentPropertiesPaneStatusProvider().getStatus(ConfigurationStatusProvider.Context.builder().scope(scope).build());
}

@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/configuration/dle-toolbar-config")
@Operation(summary = "Checks DLE Toolbar configuration")
public @NotNull ConfigurationStatus checkDleToolbarConfig() {
return ConfigurationStatusUtils.getDleToolbarStatus();
return new DleToolbarStatusProvider().getStatus(ConfigurationStatusProvider.Context.builder().build());
}

@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/configuration/live-report-config")
@Operation(summary = "Checks Live Report configuration")
public @NotNull ConfigurationStatus checkLiveReportConfig() {
return ConfigurationStatusUtils.getLiveReportMainHeadStatus();
return new LiveReportMainHeadStatusProvider().getStatus(ConfigurationStatusProvider.Context.builder().build());
}

@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/configuration/cors-config")
@Operation(summary = "Checks CORS configuration")
public @NotNull ConfigurationStatus checkCORSConfig() {
return ConfigurationStatusUtils.getCORSStatus();
return new CORSStatusProvider().getStatus(ConfigurationStatusProvider.Context.builder().build());
}

@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/configuration/weasyprint")
@Operation(summary = "Checks WeasyPrint configuration")
public @NotNull List<ConfigurationStatus> checkWeasyPrint() {
return ConfigurationStatusUtils.getWeasyPrintStatus();
return new WeasyPrintStatusProvider().getStatuses(ConfigurationStatusProvider.Context.builder().build());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,6 @@ public String getFileName(@QueryParam("locationPath") String locationPath, @Quer
@Tag(name = "Utility resources")
@Operation(summary = "Gets webhooks status - if they are enabled or not")
public WebhooksStatus getWebhooksStatus() {
return WebhooksStatus.builder().enabled(PdfExporterExtensionConfiguration.getInstance().areWebhooksEnabled()).build();
return WebhooksStatus.builder().enabled(PdfExporterExtensionConfiguration.getInstance().getWebhooksEnabled()).build();
}
}
Loading

0 comments on commit 6073100

Please sign in to comment.