Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## [Version 1.1.0](https://github.com/dataiku/dss-plugin-sendmail/releases/tag/v1.1.0) - Feature release - 2026-07

- Features added
- Use of EMAIL_TEMPLATE_TEXTAREA instead of TEXTAREA to allow preview of email body

## [Version 1.0.3](https://github.com/dataiku/dss-plugin-sendmail/releases/tag/v1.0.3) - Feature release - 2025-03

Expand Down
7 changes: 4 additions & 3 deletions custom-recipes/send-mails-from-contacts-dataset/recipe.json
Original file line number Diff line number Diff line change
Expand Up @@ -164,10 +164,11 @@
{
"name": "html_body_value",
"label" : "Body (HTML)",
"type": "TEXTAREA",
"description" : "The body content supports JINJA templating. E.g. for a value from the input dataset use {{ column_name }}, for a table of attachment data {{ attachments.dataset_name.html_table }}, or to loop through an attachment dataset {% for row in attachments.dataset_name.data %}{{ row.column_name }}{% endfor %}",
"type": "EMAIL_TEMPLATE_TEXTAREA",
"description" : "The body content supports JINJA templating. E.g. for a value from the input dataset use {{ column_name }}, for a table of attachment data {{ attachments.dataset_name.html_table }}, or to loop through an attachment dataset {% for row in attachments.dataset_name.data %}{{ row.column_name }}{% endfor %}. Preview renders with the first row of the contacts dataset only; runtime renders once per contacts row.",
"defaultValue" : "<!-- Template with CSS starter kit -->\n<html><head><style type=\"text/css\"> * { font-family: Verdana, sans-serif; } table { width: 100%; border-collapse: collapse; border: 1px solid #ddd; } td { padding: 10px; text-align: left; border: 1px solid #ddd; } th { background-color: #f2f2f2; padding: 10px; text-align: left; border: 1px solid #ddd; }</style></head><body>\n{# add content underneath this - you can use JINJA templating, e.g. <b>{{ column_name }}</b> #}\n\n</body></html>",
"visibilityCondition" : "model.use_body_value && (model.body_format == 'html')"
"visibilityCondition" : "model.use_body_value && (model.body_format == 'html')",
"templatePreviewCallback": "preview_email_body"
},
{
"name": "use_body_value",
Expand Down
4 changes: 2 additions & 2 deletions plugin.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"id": "sendmail",
"version": "1.0.3",
"version": "1.1.0",
"meta": {
"label": "Send emails",
"description": "Send emails based on a dataset containing a list of contacts, with optional attachments (other datasets)",
"description": "Send emails based on a dataset containing a list of contacts, with optional attachments (other datasets). Requires DSS 15.1 or above",
"author": "Dataiku",
"icon": "icon-envelope-alt",
"licenseInfo": "Apache Software License",
Expand Down
65 changes: 64 additions & 1 deletion resource/dynamic_form.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,65 @@
from dss_selector_choices import DSSSelectorChoices, SENDER_SUFFIX
from dku_support_detection import supports_messaging_channels_and_conditional_formatting
from jinja2 import Environment, StrictUndefined
from dku_attachment_handling import attachments_template_dict
from email_utils import build_email_message_text
import dataiku

EMAIL_TEMPLATE_PREVIEW_CALLBACK = "preview_email_body"

jinja_env = Environment(undefined=StrictUndefined)


def _input_names_for_role(inputs, role):
return [
input_desc.get("fullName")
for input_desc in (inputs or [])
if input_desc.get("role") == role and input_desc.get("fullName")
]


def _first_row(dataset):
for row in dataset.iter_rows():
return dict(row)
return None


def preview_email_body(payload, config, inputs):
html = config.get(payload["parameterName"], payload["html"])

contact_names = _input_names_for_role(inputs, "contacts")
if not contact_names:
return {"html": html}

people = dataiku.Dataset(contact_names[0])
contact_dict = _first_row(people)
if contact_dict is None:
return {"html": html}

attachment_datasets = [
dataiku.Dataset(dataset_name)
for dataset_name in _input_names_for_role(inputs, "attachments")
]

try:
attachments_templating_dict = attachments_template_dict(
attachment_datasets,
dataiku.default_project_key(),
(config or {}).get("apply_coloring_excel", False),
)
body_template = jinja_env.from_string(html)
rendered_html = build_email_message_text(
True,
body_template,
attachments_templating_dict,
contact_dict,
None,
True,
)
except Exception as e:
return {"error": str(e)}

return {"html": rendered_html}

def do(payload, config, plugin_config, inputs):
dss_client = dataiku.api_client()
Expand All @@ -22,11 +80,16 @@ def do(payload, config, plugin_config, inputs):
else:
choices.append(f"{channel.id}", channel.id)

# Add an entry for direct SMTP
# Add an entry for direct SMTP
if len(channels) > 0:
# If there is a choice of channels, giving direct SMTP a key of "__DKU__DIRECT_SMTP__" means it is there but not as default
choices.append("Manually define SMTP", "__DKU__DIRECT_SMTP__")
else:
# If there is no choice, put SMTP there but with a key of None, so it will be the default instead of "Nothing selected"
choices.append("Manually define SMTP", None)
return choices.to_dss()

if payload.get("templatePreviewCallback") == EMAIL_TEMPLATE_PREVIEW_CALLBACK:
return preview_email_body(payload, config, inputs)

return {}