Skip to content
Merged
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
27 changes: 21 additions & 6 deletions config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -561,15 +561,30 @@
# with PKs is safe since users.User's PKs are integers.
]

# EMAIL SETTINGS -- THESE NEED ADJUSTMENT WHEN DECIDED WHICH ESP WILL BE USED
EMAIL_HOST = "maildev"
EMAIL_PORT = 1025
DEFAULT_FROM_EMAIL = "boost@cppalliance.org"
# EMAIL SETTINGS
# Production sends through Mailgun (Anymail). In local development the default
# is the maildev SMTP container, but every setting below is overridable via env
# so you can route test sends through your own email service provider -- either
# an SMTP provider (set EMAIL_HOST/EMAIL_PORT/EMAIL_HOST_USER/
# EMAIL_HOST_PASSWORD/EMAIL_USE_TLS) or an Anymail API backend (set
# EMAIL_BACKEND plus that backend's credentials in ANYMAIL).
EMAIL_HOST = env("EMAIL_HOST", default="maildev")
EMAIL_PORT = env.int("EMAIL_PORT", default=1025)
EMAIL_HOST_USER = env("EMAIL_HOST_USER", default="")
EMAIL_HOST_PASSWORD = env("EMAIL_HOST_PASSWORD", default="")
EMAIL_USE_TLS = env.bool("EMAIL_USE_TLS", default=False)
# Use a "from" on a domain your provider has verified (some providers only
# accept senders on a domain you own).
DEFAULT_FROM_EMAIL = env("DEFAULT_FROM_EMAIL", default="boost@cppalliance.org")
SERVER_EMAIL = "errors@cppalliance.org"

