
* [x] Create invite emails from admin * [x] Sign up on special invite form (create password and username) * [x] Create email with unpaid orders and payment links * [x] Lodge unpaid orders somewhere in UI for visibility Reviewed-on: https://git.data.coop/data.coop/membersystem/pulls/47 Reviewed-by: valberg <valberg@orn.li> Co-authored-by: Benjamin Bach <benjamin@overtag.dk> Co-committed-by: Benjamin Bach <benjamin@overtag.dk>
126 lines
4.9 KiB
Python
126 lines
4.9 KiB
Python
"""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
|