Unverified Commit ea7fad4f authored by Martin Lowe's avatar Martin Lowe 🇨🇦 Committed by GitHub
Browse files

Add PDF to email, update email/pdf template to iron out inconsistencies (#276)

* Initial commit of PDF creation. Needs some fine tuning before golive

* Update of mail template to include more PDF data, formatting fixes

* Add missing membershipLevel field to email/PDF

* Update NOTICE file for md formatting

* Fix notice with feedback from Sharon C.

* Add paragraphs below TOS points in PDF template
parent 22cdee11
......@@ -31,6 +31,13 @@ The project maintains the following source code repositories:
## Third-party Content
### OPEN HTML TO PDF (1.09)
License: GNU Lesser General Public License (LGPL)
SPDX-License-Identifier: LGPL-2.1-only
No modifications or additions to the library have been made. License text provided through library distribution.
### Drupal-Bootstrap (7.x-3.20)
* License: The GPL License (GPL)
......@@ -19,6 +19,7 @@
......@@ -97,6 +98,28 @@
<!-- ALWAYS required, usually included transitively. -->
<!-- Required for PDF output. -->
<!-- Required for image output only. -->
package org.eclipsefoundation.react.service.impl;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
......@@ -13,6 +18,9 @@ import org.eclipsefoundation.react.service.MailerService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.openhtmltopdf.outputdevice.helper.BaseRendererBuilder.PageSizeUnits;
import com.openhtmltopdf.pdfboxout.PdfRendererBuilder;
import io.quarkus.mailer.Mail;
import io.quarkus.mailer.Mailer;
import io.quarkus.qute.Location;
......@@ -31,12 +39,19 @@ import io.smallrye.jwt.auth.principal.DefaultJWTCallerPrincipal;
public class DefaultMailerService implements MailerService {
public static final Logger LOGGER = LoggerFactory.getLogger(DefaultMailerService.class);
private static final String EMAIL_INCLUDE_PREAMBLE_VAR = "includePreamble";
private static final String EMAIL_DATA_VAR = "data";
private static final String EMAIL_NOW_VAR = "now";
private static final String EMAIL_NAME_VAR = "name";
@ConfigProperty(name = "eclipse.mailer.membership.inbox")
String membershipMailbox;
@ConfigProperty(name = "eclipse.mailer.membership.author-message.bcc")
Optional<List<String>> authorMessageMailboxBcc;
@ConfigProperty(name = "eclipse.mailer.membership.membership-message.bcc")
Optional<List<String>> membershipMessageMailboxBcc;
@ConfigProperty(name = "eclipse.mailer.membership.doc-storage-root", defaultValue = "/tmp")
String temporaryDocumentStorage;
SecurityIdentity ident;
......@@ -79,12 +94,21 @@ public class DefaultMailerService implements MailerService {
throw new IllegalStateException(
"Could not find a fully complete form for form with ID '" + data.form.getId() + "'");
Mail m = getMail(membershipMailbox, "New Request to join working group(s) - "
+ generateName((DefaultJWTCallerPrincipal) ident.getPrincipal()), data, false);
DefaultJWTCallerPrincipal defaultPrin = (DefaultJWTCallerPrincipal) ident.getPrincipal();
Mail m = getMail(membershipMailbox, "New Request to join working group(s) - " + generateName(defaultPrin), data,
// add BCC if set
if (!membershipMessageMailboxBcc.isEmpty()) {
// add the PDF attachment
m.addAttachment("membership-enrollment-" + generateName(defaultPrin).toLowerCase().replace(" ", "-") + ".pdf",
.data(EMAIL_DATA_VAR, data, EMAIL_NAME_VAR, generateName(defaultPrin), EMAIL_NOW_VAR,
LocalDateTime.now(), EMAIL_INCLUDE_PREAMBLE_VAR, false, "isPDF", true)
......@@ -101,15 +125,36 @@ public class DefaultMailerService implements MailerService {
private Mail getMail(String recipient, String subject, MailerData data, boolean includePreamble) {
String name = generateName((DefaultJWTCallerPrincipal) ident.getPrincipal());
// generate the mail message, sending the messsage to the membershipMailbox
Mail m = Mail.withHtml(recipient, subject, membershipTemplateWeb
.data("data", data, "name", name, "now", LocalDateTime.now(), "includePreamble", includePreamble)
.data("data", data, "name", name, "now", LocalDateTime.now(), "includePreamble", includePreamble)
Mail m = Mail.withHtml(recipient, subject, membershipTemplateWeb.data(EMAIL_DATA_VAR, data, EMAIL_NAME_VAR,
name, EMAIL_NOW_VAR, LocalDateTime.now(), EMAIL_INCLUDE_PREAMBLE_VAR, includePreamble).render());
m.setText(membershipTemplate.data(EMAIL_DATA_VAR, data, EMAIL_NAME_VAR, name, EMAIL_NOW_VAR,
LocalDateTime.now(), EMAIL_INCLUDE_PREAMBLE_VAR, includePreamble).render());
return m;
* Render the PDF document using HTML generated by the Qute template engine.
* @param html the HTML for the PDF document.
* @return the file that represents the document. This should be in a temporary directory so that it gets cleaned on
* occasion.
private File renderHTMLPDF(String html) {
// create a unique file name for temporary storage
File f = new File(temporaryDocumentStorage + '/' + UUID.randomUUID().toString() + ".pdf");
try (OutputStream os = new FileOutputStream(f)) {
PdfRendererBuilder builder = new PdfRendererBuilder();
builder.withHtmlContent(html, temporaryDocumentStorage);
// using a4 measurements
builder.useDefaultPageSize(210, 297, PageSizeUnits.MM);
} catch (IOException e) {
throw new RuntimeException("Could not build PDF document", e);
return f;
private String generateName(DefaultJWTCallerPrincipal defaultPrin) {
return new StringBuilder().append((String) defaultPrin.getClaim("given_name")).append(" ")
.append((String) defaultPrin.getClaim("family_name")).toString();
<div style="background: #efefef; margin-left: -10px; margin-right: -10px;">
<div style="background: #FFF; min-width: 700px; width: 66%; margin: auto; margin-top: -20px; margin-bottom: -20px;">
<div style="{#if !isPDF}background: #efefef; {/if}margin-left: -10px; margin-right: -10px;">
<div style="{#if !isPDF}min-width: 700px; width: 66%; background: #FFF; {/if} margin: auto; margin-top: -20px; margin-bottom: -20px;">
<div style="padding: 20px">
<h1>Eclipse Foundation AISBL Membership Application date submitted: {now}</h1>
<h1 style="text-align: center">Eclipse Foundation AISBL Membership Application</h1>
<p style="font-size: 18px; font-weight: 600;">Date submitted: {time:format(now,'d MMM uuuu, HH:mm')}</p>
{#if includePreamble}
<p>Dear {name},</p>
<p>Thank you for submitting the member enrollment form on behalf of your organization. Our team will review your application and follow up within 2 business days.</p>
......@@ -9,16 +10,17 @@
<p>Best regards,</p>
<p>Eclipse Foundation Membership Coordination</p>
<p>New form submission by: {name} ({data.form.userID})</p>
<p>Form submission by: {name} ({data.form.userID})</p>
<p style="margin-bottom: 0px">Name: {data.org.legalName}</p>
<p style="margin-bottom: 0px; margin-top: 5px">Aggregate Revenue: {data.org.aggregateRevenue}</p>
<p style="margin-bottom: 0px; margin-top: 5px">Employee count: {data.org.employeeCount}</p>
<p style="margin: 0px">Name: {data.org.legalName}</p>
<p style="margin: 0px;">Aggregate Revenue: {data.org.aggregateRevenue}</p>
<p style="margin: 0px;">Employee Count: {data.org.employeeCount}</p>
<p style="margin: 0px;">Organization Type: {data.org.organizationType}</p>
<p style="margin-bottom: 0px; margin-top: 5px">Twitter: {data.org.twitterHandle}</p>
<p style="margin-bottom: 0px; margin-top: 5px"></p>Requires purchase order: {data.form.purchaseOrderRequired}</p>
<p style="margin-bottom: 0px; margin-top: 5px"></p>VAT number: {data.form.vatNumber}</p>
<p style="margin-top: 5px">VAT Registration country: {data.form.registrationCountry}</p>
<p style="margin: 0px;">Twitter: {data.org.twitterHandle}</p>
<p style="margin: 0px;">Requires Purchase Order: {data.form.purchaseOrderRequired}</p>
<p style="margin: 0px;">VAT Number: {data.form.vatNumber}</p>
<p style="margin: 0px">VAT Registration Country: {data.form.registrationCountry}</p>
<p style="margin: 0px">Membership Level: {data.form.membershipLevel}</p>
......@@ -70,7 +72,19 @@
<hr style="margin:40px 20px ">
{#if isPDF}
<p style="margin-top:50px;">We thank you for completing this Member Application Form.</p>
<p>As a new Member:</p>
<li>You agree to publicly support the Eclipse Foundation and its Purpose.</li>
<li>You acknowledge your commitment in principle to comply with the Bylaws, the Internal Rules, the Eclipse Foundation Antitrust Policy, IP Policy, and any and all additional policies, procedures and other governing rules of the Eclipse Foundation.</li>
<li>You agree to provide Eclipse Foundation with your logo (or instructions to obtain your logo) in accordance with Section 2.3 of the Eclipse Foundation Membership Agreement. When providing your logo, be sure to include a reference or link to any logo and trademark usage guidelines you have.<br />
Our Membership Coordination team will work with you to complete this after your Membership Application is processed.</li>
<p>Thank you for completing the Membership Application Process. This PDF captures the information you have provided to us. Please review this information as it becomes a part of your formal application to the Eclipse Foundation.</p>
<p>Should you wish to update any of the information, please reach out to our Membership team at <a href="mailto:membership.coordination@eclipse-foundation.org">membership.coordination@eclipse-foundation.org</a></p>
<hr style="margin:40px 20px "></hr>
<p style="text-align: center">This is an automatic message from <a href="https://membership.eclipse.org">https://membership.eclipse.org</a></p>
<p style="text-align: center">Twitter: <a href="https://twitter.com/EclipseFdn">@EclipseFdn</a></p>
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment