🐛(domains) use a dedicated mail to invite user to manage domain

- modify models to allow to specify path to mail template
- rename team invitation template
- fix logo and text used for domain invitation email
This commit is contained in:
Sabrina Demagny
2025-03-10 10:04:00 +01:00
parent 185b87da40
commit ebc2b02d22
12 changed files with 156 additions and 24 deletions

View File

@@ -16,6 +16,7 @@ and this project adheres to
### Fixed
- 🐛(domains) use a dedicated mail to invite user to manage domain
- 🐛(mailbox) fix mailbox creation email language
## [1.13.1] - 2025-03-04

View File

@@ -908,6 +908,9 @@ class BaseInvitation(BaseModel):
related_name="invitations",
)
MAIL_TEMPLATE_HTML = None
MAIL_TEMPLATE_TXT = None
class Meta:
abstract = True
@@ -938,17 +941,24 @@ class BaseInvitation(BaseModel):
validity_duration = timedelta(seconds=settings.INVITATION_VALIDITY_DURATION)
return timezone.now() > (self.created_at + validity_duration)
def _get_mail_subject(self):
"""Get the subject of the invitation."""
return gettext("Invitation to join La Régie!")
def _get_mail_context(self):
"""Get the template variables for the invitation."""
return {
"site": Site.objects.get_current(),
}
def email_invitation(self):
"""Email invitation to the user."""
try:
with override(self.issuer.language):
subject = gettext("Invitation to join La Régie!")
template_vars = {
"title": subject,
"site": Site.objects.get_current(),
}
msg_html = render_to_string("mail/html/invitation.html", template_vars)
msg_plain = render_to_string("mail/text/invitation.txt", template_vars)
subject = self._get_mail_subject()
context = self._get_mail_context()
msg_html = render_to_string(self.MAIL_TEMPLATE_HTML, context)
msg_plain = render_to_string(self.MAIL_TEMPLATE_TXT, context)
mail.send_mail(
subject,
msg_plain,
@@ -974,6 +984,9 @@ class Invitation(BaseInvitation):
max_length=20, choices=RoleChoices.choices, default=RoleChoices.MEMBER
)
MAIL_TEMPLATE_HTML = "mail/html/team_invitation.html"
MAIL_TEMPLATE_TXT = "mail/text/team_invitation.txt"
class Meta:
db_table = "people_invitation"
verbose_name = _("Team invitation")

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

@@ -262,7 +262,7 @@ def test_models_team_invitations_email():
assert email.subject == "Invitation à rejoindre La Régie !"
email_content = " ".join(email.body.split())
assert "Invitation à rejoindre La Régie !" in email_content
assert "Nous sommes ravis de vous accueillir" in email_content
assert "[//example.com]" in email_content

View File

@@ -3,25 +3,37 @@
from django.urls import path
from .views import (
DebugViewHtml,
DebugViewMaildomainInvitationHtml,
DebugViewMaildomainInvitationTxt,
DebugViewNewMailboxHtml,
DebugViewTxt,
DebugViewTeamInvitationHtml,
DebugViewTeamInvitationTxt,
)
urlpatterns = [
path(
"__debug__/mail/invitation_html",
DebugViewHtml.as_view(),
name="debug.mail.invitation_html",
"__debug__/mail/team_invitation_html",
DebugViewTeamInvitationHtml.as_view(),
name="debug.mail.team_invitation_html",
),
path(
"__debug__/mail/invitation_txt",
DebugViewTxt.as_view(),
name="debug.mail.invitation_txt",
"__debug__/mail/team_invitation_txt",
DebugViewTeamInvitationTxt.as_view(),
name="debug.mail.team_invitation_txt",
),
path(
"__debug__/mail/new_mailbox_html",
DebugViewNewMailboxHtml.as_view(),
name="debug.mail.new_mailbox_html",
),
path(
"__debug__/mail/maildomain_invitation_txt",
DebugViewMaildomainInvitationTxt.as_view(),
name="debug.mail.maildomain_invitation_txt",
),
path(
"__debug__/mail/maildomain_invitation_html",
DebugViewMaildomainInvitationHtml.as_view(),
name="debug.mail.maildomain_invitation_html",
),
]

View File

@@ -8,25 +8,49 @@ class DebugBaseView(TemplateView):
def get_context_data(self, **kwargs):
"""Generates sample datas to have a valid debug email"""
context = super().get_context_data(**kwargs)
context["title"] = "Development email preview"
return context
class DebugViewHtml(DebugBaseView):
"""Debug View for HTML Email Layout"""
# TEAM INVITATION
class DebugViewTeamInvitationHtml(DebugBaseView):
"""Debug view for team invitation html email layout"""
template_name = "mail/html/invitation.html"
template_name = "mail/html/team_invitation.html"
class DebugViewTxt(DebugBaseView):
"""Debug View for Text Email Layout"""
class DebugViewTeamInvitationTxt(DebugBaseView):
"""Debug view for team invitation text email layout"""
template_name = "mail/text/invitation.txt"
template_name = "mail/text/team_invitation.txt"
# MAIL DOMAIN INVITATION
class DebugViewMaildomainInvitationBase(DebugBaseView):
"""Debug view for mail domain invitation base email layout"""
def get_context_data(self, **kwargs):
"""Add some fake context data for mail domain invitation email layout"""
context = super().get_context_data(**kwargs)
context["domain"] = "example.com"
context["role"] = "owner"
return context
class DebugViewMaildomainInvitationHtml(DebugViewMaildomainInvitationBase):
"""Debug view for mail domain invitation html email layout"""
template_name = "mail/html/maildomain_invitation.html"
class DebugViewMaildomainInvitationTxt(DebugViewMaildomainInvitationBase):
"""Debug view for mail domain invitation text email layout"""
template_name = "mail/text/maildomain_invitation.txt"
# NEW MAILBOX
class DebugViewNewMailboxHtml(DebugBaseView):
"""Debug view for new mailbox email layout"""

