"""Send email to members, using templates and contexts for the emails. * We keep everything as plain text for now. * Notice that emails can be multilingual * Generally, an email consists of templates (for body and subject) and a get_context() method. """ from accounting.models import Order from django.contrib import messages from django.contrib.auth.tokens import default_token_generator from django.contrib.sites.shortcuts import get_current_site from django.core.mail.message import EmailMessage from django.http import HttpRequest from django.template import loader from django.utils import translation from django.utils.translation import gettext_lazy as _ from .models import Membership class BaseEmail(EmailMessage): """Send emails via templated body and subjects. This base class is extended for all email functionality. Because all emails are sent to the Member object, we can keep them gathered here, even when they are generated by other apps (like the accounting app). """ template = "membership/email/base.txt" # Optional: Set to a template path for subject template_subject = None default_subject = "SET SUBJECT HERE" def __init__(self, request: HttpRequest, *args, **kwargs) -> None: self.context = kwargs.pop("context", {}) self.user = kwargs.pop("user", None) if self.user: kwargs["to"] = [self.user.email] self.context["user"] = self.user self.context["recipient_name"] = self.user.get_display_name() # Necessary to set request before instantiating body and subject self.request = request kwargs.setdefault("subject", self.get_subject()) kwargs.setdefault("body", self.get_body()) super().__init__(*args, **kwargs) def get_context_data(self) -> dict: """Resolve common context for sending emails. When overwriting, remember to call this via super(). """ c = self.context site = get_current_site(self.request) c["request"] = self.request c["domain"] = site.domain c["site_name"] = site.name c["protocol"] = "http" if self.request and not self.request.is_secure() else "https" return c def get_body(self) -> str: """Build the email body from template and context.""" if self.user and self.user.language_code: with translation.override(self.user.language_code): body = loader.render_to_string(self.template, self.get_context_data()) else: body = loader.render_to_string(self.template, self.get_context_data()) return body def get_subject(self) -> str: """Build the email subject from template or self.default_subject.""" if self.user and self.user.language_code: with translation.override(self.user.language_code): if self.template_subject: subject = loader.render_to_string(self.template_subject, self.get_context_data()).strip() else: subject = str(self.default_subject) elif self.template_subject: subject = loader.render_to_string(self.template_subject, self.get_context_data()).strip() else: subject = str(self.default_subject) return subject def send_with_feedback(self, *, success_msg: str | None = None, no_message: bool = False) -> None: """Send email, possibly adding feedback via django.contrib.messages.""" if not success_msg: success_msg = _("Email successfully sent to {}").format(", ".join(self.to)) try: self.send(fail_silently=False) if not no_message: messages.success(self.request, success_msg) except RuntimeError: messages.error(self.request, _("Not sent, something wrong with the mail server.")) class InviteEmail(BaseEmail): template = "membership/emails/invite.txt" default_subject = _("Invite to data.coop membership") def __init__(self, membership: Membership, request: HttpRequest, *args, **kwargs) -> None: self.membership = membership kwargs["user"] = membership.user super().__init__(request, *args, **kwargs) def get_context_data(self) -> dict: c = super().get_context_data() c["membership"] = self.membership c["token"] = default_token_generator.make_token(self.membership.user) c["referral_code"] = self.membership.referral_code return c class OrderEmail(BaseEmail): template = "membership/emails/order.txt" default_subject = _("Your data.coop order and payment") def __init__(self, order: Order, request: HttpRequest, *args, **kwargs) -> None: self.order = order kwargs["user"] = order.member super().__init__(request, *args, **kwargs) def get_context_data(self) -> dict: c = super().get_context_data() c["order"] = self.order return c