(backend) send email to admins when user ask for access

When a user requests access to a document, an
email is sent to the admins and owners of the
document.
This commit is contained in:
Anthony LC
2025-06-20 15:24:46 +02:00
committed by Manuel Raynaud
parent 878de08b1e
commit 394f91387d
6 changed files with 104 additions and 5 deletions

View File

@@ -36,6 +36,7 @@ from rest_framework.throttling import UserRateThrottle
from core import authentication, enums, models from core import authentication, enums, models
from core.services.ai_services import AIService from core.services.ai_services import AIService
from core.services.collaboration_services import CollaborationService from core.services.collaboration_services import CollaborationService
from core.tasks.mail import send_ask_for_access_mail
from core.utils import extract_attachments, filter_descendants from core.utils import extract_attachments, filter_descendants
from . import permissions, serializers, utils from . import permissions, serializers, utils
@@ -1829,12 +1830,14 @@ class DocumentAskForAccessViewSet(
status=drf.status.HTTP_400_BAD_REQUEST, status=drf.status.HTTP_400_BAD_REQUEST,
) )
models.DocumentAskForAccess.objects.create( ask_for_access = models.DocumentAskForAccess.objects.create(
document=document, document=document,
user=request.user, user=request.user,
role=serializer.validated_data["role"], role=serializer.validated_data["role"],
) )
send_ask_for_access_mail.delay(ask_for_access.id)
return drf.response.Response(status=drf.status.HTTP_201_CREATED) return drf.response.Response(status=drf.status.HTTP_201_CREATED)
@drf.decorators.action(detail=True, methods=["post"]) @drf.decorators.action(detail=True, methods=["post"])

View File

@@ -876,8 +876,8 @@ class Document(MP_Node, BaseModel):
) )
with override(language): with override(language):
msg_html = render_to_string("mail/html/invitation.html", context) msg_html = render_to_string("mail/html/template.html", context)
msg_plain = render_to_string("mail/text/invitation.txt", context) msg_plain = render_to_string("mail/text/template.txt", context)
subject = str(subject) # Force translation subject = str(subject) # Force translation
try: try:
@@ -1221,6 +1221,39 @@ class DocumentAskForAccess(BaseModel):
) )
self.delete() self.delete()
def send_ask_for_access_email(self, email, language=None):
"""
Method allowing a user to send an email notification when asking for access to a document.
"""
language = language or get_language()
sender = self.user
sender_name = sender.full_name or sender.email
sender_name_email = (
f"{sender.full_name:s} ({sender.email})"
if sender.full_name
else sender.email
)
with override(language):
context = {
"title": _("{name} would like access to a document!").format(
name=sender_name
),
"message": _(
"{name} would like access to the following document:"
).format(name=sender_name_email),
}
subject = (
context["title"]
if not self.document.title
else _("{name} is asking for access to the document: {title}").format(
name=sender_name, title=self.document.title
)
)
self.document.send_email(subject, [email], context, language)
class Template(BaseModel): class Template(BaseModel):
"""HTML and CSS code used for formatting the print around the MarkDown body.""" """HTML and CSS code used for formatting the print around the MarkDown body."""

View File

View File

@@ -0,0 +1,24 @@
"""Send mail using celery task."""
from django.conf import settings
from core import models
from impress.celery_app import app
@app.task
def send_ask_for_access_mail(ask_for_access_id):
"""Send mail using celery task."""
# Send email to document owners/admins
ask_for_access = models.DocumentAskForAccess.objects.get(id=ask_for_access_id)
owner_admin_accesses = models.DocumentAccess.objects.filter(
document=ask_for_access.document, role__in=models.PRIVILEGED_ROLES
).select_related("user")
for access in owner_admin_accesses:
if access.user and access.user.email:
ask_for_access.send_ask_for_access_email(
access.user.email,
access.user.language or settings.LANGUAGE_CODE,
)

View File

@@ -2,6 +2,8 @@
import uuid import uuid
from django.core import mail
import pytest import pytest
from rest_framework.test import APIClient from rest_framework.test import APIClient
@@ -41,13 +43,26 @@ def test_api_documents_ask_for_access_create_invalid_document_id():
def test_api_documents_ask_for_access_create_authenticated(): def test_api_documents_ask_for_access_create_authenticated():
"""Authenticated users should be able to create a document ask for access.""" """
document = DocumentFactory() Authenticated users should be able to create a document ask for access.
An email should be sent to document owners and admins to notify them.
"""
owner_user = UserFactory(language="en-us")
admin_user = UserFactory(language="fr-fr")
document = DocumentFactory(
users=[
(owner_user, RoleChoices.OWNER),
(admin_user, RoleChoices.ADMIN),
]
)
user = UserFactory() user = UserFactory()
client = APIClient() client = APIClient()
client.force_login(user) client.force_login(user)
assert len(mail.outbox) == 0
response = client.post(f"/api/v1.0/documents/{document.id}/ask-for-access/") response = client.post(f"/api/v1.0/documents/{document.id}/ask-for-access/")
assert response.status_code == 201 assert response.status_code == 201
@@ -57,6 +72,30 @@ def test_api_documents_ask_for_access_create_authenticated():
role=RoleChoices.READER, role=RoleChoices.READER,
).exists() ).exists()
# Verify emails were sent to both owner and admin
assert len(mail.outbox) == 2
# Check that emails were sent to the right recipients
email_recipients = [email.to[0] for email in mail.outbox]
assert owner_user.email in email_recipients
assert admin_user.email in email_recipients
# Check email content for both users
for email in mail.outbox:
email_content = " ".join(email.body.split())
email_subject = " ".join(email.subject.split())
# Check that the requesting user's name is in the email
user_name = user.full_name or user.email
assert user_name.lower() in email_content.lower()
# Check that the subject mentions access request
assert "access" in email_subject.lower()
# Check that the document title is mentioned if it exists
if document.title:
assert document.title.lower() in email_subject.lower()
def test_api_documents_ask_for_access_create_authenticated_specific_role(): def test_api_documents_ask_for_access_create_authenticated_specific_role():
""" """