"""Shared context for branded HTML/text emails (public site palette).""" from __future__ import annotations import html import logging import re from django.conf import settings from django.contrib.staticfiles.storage import staticfiles_storage logger = logging.getLogger(__name__) _URL_RE = re.compile(r"(https?://[^\s<]+)") _LOGO_STATIC_PATH = "brand/exit_logo.png" def _absolute_static_url(site_url: str, relative: str) -> str: """ Build an absolute URL for a static asset. Prefer the hashed Manifest URL when available. Fall back to the stable path when the manifest is missing (dj-queue worker does not run collectstatic) so sending mail never crashes. """ try: path = staticfiles_storage.url(relative) except ValueError: logger.debug( "staticfiles manifest miss for %s; using unhashed URL", relative ) static_prefix = settings.STATIC_URL or "/static/" path = f"{static_prefix}{relative.lstrip('/')}" if path.startswith("http://") or path.startswith("https://"): return path if not path.startswith("/"): path = f"/{path}" return f"{site_url}{path}" def _tagline_with_site_link(tagline: str, site_url: str) -> str: """Turn leading MKDRealtor.com (or similar) into a link to the public site.""" raw = (tagline or "").strip() if not raw: return "" # Match "MKDRealtor.com" (any case) at the start, optional trailing " · rest" match = re.match( r"(?i)^(MKDRealtor\.com)(\s*[·•\-–—]\s*.*)?$", raw, ) if not match: return html.escape(raw) label = html.escape(match.group(1)) rest = html.escape(match.group(2) or "") href = html.escape(site_url, quote=True) return ( f'{label}' f"{rest}" ) def email_brand_context(**extra): site_url = (getattr(settings, "PUBLIC_SITE_URL", None) or "").rstrip("/") if not site_url: site_url = "https://mkdrealtor.com" logo_url = _absolute_static_url(site_url, _LOGO_STATIC_PATH) brand_name = getattr(settings, "SITE_NAME", None) or "Monica Dhillon" brand_legal = getattr(settings, "CREDIT_NAME", None) or brand_name tagline = getattr(settings, "SITE_TAGLINE", None) or "" host_label = site_url.replace("https://", "").replace("http://", "") return { "site_url": site_url, "logo_url": logo_url, "brand_name": brand_name, "brand_legal": brand_legal, "brand_tagline": tagline, "brand_tagline_html": _tagline_with_site_link(tagline, site_url), "host_label": host_label, **extra, } def plain_text_to_email_html(text: str) -> str: """Escape plain text and turn paragraphs / URLs into simple HTML.""" raw = (text or "").replace("\r\n", "\n").strip() if not raw: return "" blocks: list[str] = [] for para in re.split(r"\n\s*\n", raw): lines = [html.escape(line) for line in para.split("\n")] joined = "
\n".join(lines) joined = _URL_RE.sub( r'\1', joined, ) blocks.append( f'

{joined}

' ) return "\n".join(blocks) _HTML_TAG_RE = re.compile( r"<\s*(p|div|br|span|strong|em|b|i|u|a|img|h[1-6]|ul|ol|li|font|table)\b", re.I, ) def sanitize_email_html(raw: str) -> str: """Light cleanup for staff-authored HTML (Quill) before sending.""" text = raw or "" text = re.sub(r"(?is)]*>.*?", "", text) text = re.sub(r"(?is)]*>.*?", "", text) text = re.sub(r"(?is)]*>.*?", "", text) text = re.sub(r"(?i)\son\w+\s*=\s*([\"']).*?\1", "", text) text = re.sub(r"(?i)\son\w+\s*=\s*[^\s>]+", "", text) text = re.sub(r"(?i)javascript:", "", text) return text.strip() def campaign_body_to_email_html(body: str) -> str: """Render campaign body for email — HTML as-is when Quill markup, else plain.""" raw = (body or "").strip() if not raw: return "" if _HTML_TAG_RE.search(raw): return sanitize_email_html(raw) return plain_text_to_email_html(raw) def campaign_body_to_plain_text(body: str) -> str: """Plain-text alternative for multipart emails.""" from django.utils.html import strip_tags raw = (body or "").strip() if not raw: return "" if _HTML_TAG_RE.search(raw): text = strip_tags(sanitize_email_html(raw)) return html.unescape(re.sub(r"[ \t]+\n", "\n", text)).strip() return raw