Skip to content

Commit

Permalink
Merge pull request #1001 from ita-social-projects/984-admin-user-acti…
Browse files Browse the repository at this point in the history
…ons-dropdown

984 admin user actions dropdown
  • Loading branch information
Chelakhovl authored Dec 6, 2024
2 parents 2fca8d1 + 0e6475f commit 904d905
Show file tree
Hide file tree
Showing 9 changed files with 449 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<!DOCTYPE html>
<html lang="uk">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Нове повідомлення</title>
<style>
body {
font-family: Arial, sans-serif;
color: black;
}
.email-container {
padding: 20px;
border: 1px solid #ddd;
border-radius: 5px;
background-color: #f9f9f9;
}
.email-footer {
margin-top: 20px;
font-size: 12px;
color: gray;
}
img {
max-width: 150px;
}
.email-body {
margin-bottom: 20px;
}
</style>
</head>
<body>
<div class="email-container">
<img src="{{ logo_url }}" alt="CraftMerge Logo" />
<div class="email-body">
<p>Доброго дня, {{ user_name }}!</p>
<p>Ви отримали нове повідомлення:</p>
<p><b>Категорія:</b> {{ category }}</p>
<p><b>Повідомлення:</b></p>
<p>{{ message }}</p>
</div>
<div class="email-footer">
<p>З повагою,</p>
<p>Команда CraftMerge</p>
</div>
</div>
</body>
</html>
109 changes: 109 additions & 0 deletions BackEnd/administration/tests/test_send_message.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
from django.core import mail
from django.conf import settings
from rest_framework import status
from rest_framework.test import APITestCase
from authentication.factories import UserFactory
from utils.administration.send_email_notification import send_email_to_user


class TestSendMessageView(APITestCase):
def setUp(self):
self.admin = UserFactory(is_staff=True, is_active=True)
self.user = UserFactory(is_active=True)
self.client.force_authenticate(self.admin)
self.url = f"/api/admin/users/{self.user.id}/send_message/"

def test_send_message_success(self):
data = {
"email": self.user.email,
"category": "Інше",
"message": "Valid message for testing.",
}
response = self.client.post(self.url, data)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)

def test_send_message_invalid_data(self):
data = {
"email": self.user.email,
"category": "Iнше",
"message": "Short",
}
response = self.client.post(self.url, data)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

def test_send_message_unauthorized(self):
self.client.logout()
data = {
"email": self.user.email,
"category": "Інше",
"message": "Valid message for testing.",
}
response = self.client.post(self.url, data)
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

def test_send_message_user_not_found(self):
url = "/api/admin/users/9999/send_message/"
data = {
"email": "[email protected]",
"category": "Інше",
"message": "Valid message for testing.",
}
response = self.client.post(url, data)
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)


class TestSendEmailFunctionality(APITestCase):
def setUp(self):
self.user = UserFactory(
name="Test", surname="User", email="[email protected]"
)

def send_email(self, category, message_content, email=None):
email = email if email else self.user.email
return send_email_to_user(
user=self.user,
category=category,
message_content=message_content,
email=email,
)

def test_send_email_success(self):
self.send_email(
category="Інше",
message_content="This is a test message.",
email="[email protected]",
)

self.assertEqual(len(mail.outbox), 1)
email = mail.outbox[0]
self.assertEqual(email.subject, "Адміністратор CraftMerge - Інше")
self.assertIn("This is a test message.", email.body)
self.assertEqual(email.to, ["[email protected]"])
self.assertEqual(email.from_email, settings.EMAIL_HOST_USER)

def test_send_email_empty_message(self):
with self.assertRaises(ValueError) as e:
self.send_email(
category="Інше",
message_content="",
email="[email protected]",
)
self.assertEqual(str(e.exception), "Message content cannot be empty.")

def test_send_email_invalid_email(self):
with self.assertRaises(ValueError) as e:
self.send_email(
category="Інше",
message_content="Test message",
email="invalid_email",
)
self.assertEqual(str(e.exception), "Invalid email address.")

