✨(backend) add email notifications for screen recordings
Implement backend method to send email notifications when screen recordings are ready for download. Enables users to be alerted when their recordings are available. Frontend implementation to follow in upcoming commits. This service is triggered by the storage hook from Minio. Add minimal unit test coverage for notification service, addressing previous lack of tests in this area. The notification service was responsible for calling the unstable summary service feature, which was developped way too quickly. The email template has been reviewed by a LLM, to make it user-friendly and crystal clear.
This commit is contained in:
committed by
aleb_the_flash
parent
88b7a7dc58
commit
b7d964db56
@@ -12,6 +12,10 @@ PYTHONPATH=/app
|
|||||||
# Mail
|
# Mail
|
||||||
DJANGO_EMAIL_HOST="mailcatcher"
|
DJANGO_EMAIL_HOST="mailcatcher"
|
||||||
DJANGO_EMAIL_PORT=1025
|
DJANGO_EMAIL_PORT=1025
|
||||||
|
DJANGO_EMAIL_BRAND_NAME=La Suite Numérique
|
||||||
|
DJANGO_EMAIL_SUPPORT_EMAIL=test@yopmail.com
|
||||||
|
DJANGO_EMAIL_LOGO_IMG=http://localhost:3000/assets/logo-suite-numerique.png
|
||||||
|
DJANGO_EMAIL_DOMAIN=http://localhost:3000/
|
||||||
|
|
||||||
# Backend url
|
# Backend url
|
||||||
MEET_BASE_URL="http://localhost:8072"
|
MEET_BASE_URL="http://localhost:8072"
|
||||||
@@ -42,3 +46,6 @@ LIVEKIT_API_SECRET=secret
|
|||||||
LIVEKIT_API_KEY=devkey
|
LIVEKIT_API_KEY=devkey
|
||||||
LIVEKIT_API_URL=http://localhost:7880
|
LIVEKIT_API_URL=http://localhost:7880
|
||||||
ALLOW_UNREGISTERED_ROOMS=False
|
ALLOW_UNREGISTERED_ROOMS=False
|
||||||
|
|
||||||
|
# Recording
|
||||||
|
SCREEN_RECORDING_BASE_URL=http://localhost:3000/recordings
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
"""Service to notify external services when a new recording is ready."""
|
"""Service to notify external services when a new recording is ready."""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import smtplib
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.core.mail import send_mail
|
||||||
|
from django.template.loader import render_to_string
|
||||||
|
from django.utils.translation import get_language, override
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
@@ -21,10 +26,7 @@ class NotificationService:
|
|||||||
return self._notify_summary_service(recording)
|
return self._notify_summary_service(recording)
|
||||||
|
|
||||||
if recording.mode == models.RecordingModeChoices.SCREEN_RECORDING:
|
if recording.mode == models.RecordingModeChoices.SCREEN_RECORDING:
|
||||||
logger.warning(
|
return self._notify_user_by_email(recording)
|
||||||
"Screen recording mode not implemented for recording %s", recording.id
|
|
||||||
)
|
|
||||||
return False
|
|
||||||
|
|
||||||
logger.error(
|
logger.error(
|
||||||
"Unknown recording mode %s for recording %s",
|
"Unknown recording mode %s for recording %s",
|
||||||
@@ -33,6 +35,59 @@ class NotificationService:
|
|||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _notify_user_by_email(recording) -> bool:
|
||||||
|
"""
|
||||||
|
Send an email notification to recording owners when their recording is ready.
|
||||||
|
|
||||||
|
The email includes a direct link that redirects owners to a dedicated download
|
||||||
|
page in the frontend where they can access their specific recording.
|
||||||
|
"""
|
||||||
|
|
||||||
|
owner_accesses = models.RecordingAccess.objects.select_related("user").filter(
|
||||||
|
role=models.RoleChoices.OWNER,
|
||||||
|
recording_id=recording.id,
|
||||||
|
)
|
||||||
|
|
||||||
|
if not owner_accesses:
|
||||||
|
logger.error("No owner found for recording %s", recording.id)
|
||||||
|
return False
|
||||||
|
|
||||||
|
language = get_language()
|
||||||
|
|
||||||
|
context = {
|
||||||
|
"brandname": settings.EMAIL_BRAND_NAME,
|
||||||
|
"support_email": settings.EMAIL_SUPPORT_EMAIL,
|
||||||
|
"logo_img": settings.EMAIL_LOGO_IMG,
|
||||||
|
"domain": settings.EMAIL_DOMAIN,
|
||||||
|
"room_name": recording.room.name,
|
||||||
|
"recording_date": recording.created_at.strftime("%A %d %B %Y"),
|
||||||
|
"recording_time": recording.created_at.strftime("%H:%M"),
|
||||||
|
"link": f"{settings.SCREEN_RECORDING_BASE_URL}/{recording.id}",
|
||||||
|
}
|
||||||
|
|
||||||
|
emails = [access.user.email for access in owner_accesses]
|
||||||
|
|
||||||
|
with override(language):
|
||||||
|
msg_html = render_to_string("mail/html/screen_recording.html", context)
|
||||||
|
msg_plain = render_to_string("mail/text/screen_recording.txt", context)
|
||||||
|
subject = str(_("Your recording is ready")) # Force translation
|
||||||
|
|
||||||
|
try:
|
||||||
|
send_mail(
|
||||||
|
subject.capitalize(),
|
||||||
|
msg_plain,
|
||||||
|
settings.EMAIL_FROM,
|
||||||
|
emails,
|
||||||
|
html_message=msg_html,
|
||||||
|
fail_silently=False,
|
||||||
|
)
|
||||||
|
except smtplib.SMTPException as exception:
|
||||||
|
logger.error("notification to %s was not sent: %s", emails, exception)
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _notify_summary_service(recording):
|
def _notify_summary_service(recording):
|
||||||
"""Notify summary service about a new recording."""
|
"""Notify summary service about a new recording."""
|
||||||
|
|||||||
167
src/backend/core/tests/recording/event/test_notification.py
Normal file
167
src/backend/core/tests/recording/event/test_notification.py
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
"""
|
||||||
|
Test event notification.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# pylint: disable=E1128,W0621,W0613,W0212
|
||||||
|
|
||||||
|
import smtplib
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
from django.contrib.sites.models import Site
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from core import factories, models
|
||||||
|
from core.recording.event.notification import NotificationService, notification_service
|
||||||
|
|
||||||
|
pytestmark = pytest.mark.django_db
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mocked_current_site():
|
||||||
|
"""Mocks the Site.objects.get_current()to return a controlled predefined domain."""
|
||||||
|
|
||||||
|
site_mock = mock.Mock()
|
||||||
|
site_mock.domain = "test-domain.com"
|
||||||
|
|
||||||
|
with mock.patch.object(
|
||||||
|
Site.objects, "get_current", return_value=site_mock
|
||||||
|
) as patched:
|
||||||
|
yield patched
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch.object(NotificationService, "_notify_summary_service", return_value=True)
|
||||||
|
def test_notify_external_services_transcript_mode(mock_notify_summary):
|
||||||
|
"""Test notification routing for transcript mode recordings."""
|
||||||
|
|
||||||
|
service = NotificationService()
|
||||||
|
|
||||||
|
recording = factories.RecordingFactory(mode=models.RecordingModeChoices.TRANSCRIPT)
|
||||||
|
result = service.notify_external_services(recording)
|
||||||
|
|
||||||
|
assert result is True
|
||||||
|
mock_notify_summary.assert_called_once_with(recording)
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch.object(NotificationService, "_notify_user_by_email", return_value=True)
|
||||||
|
def test_notify_external_services_screen_recording_mode(mock_notify_email):
|
||||||
|
"""Test notification routing for screen recording mode."""
|
||||||
|
|
||||||
|
service = NotificationService()
|
||||||
|
|
||||||
|
recording = factories.RecordingFactory(
|
||||||
|
mode=models.RecordingModeChoices.SCREEN_RECORDING
|
||||||
|
)
|
||||||
|
|
||||||
|
result = service.notify_external_services(recording)
|
||||||
|
|
||||||
|
assert result is True
|
||||||
|
mock_notify_email.assert_called_once_with(recording)
|
||||||
|
|
||||||
|
|
||||||
|
def test_notify_external_services_unknown_mode(caplog):
|
||||||
|
"""Test notification for unknown recording mode."""
|
||||||
|
recording = factories.RecordingFactory()
|
||||||
|
|
||||||
|
# Bypass validation
|
||||||
|
recording.mode = "unknown"
|
||||||
|
|
||||||
|
service = NotificationService()
|
||||||
|
|
||||||
|
result = service.notify_external_services(recording)
|
||||||
|
|
||||||
|
assert result is False
|
||||||
|
assert f"Unknown recording mode unknown for recording {recording.id}" in caplog.text
|
||||||
|
|
||||||
|
|
||||||
|
def test_notify_user_by_email_success(mocked_current_site, settings):
|
||||||
|
"""Test successful email notification to recording owners."""
|
||||||
|
settings.EMAIL_BRAND_NAME = "ACME"
|
||||||
|
settings.EMAIL_SUPPORT_EMAIL = "support@acme.com"
|
||||||
|
settings.EMAIL_LOGO_IMG = "https://acme.com/logo"
|
||||||
|
settings.SCREEN_RECORDING_BASE_URL = "https://acme.com/recordings"
|
||||||
|
settings.EMAIL_FROM = "notifications@acme.com"
|
||||||
|
|
||||||
|
recording = factories.RecordingFactory(room__name="Conference Room A")
|
||||||
|
|
||||||
|
owners = [
|
||||||
|
factories.UserRecordingAccessFactory(
|
||||||
|
recording=recording, role=models.RoleChoices.OWNER
|
||||||
|
).user,
|
||||||
|
factories.UserRecordingAccessFactory(
|
||||||
|
recording=recording, role=models.RoleChoices.OWNER
|
||||||
|
).user,
|
||||||
|
]
|
||||||
|
owner_emails = [owner.email for owner in owners]
|
||||||
|
|
||||||
|
# Create non-owner users to verify they don't receive emails
|
||||||
|
factories.UserRecordingAccessFactory(
|
||||||
|
recording=recording, role=models.RoleChoices.MEMBER
|
||||||
|
)
|
||||||
|
factories.UserRecordingAccessFactory(
|
||||||
|
recording=recording, role=models.RoleChoices.ADMIN
|
||||||
|
)
|
||||||
|
|
||||||
|
notification_service = NotificationService()
|
||||||
|
|
||||||
|
with mock.patch("core.recording.event.notification.send_mail") as mock_send_mail:
|
||||||
|
result = notification_service._notify_user_by_email(recording)
|
||||||
|
|
||||||
|
assert result is True
|
||||||
|
mock_send_mail.assert_called_once()
|
||||||
|
|
||||||
|
subject, body, sender, recipients = mock_send_mail.call_args[0]
|
||||||
|
|
||||||
|
assert subject == "Your recording is ready"
|
||||||
|
|
||||||
|
# Verify email contains expected content
|
||||||
|
required_content = [
|
||||||
|
"ACME", # Brand name
|
||||||
|
"support@acme.com", # Support email
|
||||||
|
"https://acme.com/logo", # Logo URL
|
||||||
|
f"https://acme.com/recordings/{recording.id}", # Recording link
|
||||||
|
"Conference Room A", # Room name
|
||||||
|
recording.created_at.strftime("%A %d %B %Y"), # Formatted date
|
||||||
|
recording.created_at.strftime("%H:%M"), # Formatted time
|
||||||
|
]
|
||||||
|
|
||||||
|
for content in required_content:
|
||||||
|
assert content in body
|
||||||
|
|
||||||
|
assert sender == "notifications@acme.com"
|
||||||
|
|
||||||
|
# Verify all owners received the email (order-independent comparison)
|
||||||
|
assert sorted(recipients) == sorted(owner_emails)
|
||||||
|
|
||||||
|
|
||||||
|
def test_notify_user_by_email_no_owners(mocked_current_site, caplog):
|
||||||
|
"""Test email notification when no owners are found."""
|
||||||
|
|
||||||
|
# Recording with no access
|
||||||
|
recording = factories.RecordingFactory()
|
||||||
|
|
||||||
|
result = notification_service._notify_user_by_email(recording)
|
||||||
|
|
||||||
|
assert result is False
|
||||||
|
assert f"No owner found for recording {recording.id}" in caplog.text
|
||||||
|
|
||||||
|
|
||||||
|
def test_notify_user_by_email_smtp_exception(mocked_current_site, caplog):
|
||||||
|
"""Test email notification when an exception occurs."""
|
||||||
|
|
||||||
|
recording = factories.RecordingFactory(room__name="Conference Room A")
|
||||||
|
owner = factories.UserRecordingAccessFactory(
|
||||||
|
recording=recording, role=models.RoleChoices.OWNER
|
||||||
|
).user
|
||||||
|
|
||||||
|
notification_service = NotificationService()
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"core.recording.event.notification.send_mail",
|
||||||
|
side_effect=smtplib.SMTPException("SMTP Error"),
|
||||||
|
) as mock_send_mail:
|
||||||
|
result = notification_service._notify_user_by_email(recording)
|
||||||
|
|
||||||
|
assert result is False
|
||||||
|
mock_send_mail.assert_called_once()
|
||||||
|
assert f"notification to ['{owner.email}'] was not sent" in caplog.text
|
||||||
@@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-04-14 19:00+0000\n"
|
"POT-Creation-Date: 2025-04-14 19:04+0000\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@@ -309,6 +309,10 @@ msgstr ""
|
|||||||
msgid "Invalid token"
|
msgid "Invalid token"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: core/recording/event/notification.py:81
|
||||||
|
msgid "Your recording is ready"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: core/templates/mail/html/invitation.html:160
|
#: core/templates/mail/html/invitation.html:160
|
||||||
#: core/templates/mail/text/invitation.txt:3
|
#: core/templates/mail/text/invitation.txt:3
|
||||||
msgid "La Suite Numérique"
|
msgid "La Suite Numérique"
|
||||||
@@ -394,6 +398,63 @@ msgstr ""
|
|||||||
msgid "The La Suite Numérique Team"
|
msgid "The La Suite Numérique Team"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: core/templates/mail/html/screen_recording.html:159
|
||||||
|
#: core/templates/mail/text/screen_recording.txt:3
|
||||||
|
msgid "Logo email"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: core/templates/mail/html/screen_recording.html:188
|
||||||
|
#: core/templates/mail/text/screen_recording.txt:6
|
||||||
|
msgid "Your recording is ready!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: core/templates/mail/html/screen_recording.html:195
|
||||||
|
#: core/templates/mail/text/screen_recording.txt:8
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
" Your recording of \"%(room_name)s\" on %(recording_date)s at "
|
||||||
|
"%(recording_time)s is now ready to download. "
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: core/templates/mail/html/screen_recording.html:201
|
||||||
|
#: core/templates/mail/text/screen_recording.txt:10
|
||||||
|
msgid "To keep this recording permanently:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: core/templates/mail/html/screen_recording.html:203
|
||||||
|
#: core/templates/mail/text/screen_recording.txt:12
|
||||||
|
msgid "Click the \\"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: core/templates/mail/html/screen_recording.html:204
|
||||||
|
#: core/templates/mail/text/screen_recording.txt:13
|
||||||
|
msgid "Use the \\"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: core/templates/mail/html/screen_recording.html:205
|
||||||
|
#: core/templates/mail/text/screen_recording.txt:14
|
||||||
|
msgid "Save the file to your preferred location"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: core/templates/mail/html/screen_recording.html:216
|
||||||
|
#: core/templates/mail/text/screen_recording.txt:16
|
||||||
|
msgid "Open"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: core/templates/mail/html/screen_recording.html:225
|
||||||
|
#: core/templates/mail/text/screen_recording.txt:18
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
" If you have any questions or need assistance, please contact our support "
|
||||||
|
"team at %(support_email)s. "
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: core/templates/mail/html/screen_recording.html:240
|
||||||
|
#: core/templates/mail/text/screen_recording.txt:22
|
||||||
|
#, python-format
|
||||||
|
msgid " Thank you for using %(brandname)s. "
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: core/templates/mail/text/invitation.txt:8
|
#: core/templates/mail/text/invitation.txt:8
|
||||||
msgid "Welcome to Meet"
|
msgid "Welcome to Meet"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-04-14 19:00+0000\n"
|
"POT-Creation-Date: 2025-04-14 19:04+0000\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@@ -309,6 +309,10 @@ msgstr ""
|
|||||||
msgid "Invalid token"
|
msgid "Invalid token"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: core/recording/event/notification.py:81
|
||||||
|
msgid "Your recording is ready"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: core/templates/mail/html/invitation.html:160
|
#: core/templates/mail/html/invitation.html:160
|
||||||
#: core/templates/mail/text/invitation.txt:3
|
#: core/templates/mail/text/invitation.txt:3
|
||||||
msgid "La Suite Numérique"
|
msgid "La Suite Numérique"
|
||||||
@@ -394,6 +398,63 @@ msgstr ""
|
|||||||
msgid "The La Suite Numérique Team"
|
msgid "The La Suite Numérique Team"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: core/templates/mail/html/screen_recording.html:159
|
||||||
|
#: core/templates/mail/text/screen_recording.txt:3
|
||||||
|
msgid "Logo email"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: core/templates/mail/html/screen_recording.html:188
|
||||||
|
#: core/templates/mail/text/screen_recording.txt:6
|
||||||
|
msgid "Your recording is ready!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: core/templates/mail/html/screen_recording.html:195
|
||||||
|
#: core/templates/mail/text/screen_recording.txt:8
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
" Your recording of \"%(room_name)s\" on %(recording_date)s at "
|
||||||
|
"%(recording_time)s is now ready to download. "
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: core/templates/mail/html/screen_recording.html:201
|
||||||
|
#: core/templates/mail/text/screen_recording.txt:10
|
||||||
|
msgid "To keep this recording permanently:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: core/templates/mail/html/screen_recording.html:203
|
||||||
|
#: core/templates/mail/text/screen_recording.txt:12
|
||||||
|
msgid "Click the \\"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: core/templates/mail/html/screen_recording.html:204
|
||||||
|
#: core/templates/mail/text/screen_recording.txt:13
|
||||||
|
msgid "Use the \\"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: core/templates/mail/html/screen_recording.html:205
|
||||||
|
#: core/templates/mail/text/screen_recording.txt:14
|
||||||
|
msgid "Save the file to your preferred location"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: core/templates/mail/html/screen_recording.html:216
|
||||||
|
#: core/templates/mail/text/screen_recording.txt:16
|
||||||
|
msgid "Open"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: core/templates/mail/html/screen_recording.html:225
|
||||||
|
#: core/templates/mail/text/screen_recording.txt:18
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
" If you have any questions or need assistance, please contact our support "
|
||||||
|
"team at %(support_email)s. "
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: core/templates/mail/html/screen_recording.html:240
|
||||||
|
#: core/templates/mail/text/screen_recording.txt:22
|
||||||
|
#, python-format
|
||||||
|
msgid " Thank you for using %(brandname)s. "
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: core/templates/mail/text/invitation.txt:8
|
#: core/templates/mail/text/invitation.txt:8
|
||||||
msgid "Welcome to Meet"
|
msgid "Welcome to Meet"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -318,6 +318,10 @@ class Base(Configuration):
|
|||||||
EMAIL_USE_TLS = values.BooleanValue(False)
|
EMAIL_USE_TLS = values.BooleanValue(False)
|
||||||
EMAIL_USE_SSL = values.BooleanValue(False)
|
EMAIL_USE_SSL = values.BooleanValue(False)
|
||||||
EMAIL_FROM = values.Value("from@example.com")
|
EMAIL_FROM = values.Value("from@example.com")
|
||||||
|
EMAIL_BRAND_NAME = values.Value(None)
|
||||||
|
EMAIL_SUPPORT_EMAIL = values.Value(None)
|
||||||
|
EMAIL_LOGO_IMG = values.Value(None)
|
||||||
|
EMAIL_DOMAIN = values.Value(None)
|
||||||
|
|
||||||
AUTH_USER_MODEL = "core.User"
|
AUTH_USER_MODEL = "core.User"
|
||||||
|
|
||||||
@@ -481,6 +485,9 @@ class Base(Configuration):
|
|||||||
SUMMARY_SERVICE_API_TOKEN = values.Value(
|
SUMMARY_SERVICE_API_TOKEN = values.Value(
|
||||||
None, environ_name="SUMMARY_SERVICE_API_TOKEN", environ_prefix=None
|
None, environ_name="SUMMARY_SERVICE_API_TOKEN", environ_prefix=None
|
||||||
)
|
)
|
||||||
|
SCREEN_RECORDING_BASE_URL = values.Value(
|
||||||
|
None, environ_name="SUMMARY_SERVICE_API_TOKEN", environ_prefix=None
|
||||||
|
)
|
||||||
|
|
||||||
# Marketing and communication settings
|
# Marketing and communication settings
|
||||||
SIGNUP_NEW_USER_TO_MARKETING_EMAIL = values.BooleanValue(
|
SIGNUP_NEW_USER_TO_MARKETING_EMAIL = values.BooleanValue(
|
||||||
|
|||||||
@@ -16,6 +16,10 @@ backend:
|
|||||||
DJANGO_EMAIL_HOST: "mailcatcher"
|
DJANGO_EMAIL_HOST: "mailcatcher"
|
||||||
DJANGO_EMAIL_PORT: 1025
|
DJANGO_EMAIL_PORT: 1025
|
||||||
DJANGO_EMAIL_USE_SSL: False
|
DJANGO_EMAIL_USE_SSL: False
|
||||||
|
DJANGO_EMAIL_BRAND_NAME: "La Suite Numérique"
|
||||||
|
DJANGO_EMAIL_SUPPORT_EMAIL: "test@yopmail.com"
|
||||||
|
DJANGO_EMAIL_LOGO_IMG: https://meet.127.0.0.1.nip.io/assets/logo-suite-numerique.png
|
||||||
|
DJANGO_EMAIL_DOMAIN: https://meet.127.0.0.1.nip.io
|
||||||
OIDC_OP_JWKS_ENDPOINT: https://keycloak.127.0.0.1.nip.io/realms/meet/protocol/openid-connect/certs
|
OIDC_OP_JWKS_ENDPOINT: https://keycloak.127.0.0.1.nip.io/realms/meet/protocol/openid-connect/certs
|
||||||
OIDC_OP_AUTHORIZATION_ENDPOINT: https://keycloak.127.0.0.1.nip.io/realms/meet/protocol/openid-connect/auth
|
OIDC_OP_AUTHORIZATION_ENDPOINT: https://keycloak.127.0.0.1.nip.io/realms/meet/protocol/openid-connect/auth
|
||||||
OIDC_OP_TOKEN_ENDPOINT: https://keycloak.127.0.0.1.nip.io/realms/meet/protocol/openid-connect/token
|
OIDC_OP_TOKEN_ENDPOINT: https://keycloak.127.0.0.1.nip.io/realms/meet/protocol/openid-connect/token
|
||||||
@@ -61,6 +65,7 @@ backend:
|
|||||||
RECORDING_STORAGE_EVENT_TOKEN: password
|
RECORDING_STORAGE_EVENT_TOKEN: password
|
||||||
SUMMARY_SERVICE_ENDPOINT: http://meet-summary:80/api/v1/tasks/
|
SUMMARY_SERVICE_ENDPOINT: http://meet-summary:80/api/v1/tasks/
|
||||||
SUMMARY_SERVICE_API_TOKEN: password
|
SUMMARY_SERVICE_API_TOKEN: password
|
||||||
|
SCREEN_RECORDING_BASE_URL: https://meet.127.0.0.1.nip.io/recordings
|
||||||
SSL_CERT_FILE: /usr/local/lib/python3.12/site-packages/certifi/cacert.pem
|
SSL_CERT_FILE: /usr/local/lib/python3.12/site-packages/certifi/cacert.pem
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,10 @@ backend:
|
|||||||
DJANGO_EMAIL_HOST: "mailcatcher"
|
DJANGO_EMAIL_HOST: "mailcatcher"
|
||||||
DJANGO_EMAIL_PORT: 1025
|
DJANGO_EMAIL_PORT: 1025
|
||||||
DJANGO_EMAIL_USE_SSL: False
|
DJANGO_EMAIL_USE_SSL: False
|
||||||
|
DJANGO_EMAIL_BRAND_NAME: "La Suite Numérique"
|
||||||
|
DJANGO_EMAIL_SUPPORT_EMAIL: "test@yopmail.com"
|
||||||
|
DJANGO_EMAIL_LOGO_IMG: https://meet.127.0.0.1.nip.io/assets/logo-suite-numerique.png
|
||||||
|
DJANGO_EMAIL_DOMAIN: https://meet.127.0.0.1.nip.io
|
||||||
OIDC_OP_JWKS_ENDPOINT: https://fca.integ01.dev-agentconnect.fr/api/v2/jwks
|
OIDC_OP_JWKS_ENDPOINT: https://fca.integ01.dev-agentconnect.fr/api/v2/jwks
|
||||||
OIDC_OP_AUTHORIZATION_ENDPOINT: https://fca.integ01.dev-agentconnect.fr/api/v2/authorize
|
OIDC_OP_AUTHORIZATION_ENDPOINT: https://fca.integ01.dev-agentconnect.fr/api/v2/authorize
|
||||||
OIDC_OP_TOKEN_ENDPOINT: https://fca.integ01.dev-agentconnect.fr/api/v2/token
|
OIDC_OP_TOKEN_ENDPOINT: https://fca.integ01.dev-agentconnect.fr/api/v2/token
|
||||||
@@ -83,6 +87,7 @@ backend:
|
|||||||
RECORDING_STORAGE_EVENT_TOKEN: password
|
RECORDING_STORAGE_EVENT_TOKEN: password
|
||||||
SUMMARY_SERVICE_ENDPOINT: http://meet-summary:80/api/v1/tasks/
|
SUMMARY_SERVICE_ENDPOINT: http://meet-summary:80/api/v1/tasks/
|
||||||
SUMMARY_SERVICE_API_TOKEN: password
|
SUMMARY_SERVICE_API_TOKEN: password
|
||||||
|
SCREEN_RECORDING_BASE_URL: https://meet.127.0.0.1.nip.io/recordings
|
||||||
SIGNUP_NEW_USER_TO_MARKETING_EMAIL: True
|
SIGNUP_NEW_USER_TO_MARKETING_EMAIL: True
|
||||||
BREVO_API_KEY:
|
BREVO_API_KEY:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
|
|||||||
67
src/mail/mjml/screen_recording.mjml
Normal file
67
src/mail/mjml/screen_recording.mjml
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<mjml>
|
||||||
|
<mj-include path="./partial/header.mjml" />
|
||||||
|
|
||||||
|
<mj-body mj-class="bg--blue-100">
|
||||||
|
<mj-wrapper css-class="wrapper" padding="5px 25px 0px 25px">
|
||||||
|
<mj-section css-class="wrapper-logo">
|
||||||
|
<mj-column>
|
||||||
|
<mj-image
|
||||||
|
align="center"
|
||||||
|
src="{{logo_img}}"
|
||||||
|
width="320px"
|
||||||
|
alt="{%trans 'Logo email' %}"
|
||||||
|
/>
|
||||||
|
</mj-column>
|
||||||
|
</mj-section>
|
||||||
|
<mj-section mj-class="bg--white-100" padding="0px 20px 60px 20px">
|
||||||
|
<mj-column>
|
||||||
|
<mj-text align="center">
|
||||||
|
<h1>{% trans "Your recording is ready!"%}</h1>
|
||||||
|
</mj-text>
|
||||||
|
<!-- Main Message -->
|
||||||
|
<mj-text>
|
||||||
|
{% blocktrans %}
|
||||||
|
Your recording of "{{room_name}}" on {{recording_date}} at {{recording_time}} is now ready to download.
|
||||||
|
{% endblocktrans %}
|
||||||
|
</mj-text>
|
||||||
|
<mj-text>
|
||||||
|
<p>{% trans "To keep this recording permanently:" %}</p>
|
||||||
|
<ol>
|
||||||
|
<li>{% trans "Click the \"Open\" button below" %}</li>
|
||||||
|
<li>{% trans "Use the \"Download\" button in the interface" %}</li>
|
||||||
|
<li>{% trans "Save the file to your preferred location" %}</li>
|
||||||
|
</ol>
|
||||||
|
</mj-text>
|
||||||
|
<mj-button
|
||||||
|
href="{{link}}"
|
||||||
|
background-color="#000091"
|
||||||
|
color="white"
|
||||||
|
padding-bottom="30px"
|
||||||
|
>
|
||||||
|
{% trans "Open"%}
|
||||||
|
</mj-button>
|
||||||
|
<mj-text>
|
||||||
|
{% blocktrans %}
|
||||||
|
If you have any questions or need assistance, please contact our support team at {{support_email}}.
|
||||||
|
{% endblocktrans %}
|
||||||
|
</mj-text>
|
||||||
|
<mj-divider
|
||||||
|
border-width="1px"
|
||||||
|
border-style="solid"
|
||||||
|
border-color="#DDDDDD"
|
||||||
|
width="30%"
|
||||||
|
align="center"
|
||||||
|
/>
|
||||||
|
<!-- Signature -->
|
||||||
|
<mj-text>
|
||||||
|
<p>
|
||||||
|
{% blocktrans %}
|
||||||
|
Thank you for using {{brandname}}.
|
||||||
|
{% endblocktrans %}
|
||||||
|
</p>
|
||||||
|
</mj-text>
|
||||||
|
</mj-column>
|
||||||
|
</mj-section>
|
||||||
|
</mj-wrapper>
|
||||||
|
</mj-body>
|
||||||
|
</mjml>
|
||||||
Reference in New Issue
Block a user