feat: Add rich text composing support using CKEditor 5
Add rich text composing support using CKEditor 5
Closes #540
What this MR does
Adds opt-in rich text (HTML) composing to HyperKitty's "Create New Thread" form using CKEditor 5, allowing users to send formatted HTML emails directly from the web interface.
Licensing
- CKEditor 5 is licensed under GPL 2+, compatible with HyperKitty's GPL v3
- Note: open-source usage displays a small "Powered by CKEditor" attribution badge in the editor
Changes
- Integrates CKEditor 5 (CDN) into the compose form, gated behind three conditions: site allows AND Mailman Core allows HTML AND list has it enabled
- Adds
HyperKittySiteSettingsmodel with a site-widerich_text_enabledtoggle, configurable by superusers in Django admin - Adds
rich_text_enabledBooleanField to HyperKitty'sMailingListmodel, configurable by staff/superusers in Django admin on a per-list basis - Adds
core_allows_html()helper that reads Mailman Core'sfilter_content,collapse_alternatives, andconvert_html_to_plaintextsettings — shows a warning in Django admin if Core would strip HTML, directing the admin to fix it in Postorius first - Sanitizes HTML input using bleach to prevent XSS
- Sends messages as
multipart/alternativewithhtml2textplain text fallback
Testing
- Tested locally on M1 Mac, Python 3.14, Django 5.2
- CKEditor renders correctly in the compose form when all conditions are met
- HTML is sanitized before sending
- Plain text fallback is auto-generated from HTML
Notes
- Image attachments are explicitly out of scope for this MR
- Archive rendering of HTML emails is out of scope for this MR — HTML parts currently show as downloadable attachments. The fix requires changes to the incoming MIME parsing pipeline and will be addressed in a follow-up MR soon
Images attached:
- Hyperkitty Site Settings show Rich text toggle (site wide)
- Mailing list shows rich text toggle along with mailman core check
- Sample preview of CKEditor in the compose form when rich text is enabled
Edited by Ayush Chauhan


