Markdown Email Templates
This document explains how to use the new Markdown-based email template system in PyLadiesCon Portal.
Overview
PyLadiesCon Portal now exclusively uses Markdown email templates for all email communications. This system:
- โ Simplifies email development - Write once in Markdown, get both HTML and text versions
- โ Improves maintainability - Single source of truth for email content
- โ Provides better formatting - Rich Markdown features for better-looking emails
- โ Enforces consistency - All emails use the same Markdown-based system
- โ Ensures security - Automatic HTML sanitization prevents XSS attacks
Important: HTML and text templates are no longer supported. All new and existing email templates must use the Markdown format.
Quick Start
Creating a Markdown Email Template
- Create a
.mdfile in thetemplates/emails/directory - Use Django template syntax combined with Markdown formatting
- Call the email function with the
markdown_templateparameter
Example template (templates/emails/welcome.md):
{% extends "emails/base_email.md" %}
{% load i18n %}
{% block content %}
# Welcome to PyLadiesCon, {{ user.first_name }}!
Thank you for joining our community. We're excited to have you on board.
**Welcome aboard!**
{% endblock content %}
Sending the Email
from common.send_emails import send_email
send_email(
subject="Welcome to PyLadiesCon!",
recipient_list=["user@example.com"],
markdown_template="emails/welcome.md",
context={"user": user_instance},
)
Markdown Features Supported
Headers
# H1 Header
## H2 Header
### H3 Header
Text Formatting
**Bold text**
*Italic text*
~~Strikethrough~~
`Code text`
Links
[Link text](https://example.com)
[Link with title](https://example.com "Link title")
Lists
## Unordered List
- Item 1
- Item 2
- Item 3
## Ordered List
1. First item
2. Second item
3. Third item
Code Blocks
```python
def hello_world():
print("Hello, PyLadiesCon!")
### Blockquotes
```markdown
> This is a blockquote
> with multiple lines
Horizontal Rules
---
Tables (GitHub Flavored Markdown)
| Feature | Status |
|---------|--------|
| Markdown | โ
Working |
| HTML | โ
Working |
| Text | โ
Working |
Django Template Integration
Markdown templates work seamlessly with Django template features:
Template Inheritance
{% extends "emails/base_email.md" %}
{% load i18n %}
{% block content %}
# Your content here
{% endblock content %}
Template Tags and Filters
# Welcome {{ user.first_name|title }}!
{% if user.is_staff %}
As a staff member, you have special privileges.
{% endif %}
Your registration date: {{ user.date_joined|date:"F j, Y" }}
## Available Teams
{% for team in teams %}
- **{{ team.name }}** - {{ team.description }}
{% endfor %}
Internationalization
{% load i18n %}
# {% trans "Welcome to PyLadiesCon" %}
{% blocktrans with name=user.first_name %}
Hello {{ name }}, thank you for joining us!
{% endblocktrans %}
Template Structure
Base Template
All email templates should extend the base template for consistency:
{% extends "emails/base_email.md" %}
{% load i18n %}
{% block content %}
# Your email content here
{% endblock content %}
Template Locations
- Base template:
templates/emails/base_email.md - Volunteer emails:
templates/emails/volunteer/*.md - Sponsorship emails:
templates/emails/sponsorship/*.md - Custom emails:
templates/emails/your-app/*.md
API Reference
send_email() Function
The send_email() function now exclusively supports Markdown templates:
def send_email(
subject: str,
recipient_list: list,
*,
markdown_template: str, # Required Markdown template
context: Optional[Dict[str, Any]] = None,
) -> None
Parameters:
subject: Email subject linerecipient_list: List of recipient email addressesmarkdown_template: Path to Markdown template (required)context: Dictionary of template variables
Examples:
# New Markdown approach (recommended)
send_email(
subject="Welcome!",
recipient_list=["user@example.com"],
markdown_template="emails/welcome.md",
context={"user": user},
)
send_markdown_email() Function
For direct Markdown email sending:
from common.markdown_emails import send_markdown_email
send_markdown_email(
subject="Test Email",
recipient_list=["test@example.com"],
markdown_template="emails/test.md",
context={"name": "John"},
)
Migration Complete
All email templates have been converted to Markdown format. The system no longer supports HTML or text templates.
Markdown Conversion Reference
For understanding how the conversion was done:
- HTML syntax converted to Markdown:
<h1>Title</h1>โ# Title<strong>Bold</strong>โ**Bold**<a href="url">Link</a>โ[Link](url)<ul><li>Item</li></ul>โ- Item- All email sending code updated to use
markdown_templateparameter - Old HTML/text templates removed from the system
Before (HTML template):
{% extends "emails/base_email.html" %}
{% block content %}
<h1>Welcome {{ user.first_name }}!</h1>
<p>Thank you for joining <strong>PyLadiesCon</strong>.</p>
<ul>
<li>Join our <a href="https://discord.com/invite/example">Discord</a></li>
<li>Read the <a href="https://conference.pyladies.com/docs/">docs</a></li>
</ul>
{% endblock content %}
After (Markdown template):
{% extends "emails/base_email.md" %}
{% block content %}
# Welcome {{ user.first_name }}!
Thank you for joining **PyLadiesCon**.
- Join our [Discord](https://discord.com/invite/example)
- Read the [docs](https://conference.pyladies.com/docs/)
{% endblock content %}
Best Practices
๐ Writing Guidelines
- Use semantic headings (
#,##,###) for structure - Bold important information with
**text** - Create clear action items with bullet points
- Include descriptive link text instead of raw URLs
- Use horizontal rules (
---) to separate sections
๐จ Design Considerations
- Keep line length reasonable (under 80 characters when possible)
- Use whitespace effectively for readability
- Group related information under headings
- Make calls-to-action prominent with bold text or buttons
๐ Security
- HTML is automatically sanitized to prevent XSS attacks
- Only safe HTML tags are allowed in the output
- Link URLs are validated and dangerous protocols are blocked
- Template context is escaped following Django security practices
๐งช Testing
# Test Markdown email in development
from django.test import TestCase
from common.markdown_emails import send_markdown_email
from django.core import mail
class EmailTest(TestCase):
def test_welcome_email(self):
mail.outbox = []
send_markdown_email(
subject="Welcome Test",
recipient_list=["test@example.com"],
markdown_template="emails/welcome.md",
context={"user": {"first_name": "Test"}},
)
self.assertEqual(len(mail.outbox), 1)
email = mail.outbox[0]
self.assertIn("Welcome Test!", email.body)
self.assertIn("<h1>Welcome Test!</h1>", email.alternatives[0][0])
Troubleshooting
Common Issues
Template not found error:
TemplateDoesNotExist: emails/my_template.md
templates/emails/
- Verify template inheritance paths
Markdown not rendering:
# Make sure you're using markdown_template, not html_template
send_email(
subject="Test",
recipient_list=["test@example.com"],
markdown_template="emails/test.md", # โ Correct parameter
context=context,
)
Missing dependencies:
ImportError: No module named 'markdown'
markdown and bleach are installed
- Check requirements-app.txt includes the dependencies
Debug Mode
Enable email debugging to see both HTML and text output:
# In development settings
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
Examples
Volunteer Onboarding Email
{% extends "emails/base_email.md" %}
{% load i18n %}
{% block content %}
# Welcome to the PyLadiesCon team, {{ profile.user.first_name }}!
Thank you for applying to volunteer with PyLadiesCon. We're excited to share that your application has been **approved**.
## ๐ Team Assignment
You have been assigned to the following team(s):
{% for team in profile.teams.all %}
- **{{ team.short_name }}** - Team Lead(s): {% for lead in team.team_leads.all %}{{ lead.user.get_full_name }}{% if not forloop.last %}, {% endif %}{% endfor %}
{% endfor %}
## ๐ Action Items
**Please complete these steps within 7 days:**
- **Join our [Discord server](https://discord.com/invite/example)** *(Required)*
- **Read the [Volunteer Guide](https://conference.pyladies.com/docs/)**
- **Complete your [profile setup](https://{{ current_site.domain }}{% url 'volunteer:index' %})**
---
**Questions?** Reach out to your team lead or contact us on Discord.
**Thank you for volunteering!** ๐
{% endblock content %}
Sponsorship Notification Email
{% extends "emails/base_email.md" %}
{% block content %}
# New Sponsorship Application ๐
Hello {{ recipient_name }}, a new sponsorship application has been submitted.
## Sponsor Details
- **Organization:** {{ profile.organization_name }}
- **Tier:** {{ profile.sponsorship_tier }}
- **Contact:** {{ profile.sponsor_contact_name }} ({{ profile.sponsors_contact_email }})
- **Amount:** ${{ profile.sponsorship_tier.amount }}
## Next Steps
Please review the application in the [admin dashboard](https://{{ current_site.domain }}/admin/).
{% endblock content %}
Contributing
When contributing new email templates:
- Use Markdown format for new templates (
.mdfiles) - Follow the style guide in this documentation
- Include comprehensive tests for email functionality
- Update this documentation if adding new features
- Test in multiple email clients when possible
For technical questions about the Markdown email system, check the code in common/markdown_emails.py or reach out to the development team.