- Implemented `send_subscription_confirmation` function to send a confirmation email upon subscription. - Added a default HTML template for the confirmation email in settings. - Updated the newsletter management page to handle subscription and unsubscription actions. - Created new templates for embedding contact and newsletter forms. - Added styles for the new templates and unified CSS styles across the application. - Updated tests to reflect changes in the newsletter management API endpoints.
145 lines
6.0 KiB
Python
145 lines
6.0 KiB
Python
"""Newsletter subscription routes."""
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
|
|
from flask import Blueprint, jsonify, request, render_template
|
|
|
|
from ..services import newsletter
|
|
|
|
bp = Blueprint("newsletter", __name__)
|
|
|
|
|
|
@bp.route("/api/newsletter", methods=["POST"])
|
|
def subscribe():
|
|
payload = request.form or request.get_json(silent=True) or {}
|
|
email = (payload.get("email") or "").strip()
|
|
|
|
if not newsletter.validate_email(email):
|
|
return jsonify({"status": "error", "message": "Valid email is required."}), 400
|
|
|
|
try:
|
|
created = newsletter.subscribe(email)
|
|
except Exception as exc: # pragma: no cover - errors are logged
|
|
logging.exception("Failed to persist subscriber: %s", exc)
|
|
return jsonify({"status": "error", "message": "Could not store subscription."}), 500
|
|
|
|
if not created:
|
|
logging.info("Newsletter subscription ignored (duplicate): %s", email)
|
|
return jsonify({"status": "error", "message": "Email is already subscribed."}), 409
|
|
|
|
logging.info("New newsletter subscription: %s", email)
|
|
|
|
# Send confirmation email
|
|
email_sent = newsletter.send_subscription_confirmation(email)
|
|
if not email_sent:
|
|
logging.warning("Confirmation email not sent for %s", email)
|
|
|
|
return jsonify({"status": "ok", "message": "Subscribed successfully."}), 201
|
|
|
|
|
|
@bp.route("/api/newsletter", methods=["DELETE"])
|
|
def unsubscribe():
|
|
payload = request.form or request.get_json(silent=True) or {}
|
|
email = (payload.get("email") or "").strip()
|
|
|
|
if not newsletter.validate_email(email):
|
|
return jsonify({"status": "error", "message": "Valid email is required."}), 400
|
|
|
|
try:
|
|
deleted = newsletter.unsubscribe(email)
|
|
except Exception as exc: # pragma: no cover - errors are logged
|
|
logging.exception("Failed to remove subscriber: %s", exc)
|
|
return jsonify({"status": "error", "message": "Could not remove subscription."}), 500
|
|
|
|
if not deleted:
|
|
logging.info(
|
|
"Newsletter unsubscription ignored (not subscribed): %s", email)
|
|
return jsonify({"status": "error", "message": "Email is not subscribed."}), 404
|
|
|
|
logging.info("Newsletter unsubscription: %s", email)
|
|
return jsonify({"status": "ok", "message": "Unsubscribed successfully."}), 200
|
|
|
|
|
|
@bp.route("/api/newsletter", methods=["PUT"])
|
|
def update_subscription():
|
|
payload = request.form or request.get_json(silent=True) or {}
|
|
old_email = (payload.get("old_email") or "").strip()
|
|
new_email = (payload.get("new_email") or "").strip()
|
|
|
|
if not newsletter.validate_email(old_email) or not newsletter.validate_email(new_email):
|
|
return jsonify({"status": "error", "message": "Valid old and new emails are required."}), 400
|
|
|
|
try:
|
|
updated = newsletter.update_email(old_email, new_email)
|
|
except Exception as exc: # pragma: no cover - errors are logged
|
|
logging.exception("Failed to update subscriber: %s", exc)
|
|
return jsonify({"status": "error", "message": "Could not update subscription."}), 500
|
|
|
|
if not updated:
|
|
return jsonify({"status": "error", "message": "Old email not found or new email already exists."}), 404
|
|
|
|
logging.info("Newsletter subscription updated: %s -> %s",
|
|
old_email, new_email)
|
|
return jsonify({"status": "ok", "message": "Subscription updated successfully."}), 200
|
|
|
|
|
|
@bp.route("/newsletter/manage", methods=["GET", "POST"])
|
|
def manage_subscription():
|
|
"""Display newsletter subscription management page."""
|
|
message = None
|
|
message_type = None
|
|
|
|
if request.method == "POST":
|
|
action = request.form.get("action")
|
|
email = (request.form.get("email") or "").strip()
|
|
|
|
if not newsletter.validate_email(email):
|
|
message = "Please enter a valid email address."
|
|
message_type = "error"
|
|
else:
|
|
try:
|
|
if action == "subscribe":
|
|
created = newsletter.subscribe(email)
|
|
if created:
|
|
message = "Successfully subscribed to newsletter!"
|
|
message_type = "success"
|
|
else:
|
|
message = "This email is already subscribed."
|
|
message_type = "info"
|
|
elif action == "unsubscribe":
|
|
deleted = newsletter.unsubscribe(email)
|
|
if deleted:
|
|
return render_template("unsubscribe_confirmation.html")
|
|
else:
|
|
message = "This email is not currently subscribed."
|
|
message_type = "info"
|
|
elif action == "update":
|
|
old_email = (request.form.get("old_email") or "").strip()
|
|
if not newsletter.validate_email(old_email):
|
|
message = "Please enter a valid current email address."
|
|
message_type = "error"
|
|
elif old_email == email:
|
|
message = "New email must be different from current email."
|
|
message_type = "error"
|
|
else:
|
|
updated = newsletter.update_email(old_email, email)
|
|
if updated:
|
|
message = "Email address updated successfully!"
|
|
message_type = "success"
|
|
else:
|
|
message = "Current email not found or new email already exists."
|
|
message_type = "error"
|
|
except Exception as exc:
|
|
logging.exception("Failed to manage subscription: %s", exc)
|
|
message = "An error occurred. Please try again."
|
|
message_type = "error"
|
|
|
|
return render_template("newsletter_manage.html", message=message, message_type=message_type)
|
|
|
|
|
|
@bp.route("/newsletter/unsubscribe/confirmation", methods=["GET"])
|
|
def unsubscribe_confirmation():
|
|
"""Display newsletter unsubscription confirmation page."""
|
|
return render_template("unsubscribe_confirmation.html")
|