DEV Community

Explorer
Explorer

Posted on

Sending Programmatic HTML Emails with Base64 File Attachments in Joget

Joget DX provides a standard Email Tool plugin for basic email dispatch. However, when enterprise applications need to convert form file attachments (PDFs, images) into Base64 payloads and dispatch responsive HTML email templates through a custom REST API endpoint, a BeanShell script provides full programmatic control.

In this guide, we'll write a Java/BeanShell script that validates file types, reads form attachments using Joget's FileUtil service, converts them to Base64, and dispatches a JSON POST payload to a custom email service API.


How It Works

  1. Attachment Discovery: Using FileUtil.getFile(fileName, formId, recordId), the script locates uploaded files on the Joget server storage path.
  2. File Validation & Base64 Encoding: The script checks allowed file extensions (.pdf, .png, .jpg), reads the file bytes, and converts them to Base64 strings.
  3. HTML Email Formatting: A responsive HTML string is built using StringBuilder with embedded CSS styling.
  4. REST API Dispatch: An HttpURLConnection dispatches the JSON payload (recipients, subject, HTML body, Base64 attachments) to the target email API endpoint (#appVariable.send_email_api_url#).

The BeanShell Script

Place this code inside a BeanShell Tool workflow step:

import org.joget.apps.app.service.AppUtil;
import org.joget.commons.util.LogUtil;
import org.joget.apps.app.model.AppDefinition;
import org.joget.apps.form.service.FileUtil;
import java.io.File;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.Base64;
import org.json.JSONObject;
import org.json.JSONArray;

// 1. Helper: Validate allowed attachment file types
public boolean isSupportedFileType(String fileName) {
    if (fileName == null) return false;
    String[] allowedExts = {".pdf", ".png", ".jpg", ".jpeg", ".docx"};
    String lower = fileName.toLowerCase();
    for (String ext : allowedExts) {
        if (lower.endsWith(ext)) return true;
    }
    return false;
}

// 2. Helper: Encode form attachment into Base64 JSON object
public void appendFileAttachment(JSONArray attachmentArray, String fileName, String formId, String recordId) {
    try {
        if (!isSupportedFileType(fileName)) {
            LogUtil.warn("Email Service", "Skipped unsupported file attachment: " + fileName);
            return;
        }

        File fileObj = FileUtil.getFile(fileName, formId, recordId);
        if (fileObj != null && fileObj.exists()) {
            byte[] fileBytes = Files.readAllBytes(fileObj.toPath());
            String base64Data = Base64.getEncoder().encodeToString(fileBytes);

            JSONObject item = new JSONObject();
            item.put("name", fileName);
            item.put("bytes", base64Data);
            attachmentArray.put(item);

            LogUtil.info("Email Service", "Successfully encoded attachment: " + fileName);
        }
    } catch (Exception e) {
        LogUtil.error("Email Service", e, "Error encoding file attachment: " + fileName);
    }
}

// 3. Prepare Email Payload
JSONArray attachments = new JSONArray();
String formId = "purchase_request";
String recordId = "#form.purchase_request.id#";
String attachmentFileName = "#form.purchase_request.attachment#";

// Add attachment if present
if (attachmentFileName != null && !attachmentFileName.trim().isEmpty()) {
    appendFileAttachment(attachments, attachmentFileName, formId, recordId);
}

// 4. Construct Styled HTML Email Body
StringBuilder htmlBody = new StringBuilder();
htmlBody.append("<!DOCTYPE html><html><head><meta charset='UTF-8'>");
htmlBody.append("<style>");
htmlBody.append("body { font-family: Arial, sans-serif; background-color: #f4f6f8; margin: 0; padding: 20px; }");
htmlBody.append(".card { max-width: 600px; background: #ffffff; padding: 30px; border-radius: 8px; border: 1px solid #e1e4e8; margin: auto; }");
htmlBody.append(".header { background: #0056b3; color: #ffffff; padding: 15px 20px; border-radius: 6px 6px 0 0; font-size: 20px; font-weight: bold; }");
htmlBody.append(".content { padding: 20px 0; color: #333333; line-height: 1.6; }");
htmlBody.append(".btn { display: inline-block; background: #0056b3; color: #ffffff; padding: 10px 20px; text-decoration: none; border-radius: 4px; font-weight: bold; margin-top: 15px; }");
htmlBody.append("</style></head><body>");
htmlBody.append("<div class='card'>");
htmlBody.append("<div class='header'>Request Approval Notice</div>");
htmlBody.append("<div class='content'>");
htmlBody.append("<p>Dear Approver,</p>");
htmlBody.append("<p>A new purchase request (<strong>#form.purchase_request.ref_no#</strong>) requires your review.</p>");
htmlBody.append("<p><strong>Requested Amount:</strong> $#form.purchase_request.amount#</p>");
htmlBody.append("<p><strong>Department:</strong> #form.purchase_request.department#</p>");
htmlBody.append("<a href='#appVariable.userview_approval_url#' class='btn'>Open Approval View</a>");
htmlBody.append("</div>");
htmlBody.append("</div></body></html>");

// 5. Dispatch JSON Payload via REST API
String apiUrl = "#appVariable.send_email_api_url#"; // e.g. https://api.your-domain.com/v1/send-email
JSONObject requestJson = new JSONObject();
requestJson.put("to", "#form.purchase_request.approver_email#");
requestJson.put("subject", "Action Required: Request #form.purchase_request.ref_no#");
requestJson.put("htmlBody", htmlBody.toString());
requestJson.put("attachments", attachments);

try {
    URL url = new URL(apiUrl);
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    conn.setRequestMethod("POST");
    conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
    conn.setDoOutput(true);
    conn.setConnectTimeout(8000);

    OutputStream os = conn.getOutputStream();
    os.write(requestJson.toString().getBytes(StandardCharsets.UTF_8));
    os.flush();
    os.close();

    int responseCode = conn.getResponseCode();
    LogUtil.info("Email Service", "Dispatched email REST request. Response Code: " + responseCode);

} catch (Exception e) {
    LogUtil.error("Email Service", e, "Failed to dispatch REST email API request");
}
Enter fullscreen mode Exit fullscreen mode

Key Benefits

  • File Attachment Support: Automatically converts server-stored form files (FileUtil.getFile()) into Base64 strings for external API delivery.
  • Custom HTML Email Branding: Delivers clean inline CSS styling across desktop and mobile email clients.

Top comments (0)