def test_send_email_missing_category(self):
with self.assertRaises(ValueError) as e:
self.send_email(
category="",
message_content="Test message",
email="[email protected]",
)
self.assertEqual(str(e.exception), "Category is required.")
6 changes: 6 additions & 0 deletions BackEnd/administration/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
ModerationEmailView,
FeedbackView,
CreateAdminUserView,
SendMessageView,
)

app_name = "administration"
Expand All @@ -28,4 +29,9 @@
path("contacts/", ContactsView.as_view(), name="contacts"),
path("feedback/", FeedbackView.as_view(), name="feedback"),
path("admin_create/", CreateAdminUserView.as_view(), name="admin-create"),
path(
"users/<pk>/send_message/",
SendMessageView.as_view(),
name="send-message",
),
]
41 changes: 40 additions & 1 deletion BackEnd/administration/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
OpenApiExample,
OpenApiResponse,
)

from rest_framework.generics import (
ListAPIView,
RetrieveUpdateDestroyAPIView,
Expand All @@ -30,6 +29,7 @@
from .permissions import IsStaffUser, IsStaffUserOrReadOnly, IsSuperUser
from .serializers import FeedbackSerializer
from utils.administration.send_email_feedback import send_email_feedback
from utils.administration.send_email_notification import send_email_to_user

from django_filters.rest_framework import DjangoFilterBackend
from .filters import UsersFilter
Expand Down Expand Up @@ -199,3 +199,42 @@ def perform_create(self, serializer):
category = serializer.validated_data["category"]

send_email_feedback(email, message, category)


class SendMessageView(CreateAPIView):
"""
API endpoint for sending a custom email message to a specific user.
This view allows administrators to send a message to a user's registered email.
It validates the request payload, retrieves the user based on the provided ID,
and sends the email using the specified category and message content.
"""

queryset = CustomUser.objects.all()
permission_classes = [IsStaffUser]
serializer_class = FeedbackSerializer

def perform_create(self, serializer):
"""
Handles the email sending logic after successful validation.
This method is executed after the request data has been validated
by the serializer. It retrieves the user, validates their existence,
and sends the email with the provided category and message content.
Parameters:
serializer (FeedbackSerializer): The serializer instance containing
the validated data from the request.
"""
user = self.get_object()
email = serializer.validated_data["email"]
category = serializer.validated_data["category"]
message_content = serializer.validated_data["message"]

send_email_to_user(
user=user,
category=category,
message_content=message_content,
email=email,
sender_name="Адміністратор CraftMerge",
)
58 changes: 58 additions & 0 deletions BackEnd/utils/administration/send_email_notification.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string
from django.conf import settings
from django.core.validators import validate_email
from django.core.exceptions import ValidationError

EMAIL_CONTENT_SUBTYPE = "html"
PROTOCOL = "http"


def send_email_to_user(
user,
category,
message_content,
email=None,
sender_name="Адміністратор CraftMerge",
template_name="administration/admin_message_template.html",
):
"""
Sends an email message to the user using the specified template.
:param user: The user object (CustomUser)
:param category: The email category
:param message_content: The message content
:param email: (Optional) The recipient's email
:param sender_name: Name of the sender
:param template_name: The path to the HTML template
"""
if not category:
raise ValueError("Category is required.")
if not message_content.strip():
raise ValueError("Message content cannot be empty.")
try:
validate_email(email or user.email)
except ValidationError:
raise ValueError("Invalid email address.")

context = {
"user_name": f"{user.name} {user.surname}",
"message": message_content,
"category": category,
"sender_name": sender_name,
"logo_url": f"{PROTOCOL}://178.212.110.52/craftMerge-logo.png",
}

email_body = render_to_string(template_name, context)
recipient_email = email if email else user.email

subject = f"{sender_name} - {category}"

email = EmailMultiAlternatives(
subject=subject,
body=email_body,
from_email=settings.EMAIL_HOST_USER,
to=[recipient_email],
)
email.content_subtype = EMAIL_CONTENT_SUBTYPE
email.send(fail_silently=False)
Binary file added FrontEnd/public/craftMerge-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 904d905

Please sign in to comment.