Files
print_forge/site/public/views.py
T
westfarn c9b81ceed7
CI / test (pull_request) Successful in 35s
Add customer accounts, shipment tracking, and purchase reviews.
Shoppers can register, save shipping details, and view order history while cards stay on Stripe. EasyPost tracker updates (including numbers from Pirate Ship) and 1–5 star reviews are limited to buyers. The contact form now only asks for email and a message.
2026-09-07 06:37:52 -05:00

250 lines
8.3 KiB
Python

from django.conf import settings
from django.contrib import messages
from django.http import HttpResponse
from django.shortcuts import redirect, render
from django.urls import reverse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_GET, require_http_methods
from analytics.services import attribute_lead_from_request
from contacts.models import Channel, ConsentRecord, Contact
from contacts.services import upsert_contact
from leads.models import Lead
from contacts.consent import (
channel_preferences,
parse_unsubscribe_token,
process_unsubscribe_token,
set_channel_preferences,
unsubscribe_all,
)
from public.forms import ContactForm, NotifyForm
from public.notifications import notify_admins_of_contact_form
def _public_site_base(request) -> str:
base = (settings.PUBLIC_SITE_URL or "").rstrip("/")
if base:
return base
return request.build_absolute_uri("/").rstrip("/")
def home(request):
featured = []
from django.apps import apps as django_apps
if django_apps.is_installed("shop"):
from shop.models import Product
featured = list(
Product.objects.filter(is_published=True)
.select_related("image")
.prefetch_related("colors")[:6]
)
return render(request, "public/home.html", {"featured_products": featured})
def about(request):
return render(request, "public/about.html")
def terms(request):
return render(request, "public/terms.html")
@require_GET
def robots_txt(request):
site = _public_site_base(request)
body = "\n".join(
[
"User-agent: *",
"Allow: /",
"Disallow: /portal/",
"Disallow: /accounts/",
"Disallow: /account/",
"Disallow: /admin/",
"Disallow: /api/",
f"Sitemap: {site}/sitemap.xml",
"",
]
)
return HttpResponse(body, content_type="text/plain; charset=utf-8")
@require_GET
def sitemap_xml(request):
site = _public_site_base(request)
paths = [
("public:home", "1.0", "weekly"),
("public:about", "0.8", "monthly"),
("public:contact", "0.9", "monthly"),
("public:terms", "0.5", "yearly"),
]
from django.apps import apps as django_apps
if django_apps.is_installed("blog"):
paths.append(("blog:list", "0.7", "weekly"))
if django_apps.is_installed("shop"):
paths.append(("shop:list", "0.8", "weekly"))
if django_apps.is_installed("events"):
paths.append(("events:list", "0.8", "weekly"))
urls = []
for name, priority, changefreq in paths:
path = reverse(name)
urls.append(
" <url>\n"
f" <loc>{site}{path}</loc>\n"
f" <changefreq>{changefreq}</changefreq>\n"
f" <priority>{priority}</priority>\n"
" </url>"
)
body = (
'<?xml version="1.0" encoding="UTF-8"?>\n'
'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n'
+ "\n".join(urls)
+ "\n</urlset>\n"
)
return HttpResponse(body, content_type="application/xml; charset=utf-8")
@require_http_methods(["GET", "POST"])
def contact(request):
if request.method == "POST":
form = ContactForm(request.POST)
if form.is_valid():
data = form.cleaned_data
submitted_email = data["email"].lower()
contact_obj, _created, match_reason = upsert_contact(
email=submitted_email,
source=Contact.Source.CONTACT_FORM,
merge_phone_address=False,
)
ConsentRecord.objects.update_or_create(
contact=contact_obj,
channel=Channel.EMAIL,
defaults={"opted_in": True, "reason": "contact_form"},
)
interest = data.get("interest") or ""
interest_label = dict(ContactForm.INTEREST_CHOICES).get(interest, interest)
body = data.get("message") or ""
if interest_label:
body = f"Interest: {interest_label}\n\n{body}".strip()
if (
match_reason
and (contact_obj.email or "").lower() != submitted_email
):
body = (
f"Submitted email: {submitted_email} "
f"(merged by {match_reason} with "
f"{contact_obj.email or 'existing contact'})\n\n{body}"
).strip()
lead = Lead.objects.create(
contact=contact_obj,
message=body,
status=Lead.Status.NEW,
)
attribute_lead_from_request(request, lead)
notify_admins_of_contact_form(lead)
messages.success(request, "Thanks — we will be in touch soon.")
return redirect(f"{reverse('public:contact')}?sent=1")
else:
form = ContactForm()
return render(request, "public/contact.html", {"form": form})
@require_http_methods(["GET", "POST"])
def under_construction(request):
"""Direct route (also used by middleware). Accepts notify-me emails."""
if request.method == "POST":
form = NotifyForm(request.POST)
if form.is_valid():
email = form.cleaned_data["email"].lower()
Contact.objects.get_or_create(
email=email,
defaults={
"first_name": "",
"source": Contact.Source.NOTIFY_ME,
},
)
messages.success(request, "You're on the list — we'll email when we launch.")
return redirect("public:under_construction")
else:
form = NotifyForm()
return render(request, "public/under_construction.html", {"form": form})
@csrf_exempt
@require_http_methods(["GET", "POST"])
def unsubscribe_one_click(request, token: str):
"""
One-click opt-out for the channel encoded in the token.
CSRF-exempt so mail clients can POST List-Unsubscribe=One-Click (RFC 8058).
"""
ok = process_unsubscribe_token(token)
if not ok:
return render(request, "public/unsubscribe.html", {"valid": False})
return redirect("public:unsubscribe", token=token)
@require_http_methods(["GET", "POST"])
def unsubscribe(request, token: str):
"""
Signed-token preference center for email / SMS / postcard.
GET ?one_click=1 opts out the token channel then redirects here.
POST saves checkboxes or unsubscribes from all channels.
"""
contact, token_channel = parse_unsubscribe_token(token)
if not contact:
return render(
request,
"public/unsubscribe.html",
{"valid": False},
)
if request.method == "GET" and request.GET.get("one_click") in {
"1",
"true",
"yes",
}:
process_unsubscribe_token(token)
return redirect("public:unsubscribe", token=token)
if request.method == "POST":
action = (request.POST.get("action") or "save").strip()
if action == "unsubscribe_all":
unsubscribe_all(contact, reason="preferences_unsubscribe_all")
messages.success(
request, "You are unsubscribed from all marketing channels."
)
else:
prefs = {
Channel.EMAIL: "consent_email" in request.POST,
Channel.SMS: "consent_sms" in request.POST,
Channel.POSTCARD: "consent_postcard" in request.POST,
}
set_channel_preferences(
contact, prefs, reason="preferences_save"
)
messages.success(request, "Your communication preferences were saved.")
return redirect("public:unsubscribe", token=token)
contact = Contact.objects.prefetch_related("consents").get(pk=contact.pk)
prefs = channel_preferences(contact)
identity = contact.email or contact.phone or contact.full_name or "your profile"
return render(
request,
"public/unsubscribe.html",
{
"valid": True,
"contact": contact,
"identity": identity,
"prefs": prefs,
"token_channel": token_channel,
"token": token,
},
)
def page_not_found(request, exception):
return render(request, "public/404.html", status=404)