diff --git a/pontoon/messaging/emails.py b/pontoon/messaging/emails.py
index b0da2bb8b..2de53b476 100644
--- a/pontoon/messaging/emails.py
+++ b/pontoon/messaging/emails.py
@@ -572,3 +572,33 @@ def send_verification_email(user, link):
msg.send()
log.info(f"Verification email sent to { user.contact_email }.")
+
+
+def send_manual_emails(users, subject, body, is_transactional):
+ """
+ Sends manual emails composed in the Messaging Center.
+ """
+ template = get_template("messaging/emails/manual.html")
+
+ for user in users:
+ body_html = template.render(
+ {
+ "subject": subject,
+ "content": body,
+ "is_transactional": is_transactional,
+ "settings": settings,
+ "user": user,
+ }
+ )
+ body_text = html_to_plain_text_with_links(body_html)
+
+ msg = EmailMultiAlternatives(
+ subject=subject,
+ body=body_text,
+ from_email=settings.DEFAULT_FROM_EMAIL,
+ to=[user.contact_email],
+ )
+ msg.attach_alternative(body_html, "text/html")
+ msg.send()
+
+ log.info(f"Emails sent to {len(users)} users.")
diff --git a/pontoon/messaging/templates/messaging/emails/manual.html b/pontoon/messaging/templates/messaging/emails/manual.html
new file mode 100644
index 000000000..ae471e5c0
--- /dev/null
+++ b/pontoon/messaging/templates/messaging/emails/manual.html
@@ -0,0 +1,22 @@
+{% extends 'messaging/emails/base.html' %}
+
+{% block title %}{{ subject }}{% endblock %}
+
+{% block extend_css %}{% endblock %}
+
+{% block header %}Hello.{% endblock %}
+
+{% block subheader %}{% endblock %}
+
+{% block content_class %}{% endblock %}
+
+{% block content %}
+{{ content|safe }}
+{% endblock %}
+
+{% block footer %}
+{% if not is_transactional %}
+{{ settings.EMAIL_COMMUNICATIONS_FOOTER_PRE_TEXT }}
+To no longer receive emails like these, unsubscribe here: Unsubscribe.
+{% endif %}
+{% endblock %}
diff --git a/pontoon/messaging/views.py b/pontoon/messaging/views.py
index 864f05abf..4633bf01b 100644
--- a/pontoon/messaging/views.py
+++ b/pontoon/messaging/views.py
@@ -2,16 +2,12 @@
import logging
import uuid
-from urllib.parse import urljoin
-
from guardian.decorators import permission_required_or_403
from notifications.signals import notify
-from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.core.exceptions import PermissionDenied
-from django.core.mail import EmailMultiAlternatives
from django.db import transaction
from django.db.models import Count, F
from django.http import JsonResponse
@@ -21,7 +17,8 @@
from pontoon.base.models import Locale, Project, Translation, UserProfile
from pontoon.base.utils import require_AJAX, split_ints
-from pontoon.messaging import forms, utils
+from pontoon.messaging import forms
+from pontoon.messaging.emails import send_manual_emails
from pontoon.messaging.models import Message
@@ -309,35 +306,13 @@ def send_message(request):
log.info(f"Notifications sent to {len(recipients)} users.")
if is_email:
- unsubscribe_url = urljoin(settings.SITE_URL, f"unsubscribe/{uuid}")
- footer = (
- f"""
-{ settings.EMAIL_COMMUNICATIONS_FOOTER_PRE_TEXT }
To no longer receive emails like these, unsubscribe here: Unsubscribe.
- """
- if not is_transactional
- else ""
- )
- html_template = body + footer
- text_template = utils.html_to_plain_text_with_links(html_template)
-
- for recipient in recipients:
- if not recipient.profile.email_communications_enabled:
- continue
-
- unique_id = str(recipient.profile.unique_id)
- text = text_template.replace("{ uuid }", unique_id)
- html = html_template.replace("{ uuid }", unique_id)
-
- msg = EmailMultiAlternatives(
- subject=subject,
- body=text,
- from_email=settings.DEFAULT_FROM_EMAIL,
- to=[recipient.contact_email],
- )
- msg.attach_alternative(html, "text/html")
- msg.send()
+ email_recipients = [
+ recipient
+ for recipient in recipients
+ if recipient.profile.email_communications_enabled
+ ]
- log.info(f"Emails sent to {len(recipients)} users.")
+ send_manual_emails(email_recipients, subject, body, is_transactional)
if not send_to_myself:
message = form.save(commit=False)