View File

@@ -7,6 +7,7 @@ from django.contrib.auth.base_user import AbstractBaseUser
from django.core import exceptions, validators
from django.db import models
from django.utils.text import slugify
from django.utils.translation import gettext
from django.utils.translation import gettext_lazy as _
from core.models import BaseInvitation, BaseModel, Organization, User
@@ -294,6 +295,9 @@ class MailDomainInvitation(BaseInvitation):
default=MailDomainRoleChoices.VIEWER,
)
MAIL_TEMPLATE_HTML = "mail/html/maildomain_invitation.html"
MAIL_TEMPLATE_TXT = "mail/text/maildomain_invitation.txt"
class Meta:
db_table = "people_mail_domain_invitation"
verbose_name = _("Mail domain invitation")
@@ -307,6 +311,18 @@ class MailDomainInvitation(BaseInvitation):
def __str__(self):
return f"{self.email} invited to {self.domain}"
def _get_mail_subject(self):
"""Get the subject of the invitation."""
return gettext("[La Suite] You have been invited to join La Régie")
def _get_mail_context(self):
"""Get the template variables for the invitation."""
return {
**super()._get_mail_context(),
"domain": self.domain.name,
"role": self.get_role_display(),
}
def get_abilities(self, user):
"""Compute and return abilities for a given user."""
can_delete = False

View File

@@ -3,6 +3,8 @@ Tests for MailDomainInvitations API endpoint in People's app mailbox_manager.
Focus on "create" action.
"""
from django.core import mail
import pytest
from rest_framework import status
from rest_framework.test import APIClient
@@ -70,12 +72,18 @@ def test_api_domain_invitations__admin_should_create_invites(role):
client = APIClient()
client.force_login(user)
assert len(mail.outbox) == 0
response = client.post(
f"/api/v1.0/mail-domains/{domain.slug}/invitations/",
invitation_values,
format="json",
)
assert response.status_code == status.HTTP_201_CREATED
assert len(mail.outbox) == 1
email = mail.outbox[0]
assert email.to == [invitation_values["email"]]
assert email.subject == "[La Suite] You have been invited to join La Régie"
def test_api_domain_invitations__viewers_should_not_invite_to_manage_domains():

View File

@@ -0,0 +1,58 @@
<mjml>
<mj-include path="./partial/header.mjml" />
<mj-body mj-class="bg--blue-100">
<mj-wrapper css-class="wrapper" padding="10px">
<mj-section>
<mj-column>
<mj-image align="center" src="{% base64_static 'images/logo-laregie.png' %}" width="157px" align="left" alt="{%trans 'La Régie' %}" padding="10px" />
</mj-column>
</mj-section>
<mj-section mj-class="bg--white-100">
<mj-column>
<mj-divider border-width="1px" border-style="solid" border-color="#EEEEEE" width="100%" padding="10px 20px" />
<mj-text>
<strong>{% trans "Welcome to" %} La Régie</strong>
</mj-text>
<!-- Main Message -->
<mj-text>{% trans "Hello," %}</mj-text>
<mj-text>{% blocktranslate with role=role domain=domain trimmed %}
You have been invited to join La Régie to be {{ role }} of the domain {{ domain }}.
{% endblocktranslate %}
</mj-text>
<mj-text>{% trans "To do so, please log in La Régie via ProConnect, by following this link:" %}</mj-text>
<mj-button href="//{{ site.domain }}" background-color="#000091" color="white" padding-bottom="30px">
<img src="{% base64_static 'images/arrow.png' %}" width="25px" align="left" alt="arrow" background-color="red"/>
{% trans "Go to La Régie"%}
</mj-button>
<!-- end Main Message -->
<mj-text>
<strong>{% trans "What is La Régie?" %}</strong>
</mj-text>
<mj-text>
{% trans "La Régie is the administration center of la Suite, where you can manage users, groups and domains. You will be able to:" %}
<ul>
<li>{% trans "create work groups,"%}</li>
<li>{% trans "invite new members,"%}</li>
<li>{% trans "manage mail domains,"%}</li>
<li>{% trans "create new mail accounts,"%}</li>
<li>{% trans "etc."%}</li>
</ul>
</mj-text>
<mj-text>
{% trans "Welcome aboard!" %}
</mj-text>
<!-- Signature -->
<mj-text>
<p>{% trans "Regards," %}</p>
<p>{% trans "La Suite Team" %}</p>
</mj-text>
<mj-divider border-width="1px" border-style="solid" border-color="#EEEEEE" width="100%" />
<mj-image align="left" src="{% base64_static 'images/logo-footer-mail.png' %}" width="160px" align="left" alt="La Suite" />
</mj-column>
</mj-section>
</mj-wrapper>
</mj-body>
</mjml>