# Deployed email configuration
if LOCAL_DEVELOPMENT:
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_BACKEND = env(
"EMAIL_BACKEND", default="django.core.mail.backends.smtp.EmailBackend"
)
# Credentials for an Anymail backend, as a JSON env var, e.g.
# ANYMAIL='{"<PROVIDER>_API_TOKEN": "..."}'. Empty/ignored for plain SMTP.
ANYMAIL = env.json("ANYMAIL", default={})
else:
EMAIL_BACKEND = "anymail.backends.mailgun.EmailBackend"
ANYMAIL = {
Expand Down
1 change: 1 addition & 0 deletions templates/emails/_social_icon.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{% load custom_static %}<td style="padding:0 16px 0 0;" valign="middle"><a href="{{ url }}" target="_blank" rel="noopener noreferrer" style="text-decoration:none;"><img src="{% large_static 'img/emails/'|add:icon %}" width="24" height="24" alt="{{ name }}" style="display:block;width:24px;height:24px;"></a></td>
144 changes: 144 additions & 0 deletions templates/emails/base_email.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
{% load custom_static %}{% comment %}
Shared base for Boost transactional emails.

Frontend / markup only — sending logic lives in the sign-up and
password-reset flows. Render this with a context that
provides absolute-URL helpers so images and links resolve in an inbox:

scheme "https" (request.scheme)
host "www.boost.org" (request.get_host)
action_url the CTA + fallback URL (confirmation or reset link)
preferences_url manage-email-preferences URL (optional, defaults to #)
unsubscribe_url unsubscribe URL (optional, defaults to #)

Body copy uses the platform UI font stack (Segoe UI on Windows, San Francisco
on Apple, Roboto on Android) at a light 300 weight; the title, button and
footer use Arial.

Child templates override the blocks: preheader, hero, card_title,
card_body, cta_label and after_card.
{% endcomment %}<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="x-apple-disable-message-reformatting">
<meta name="color-scheme" content="light">
<meta name="supported-color-schemes" content="light">
<meta name="format-detection" content="telephone=no, date=no, address=no, email=no">
<title>{% block title %}Boost{% endblock %}</title>
<!--[if mso]>
<style type="text/css">table,td{border-collapse:collapse;mso-table-lspace:0pt;mso-table-rspace:0pt}</style>
<![endif]-->
<style type="text/css">
body{margin:0;padding:0;width:100%!important;background-color:#f7fdfe;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}
img{border:0;line-height:100%;outline:none;text-decoration:none;-ms-interpolation-mode:bicubic;}
a{color:#050816;}
table{border-collapse:collapse;}
.email-card{border-collapse:separate!important;}
@media only screen and (max-width:600px){
.email-container{width:100%!important;}
.email-px{padding-left:16px!important;padding-right:16px!important;}
}
</style>
</head>
<body style="margin:0;padding:0;background-color:#f7fdfe;">
<!-- Preheader (hidden preview text) -->
<div style="display:none;max-height:0;overflow:hidden;mso-hide:all;font-size:1px;line-height:1px;color:#f7fdfe;opacity:0;">
{% block preheader %}{% endblock %}
</div>

<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:#f7fdfe;">
<tr>
<td align="center" style="padding:0;">
<!--[if mso]><table role="presentation" align="center" width="600" cellpadding="0" cellspacing="0" border="0"><tr><td><![endif]-->
<table role="presentation" class="email-container" width="600" cellpadding="0" cellspacing="0" border="0" style="width:600px;max-width:600px;background-color:#f7fdfe;">

<!-- Header: Boost logo -->
<tr>
<td style="padding:18px 16px;" align="left">
<a href="{{ scheme }}://{{ host }}/" style="text-decoration:none;">
<img src="{% large_static 'img/emails/email-logo.png' %}" width="184" height="43" alt="Boost" style="display:block;width:184px;height:43px;">
Comment thread
ycanales marked this conversation as resolved.
</a>
</td>
</tr>

<!-- Content -->
<tr>
<td class="email-px" style="padding:8px 16px 32px 16px;">

{% block hero %}{% endblock %}

<!-- Verify card -->
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" class="email-card" style="background-color:#ffffff;border:1px solid #e2e2e4;border-radius:12px;">
<tr>
<td style="padding:24px 20px;">
<!-- Title -->
<div style="font-family:Arial,Helvetica,sans-serif;font-size:24px;line-height:1.2;letter-spacing:-0.24px;color:#050816;text-align:center;">
{% block card_title %}{% endblock %}
</div>
<!-- Divider -->
<div style="height:1px;line-height:1px;font-size:0;margin:20px 0;background-color:#e2e2e4;">&nbsp;</div>
<!-- Body -->
<div style="font-family:'Segoe UI',-apple-system,BlinkMacSystemFont,Roboto,Helvetica,Arial,sans-serif;font-weight:300;font-size:14px;line-height:1.45;color:#050816;">
{% block card_body %}{% endblock %}
</div>
<!-- CTA button -->
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="margin:16px 0 0 0;">
<tr>
<td bgcolor="#ffa000" align="center" style="border-radius:4px;">
<a href="{{ action_url|default:'#' }}" style="display:block;padding:12px 24px;font-family:Arial,Helvetica,sans-serif;font-size:12px;font-weight:bold;line-height:1.5;letter-spacing:-0.24px;color:#050816;text-decoration:none;border-radius:4px;">{% block cta_label %}{% endblock %}</a>
</td>
</tr>
</table>
<!-- Fallback URL -->
<div style="font-family:'Segoe UI',-apple-system,BlinkMacSystemFont,Roboto,Helvetica,Arial,sans-serif;font-weight:300;font-size:12px;line-height:1.45;color:#71737b;margin:16px 0 0 0;">
Or copy and paste this URL into your browser:<br>
<a href="{{ action_url|default:'#' }}" style="color:#71737b;text-decoration:underline;word-break:break-all;">{{ action_url|default:'#' }}</a>
</div>
</td>
</tr>
</table>

{% block after_card %}{% endblock %}

</td>
</tr>

<!-- Footer -->
<tr>
<td style="padding:0 16px;">
<div style="height:1px;line-height:1px;font-size:0;background-color:#e2e2e4;">&nbsp;</div>
</td>
</tr>
<tr>
<td class="email-px" style="padding:24px 16px 40px 16px;">
<!-- Social icons -->
<table role="presentation" cellpadding="0" cellspacing="0" border="0" style="margin:0 0 12px 0;">
<tr>
{% include "emails/_social_icon.html" with url="https://x.com/boost_libraries" name="X" icon="email-social-x.png" %}
{% include "emails/_social_icon.html" with url="https://bsky.app/profile/boost.org" name="Bluesky" icon="email-social-bluesky.png" %}
{% include "emails/_social_icon.html" with url="https://mastodon.social/@boostlibs" name="Mastodon" icon="email-social-mastodon.png" %}
{% include "emails/_social_icon.html" with url="https://www.reddit.com/user/boostlibs/" name="Reddit" icon="email-social-reddit.png" %}
{% include "emails/_social_icon.html" with url="https://github.com/boostorg" name="GitHub" icon="email-social-github.png" %}
{% include "emails/_social_icon.html" with url="https://www.linkedin.com/company/cppalliance/" name="LinkedIn" icon="email-social-linkedin.png" %}
</tr>
</table>
<!-- Footer text -->
<div style="font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:1.45;letter-spacing:0.14px;color:#050816;">
<a href="https://www.boost.org/" style="color:#050816;text-decoration:underline;">Boost.org</a> is supported by grants from <a href="https://cppalliance.org/" style="color:#050816;text-decoration:underline;">The C++ Alliance.</a>
<br><br>
Want to change how you receive these emails?<br>
You can <a href="{{ preferences_url|default:'#' }}" style="color:#050816;text-decoration:underline;">update your preferences</a> or <a href="{{ unsubscribe_url|default:'#' }}" style="color:#050816;text-decoration:underline;">unsubscribe from this list</a>.
</div>
</td>
</tr>

</table>
<!--[if mso]></td></tr></table><![endif]-->
</td>
</tr>
</table>
</body>
</html>
43 changes: 43 additions & 0 deletions templates/emails/confirm_email.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{% extends "emails/base_email.html" %}
{% load custom_static %}

{% comment %}
Sign-up confirmation email.
Subject: "Confirm your email" (see confirm_email_subject.txt).
Context: first_name, action_url (confirmation URL), scheme, host,
confirmation_link_lifetime (e.g. "3 days").
{% endcomment %}

{% block title %}Confirm your email{% endblock %}

{% block preheader %}Confirm your email address to activate your Boost account.{% endblock %}

{% block hero %}
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0">
<tr>
<td style="padding:0 0 24px 0;">
<img src="{% large_static 'img/emails/email-welcome.jpg' %}" width="568" alt="" style="display:block;width:100%;max-width:568px;height:auto;border-radius:12px;">
</td>
</tr>
</table>
{% endblock %}

{% block card_title %}Welcome to Boost{% endblock %}

{% block card_body %}
<p style="margin:0;">Hi {{ first_name|default:"there" }},</p>
<p style="margin:14px 0 0 0;">Thanks for signing up for Boost. To activate your account, please confirm your email address by clicking the button below.</p>
<p style="margin:14px 0 0 0;">This link expires in {{ confirmation_link_lifetime }}.</p>
{% endblock %}

{% block cta_label %}Confirm my email{% endblock %}

{% block after_card %}
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0">
<tr>
<td style="padding:24px 0 0 0;font-family:'Segoe UI',-apple-system,BlinkMacSystemFont,Roboto,Helvetica,Arial,sans-serif;font-weight:300;font-size:12px;line-height:1.45;color:#71737b;text-align:center;">
If you didn&#39;t create a Boost account, you can safely ignore this email.
</td>
</tr>
</table>
{% endblock %}
19 changes: 19 additions & 0 deletions templates/emails/confirm_email.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Welcome to Boost

Hi {{ first_name|default:"there" }},

Thanks for signing up for Boost. To activate your account, please confirm
your email address by visiting the link below.

This link expires in {{ confirmation_link_lifetime }}.

Confirm my email:
{{ action_url }}

If you didn't create a Boost account, you can safely ignore this email.

--
Boost.org is supported by grants from The C++ Alliance.

Want to change how you receive these emails?
You can update your preferences or unsubscribe from this list.
1 change: 1 addition & 0 deletions templates/emails/confirm_email_subject.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Confirm your email
52 changes: 52 additions & 0 deletions templates/emails/password_reset.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{% extends "emails/base_email.html" %}
{% load custom_static %}

{% comment %}
Password reset email.
Subject: "Password reset link" (see password_reset_subject.txt).
Context: first_name, user_email, action_url (reset URL), scheme, host,
password_reset_link_lifetime (e.g. "3 days").
{% endcomment %}

{% block title %}Password reset link{% endblock %}

{% block preheader %}Reset the password for your Boost account.{% endblock %}

{% block hero %}
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0">
<tr>
<td style="padding:0 0 24px 0;">
<img src="{% large_static 'img/emails/email-password-reset.jpg' %}" width="568" alt="" style="display:block;width:100%;max-width:568px;height:auto;border-radius:12px;">
</td>
</tr>
</table>
{% endblock %}

{% block card_title %}Reset your password{% endblock %}

{% block card_body %}
<p style="margin:0;">Hi {{ first_name|default:"there" }},</p>
<p style="margin:14px 0 0 0;">We received a request to reset the password for your Boost account ({{ user_email }}).</p>
<p style="margin:14px 0 0 0;">Click the button below to set a new password. This link will expire in {{ password_reset_link_lifetime }}.</p>
{% endblock %}

{% block cta_label %}Reset my password{% endblock %}

{% block after_card %}
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="margin:24px 0 0 0;">
<tr>
<td style="background-color:#fef7f7;border:1px solid #f2c9c9;border-radius:8px;padding:12px 14px;">
<table role="presentation" cellpadding="0" cellspacing="0" border="0">
<tr>
<td valign="top" style="padding:2px 12px 0 0;">
<img src="{% large_static 'img/emails/email-warning.png' %}" width="16" height="16" alt="" style="display:block;width:16px;height:16px;">
</td>
<td valign="top" style="font-family:'Segoe UI',-apple-system,BlinkMacSystemFont,Roboto,Helvetica,Arial,sans-serif;font-weight:300;font-size:12px;line-height:1.45;color:#050816;">
Didn&#39;t ask for this? Maybe someone typed your email by accident. You can ignore this email safely &mdash; your password will stay the same.
</td>
</tr>
</table>
</td>
</tr>
</table>
{% endblock %}
20 changes: 20 additions & 0 deletions templates/emails/password_reset.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Reset your password

Hi {{ first_name|default:"there" }},

We received a request to reset the password for your Boost account
({{ user_email }}).

Click the link below to set a new password. This link will expire in {{ password_reset_link_lifetime }}.

Reset my password:
{{ action_url }}

Didn't ask for this? Maybe someone typed your email by accident. You can
ignore this email safely - your password will stay the same.

--
Boost.org is supported by grants from The C++ Alliance.

Want to change how you receive these emails?
You can update your preferences or unsubscribe from this list.
1 change: 1 addition & 0 deletions templates/emails/password_reset_subject.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Password reset link
Loading
Loading