from celery import shared_task from celery.utils.log import get_task_logger from django.core.mail import send_mail from django.conf import settings from django.utils.http import urlsafe_base64_encode from django.utils.encoding import force_bytes from django.template.loader import render_to_string from .tokens import * from .models import CustomUser logger = get_task_logger(__name__) def send_email_with_context(recipients, subject, message=None, template_name=None, html_template_name=None, context=None): """ General function to send emails with a specific context. Supports rendering plain text and HTML templates. Converts `user` in context to a plain dict to avoid template access to the model. """ if isinstance(recipients, str): recipients = [recipients] html_message = None if template_name or html_template_name: # Best effort to resolve both templates if only one provided if not template_name and html_template_name: template_name = html_template_name.replace(".html", ".txt") if not html_template_name and template_name: html_template_name = template_name.replace(".txt", ".html") ctx = dict(context or {}) # Sanitize user if someone passes the model by mistake if "user" in ctx and not isinstance(ctx["user"], dict): try: ctx["user"] = _build_user_template_ctx(ctx["user"]) except Exception: ctx["user"] = {} message = render_to_string(template_name, ctx) html_message = render_to_string(html_template_name, ctx) try: send_mail( subject=subject, message=message or "", from_email=None, recipient_list=recipients, fail_silently=False, html_message=html_message, ) if settings.EMAIL_BACKEND == 'django.core.mail.backends.console.EmailBackend': logger.debug(f"\nEMAIL OBSAH:\n{message}\nKONEC OBSAHU") return True except Exception as e: logger.error(f"E-mail se neodeslal: {e}") return False def _build_user_template_ctx(user: CustomUser) -> dict: """ Return a plain dict for templates instead of passing the DB model. Provides aliases to avoid template errors (firstname vs first_name). Adds a backward-compatible key 'get_full_name' for templates using `user.get_full_name`. """ first_name = getattr(user, "first_name", "") or "" last_name = getattr(user, "last_name", "") or "" full_name = f"{first_name} {last_name}".strip() return { "id": user.pk, "email": getattr(user, "email", "") or "", "first_name": first_name, "firstname": first_name, # alias for templates using `firstname` "last_name": last_name, "lastname": last_name, # alias for templates using `lastname` "full_name": full_name, "get_full_name": full_name, # compatibility for templates using method-style access } #---------------------------------------------------------------------------------------------------- # This function sends an email to the user for email verification after registration. @shared_task def send_email_verification_task(user_id): try: user = CustomUser.objects.get(pk=user_id) except CustomUser.DoesNotExist: logger.info(f"Task send_email_verification has failed. Invalid User ID was sent.") return 0 uid = urlsafe_base64_encode(force_bytes(user.pk)) # {changed} generate and store a per-user token token = user.generate_email_verification_token() verify_url = f"{settings.FRONTEND_URL}/email-verification/?uidb64={uid}&token={token}" context = { "user": _build_user_template_ctx(user), "action_url": verify_url, "frontend_url": settings.FRONTEND_URL, "cta_label": "Ověřit e‑mail", } send_email_with_context( recipients=user.email, subject="Ověření e‑mailu", template_name="email/email_verification.txt", html_template_name="email/email_verification.html", context=context, ) @shared_task def send_email_test_task(email): context = { "action_url": settings.FRONTEND_URL, "frontend_url": settings.FRONTEND_URL, "cta_label": "Otevřít aplikaci", } send_email_with_context( recipients=email, subject="Testovací e‑mail", template_name="email/test.txt", html_template_name="email/test.html", context=context, ) @shared_task def send_password_reset_email_task(user_id): try: user = CustomUser.objects.get(pk=user_id) except CustomUser.DoesNotExist: logger.info(f"Task send_password_reset_email has failed. Invalid User ID was sent.") return 0 uid = urlsafe_base64_encode(force_bytes(user.pk)) token = password_reset_token.make_token(user) reset_url = f"{settings.FRONTEND_URL}/reset-password/{uid}/{token}" context = { "user": _build_user_template_ctx(user), "action_url": reset_url, "frontend_url": settings.FRONTEND_URL, "cta_label": "Obnovit heslo", } send_email_with_context( recipients=user.email, subject="Obnova hesla", template_name="email/password_reset.txt", html_template_name="email/password_reset.html", context=context, )