- Implemented email settings configuration in the admin panel, allowing for SMTP settings and notification preferences. - Created a new template for email settings with fields for SMTP host, port, username, password, sender address, and recipients. - Added JavaScript functionality to handle loading, saving, and validating email settings. - Introduced email templates management, enabling the listing, editing, and saving of email templates. - Updated navigation to include links to email settings and templates. - Added tests for email settings and templates to ensure proper functionality and validation.
122 lines
4.0 KiB
Python
122 lines
4.0 KiB
Python
"""Business logic for contact submissions."""
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import smtplib
|
|
from dataclasses import dataclass
|
|
from datetime import datetime, timezone
|
|
from email.message import EmailMessage
|
|
from typing import Any, Dict, Tuple, cast
|
|
|
|
from ..database import save_contact
|
|
from ..metrics import record_submission
|
|
from ..utils import is_valid_email
|
|
from .email_settings import load_effective_smtp_settings
|
|
|
|
|
|
@dataclass
|
|
class ContactSubmission:
|
|
name: str
|
|
email: str
|
|
company: str | None
|
|
message: str
|
|
timeline: str | None
|
|
created_at: str = datetime.now(timezone.utc).isoformat()
|
|
|
|
|
|
def validate_submission(raw: Dict[str, Any]) -> Tuple[ContactSubmission | None, Dict[str, str]]:
|
|
"""Validate the incoming payload and return a submission object."""
|
|
name = (raw.get("name") or "").strip()
|
|
email = (raw.get("email") or "").strip()
|
|
message = (raw.get("message") or "").strip()
|
|
consent = raw.get("consent")
|
|
company = (raw.get("company") or "").strip()
|
|
|
|
errors: Dict[str, str] = {}
|
|
if not name:
|
|
errors["name"] = "Name is required."
|
|
elif len(name) > 200:
|
|
errors["name"] = "Name is too long (max 200 chars)."
|
|
if not is_valid_email(email):
|
|
errors["email"] = "Valid email is required."
|
|
if not message:
|
|
errors["message"] = "Message is required."
|
|
elif len(message) > 5000:
|
|
errors["message"] = "Message is too long (max 5000 chars)."
|
|
if not consent:
|
|
errors["consent"] = "Consent is required."
|
|
if company and len(company) > 200:
|
|
errors["company"] = "Organisation name is too long (max 200 chars)."
|
|
|
|
if errors:
|
|
return None, errors
|
|
|
|
submission = ContactSubmission(
|
|
name=name,
|
|
email=email,
|
|
company=company or None,
|
|
message=message,
|
|
timeline=(raw.get("timeline") or "").strip() or None,
|
|
)
|
|
return submission, {}
|
|
|
|
|
|
def persist_submission(submission: ContactSubmission) -> int:
|
|
"""Persist the submission and update metrics."""
|
|
record_id = save_contact(submission)
|
|
record_submission()
|
|
return record_id
|
|
|
|
|
|
def send_notification(submission: ContactSubmission) -> bool:
|
|
"""Send an email notification for the submission if SMTP is configured."""
|
|
smtp_config = load_effective_smtp_settings()
|
|
|
|
if not smtp_config.get("notify_contact_form"):
|
|
logging.info("Contact notifications disabled; skipping email dispatch")
|
|
return False
|
|
|
|
if not smtp_config.get("host") or not smtp_config.get("recipients"):
|
|
logging.info("SMTP not configured; skipping email notification")
|
|
return False
|
|
|
|
sender = smtp_config.get("sender") or "no-reply@example.com"
|
|
recipients = smtp_config.get("recipients", [])
|
|
host = cast(str, smtp_config.get("host"))
|
|
port = int(smtp_config.get("port") or 0)
|
|
username = smtp_config.get("username") or None
|
|
password = smtp_config.get("password") or ""
|
|
|
|
msg = EmailMessage()
|
|
msg["Subject"] = f"Neue Kontaktanfrage von {submission.name}"
|
|
msg["From"] = sender
|
|
msg["To"] = ", ".join(recipients)
|
|
msg.set_content(
|
|
"\n".join(
|
|
[
|
|
f"Name: {submission.name}",
|
|
f"E-Mail: {submission.email}",
|
|
f"Organisation: {submission.company or '—'}",
|
|
f"Zeithorizont: {submission.timeline or '—'}",
|
|
"",
|
|
"Nachricht:",
|
|
submission.message,
|
|
"",
|
|
f"Eingang: {submission.created_at}",
|
|
]
|
|
)
|
|
)
|
|
|
|
try:
|
|
with smtplib.SMTP(host, port, timeout=15) as server:
|
|
if smtp_config.get("use_tls"):
|
|
server.starttls()
|
|
if username:
|
|
server.login(username, password)
|
|
server.send_message(msg)
|
|
logging.info("Notification email dispatched to %s", recipients)
|
|
return True
|
|
except Exception as exc: # pragma: no cover - SMTP failures are logged only
|
|
logging.error("Failed to send notification email: %s", exc)
|
|
return False
|