Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 36 additions & 9 deletions backend/campaigns/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -714,25 +714,35 @@ def _build_fallback_content(self, request):

from django.http import HttpResponse
from django.middleware.csrf import get_token
from django.utils.html import escape
from leads.models import Lead
from .utils import verify_unsubscribe_token


def _unsubscribe_page(title, message, extra_html=''):
def _unsubscribe_page(title, message, extra_html='', logo_url=''):
safe_title = escape(title or "")
safe_message = escape(message or "")
safe_logo_url = escape(logo_url or "")

logo_html = (
f'<img src="{safe_logo_url}" alt="Organization Logo" style="max-width:180px;margin-bottom:20px;">'
if safe_logo_url else ''
)

return (
'<!DOCTYPE html>'
'<html lang="en">'
'<head>'
'<meta charset="utf-8">'
'<meta name="viewport" content="width=device-width,initial-scale=1">'
f'<title>{title} | LeadOrbit</title>'
f'<title>{safe_title} | LeadOrbit</title>'
'<style>body{margin:0;font-family:Inter,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Ubuntu,sans-serif;background:#f8fafc;color:#111827;}'
'.container{max-width:720px;margin:72px auto;padding:32px;background:#ffffff;border:1px solid #e5e7eb;border-radius:24px;box-shadow:0 20px 80px rgba(15,23,42,.08);}'
'h1{margin-top:0;font-size:2rem;color:#0f172a;}p{font-size:1rem;line-height:1.7;color:#475569;}'
'button{margin-top:12px;border:0;border-radius:999px;background:#1d4ed8;color:#fff;font-weight:700;padding:12px 20px;cursor:pointer;}'
'</style>'
'</head>'
f'<body><div class="container"><h1>{title}</h1><p>{message}</p>{extra_html}</div></body>'
f'<body><div class="container">{logo_html}<h1>{safe_title}</h1><p>{safe_message}</p>{extra_html}</div></body>'
'</html>'
)

Expand All @@ -749,6 +759,20 @@ def unsubscribe_view(request, lead_id, token):

try:
lead = Lead.objects.get(id=lead_id)

organization = lead.organization

custom_title = (
organization.unsubscribe_title
or "Confirm unsubscribe"
)

custom_message = (
organization.unsubscribe_message
or "Please confirm that you want to unsubscribe from future emails sent through LeadOrbit."
)

logo_url = organization.brand_logo_url or ""
except Lead.DoesNotExist:
return HttpResponse(
"Lead not found",
Expand All @@ -764,20 +788,23 @@ def unsubscribe_view(request, lead_id, token):
'</form>'
)
html = _unsubscribe_page(
'Confirm unsubscribe',
'Please confirm that you want to unsubscribe from future emails sent through LeadOrbit.',
custom_title,
custom_message,
form,
logo_url
)

return HttpResponse(html, content_type='text/html')

lead.global_unsubscribe = True
lead.save(update_fields=["global_unsubscribe"])

html = _unsubscribe_page(
'Unsubscribed',
'You have been unsubscribed from all future emails sent through LeadOrbit.',
'<p>If you received this link by mistake, no further action is needed.</p>',
)
'Unsubscribed',
'You have been unsubscribed from all future emails sent through LeadOrbit.',
'<p>If you received this link by mistake, no further action is needed.</p>',
logo_url,
)

return HttpResponse(html, content_type='text/html')

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Generated by Django 5.0.14 on 2026-06-22 05:33

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('tenants', '0002_organization_enable_ai_personalization_and_more'),
]

operations = [
migrations.AddField(
model_name='organization',
name='brand_logo_url',
field=models.URLField(blank=True, null=True),
),
migrations.AddField(
model_name='organization',
name='unsubscribe_message',
field=models.TextField(blank=True, null=True),
),
migrations.AddField(
model_name='organization',
name='unsubscribe_title',
field=models.CharField(blank=True, max_length=255, null=True),
),
]
15 changes: 15 additions & 0 deletions backend/tenants/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,22 @@ class Organization(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
gemini_api_key = models.CharField(max_length=255, blank=True, null=True)
enable_ai_personalization = models.BooleanField(default=True)

unsubscribe_title = models.CharField(
max_length=255,
blank=True,
null=True
)

unsubscribe_message = models.TextField(
blank=True,
null=True
)

brand_logo_url = models.URLField(
blank=True,
null=True
)
def __str__(self):
return self.name

Expand Down