From d14eda3524bb403748443164084e41ae925da5aa Mon Sep 17 00:00:00 2001 From: "William (Lindy) Lindstrom" Date: Mon, 21 Sep 2026 12:41:29 -0700 Subject: [PATCH 1/7] add email configurtion docs The build upon tom_registrations README.md --- docs/common/email_configuration.rst | 167 ++++++++++++++++++++++++++++ docs/common/index.rst | 1 + 2 files changed, 168 insertions(+) create mode 100644 docs/common/email_configuration.rst diff --git a/docs/common/email_configuration.rst b/docs/common/email_configuration.rst new file mode 100644 index 000000000..b839049c5 --- /dev/null +++ b/docs/common/email_configuration.rst @@ -0,0 +1,167 @@ +Email Configuration +=================== + +When configured, TOM Toolkit sends emails for registration requests, +registration approval notices, and password resets. + +Email delivery is a Django feature. There are no TOM Toolkit-specific email settings. +See Django's `Sending email `_ +topic guide for all the details. This section aims to put the Django configuration in +a TOM Toolkit context. Email is not configured out of the box. + +.. Note:: + **These instructions will change**. This page describes email configuration as of + Django 5.2. Django's email framework being moderized over the current (6.1) and + future releases: + + - Django 6.0 rebuilt ``django.core.mail`` on Python's modern email API and deprecated + the ``(name, address)`` tuple form of ``MANAGERS`` and ``ADMINS`` that we show below. + - Django 6.1 introduced a ``MAILERS`` setting dictionary, similar to + the ``DATABASES`` and ``CACHES`` configuration dictionaries. + - Django 7.0 removes ``EMAIL_BACKEND`` and the other ``EMAIL_*`` settings. + + Expect this page to change as TOM Toolkit moves to those releases + (see the `Django 6.0 `_ and + `Django 6.1 `_ release notes). + + +We'll start with a short tutorial. To go directly to the configuration of your TOM and +skip the tutorial, see :ref:`email-configuration-in-tom-toolkit`. + +----------------- +A short tutorial +----------------- + +Django has a ``sendtestemail`` management command. Let's start by trying to send an email:: + + ./manage.py sendtestemail + +Unless you've already done some +configuration, that didn't work. Try this:: + + ./manage.py sendtestemail --help + +Notice the ``--managers`` and ``--admins`` options and their references to ``settings.MANAGERS`` and +``settings.ADMINS``, respectively. + +Let's configure those in your ``settings.py``. We'll configure an ``EMAIL_BACKEND`` while +we're at it):: + + MANAGERS = [("Mary", "mary@example.com"),] + ADMINS = [("John", "john@example.com"),] + EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' + +The `console.EMAIL_BACKEND `_ +we've configured doesn't send email. Rather, it outputs to stdout. With those settings, try it again:: + + ./manage.py sendtestemail --managers + +Now, in the *console*, you should see something like this:: + + Content-Type: text/plain; charset="utf-8" + MIME-Version: 1.0 + Content-Transfer-Encoding: 7bit + Subject: [Django] Test email from tomtoolkit-host on 2026-09-19 + 00:32:32.742613+00:00 + From: root@localhost + To: mary@example.com + Date: Sat, 19 Sep 2026 00:32:32 -0000 + Message-ID: <178977795274.2655038.15656462345865752721@tomtoolkit-host> + + This email was sent to the site managers. + ------------------------------------------------------------------------------- + +------------------------ + +.. _email-configuration-in-tom-toolkit: + +Configuring Email in TOM Toolkit +--------------------------------- + +Your TOM uses email for a small set of optional features: + +- **Registration requests**: with ``TOM_REGISTRATION_STRATEGY = 'approval_required'``, the addresses in + Django's `MANAGERS `_ setting are + notified of each sign-up awaiting approval. +- **Approval notices**: the new user is notified when a superuser approves their account. +- **Password reset**: ``TOM_PASSWORD_RESET_ENABLED = True`` adds the "Forgot your password?" flow. + +(See :doc:`Accounts and Authentication ` for the features themselves.) + +When email is not configured, or a send fails, these features degrade with guidance in the UI +rather than breaking (see `What happens when email is not configured or when sends fail`_ below). + +Configuring the backend +------------------------- + +Email delivery is standard Django — TOM Toolkit adds no email settings of its own. The complete +reference is Django's `Sending email `_ +topic guide; this section only puts the settings in TOM context. + +In the tutorial section above, we configured an email backend that prints to stdout, which is useful +for development. For production, point Django's SMTP backend (the default) at your mail relay, and +say who your TOM's mail comes from and who its administrators are:: + + EMAIL_HOST = 'smtp.example.org' # your institution's or provider's SMTP relay + EMAIL_PORT = 587 + EMAIL_HOST_USER = 'tom@example.org' + EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD', '') # keep secrets out of settings.py + EMAIL_USE_TLS = True + DEFAULT_FROM_EMAIL = 'tom@example.org' # the From: address on everything the TOM sends + MANAGERS = [('TOM admins', 'admins@example.org')] # registration requests go here + +The host, port, credentials and TLS mode come from your email provider; what each setting means is +in Django's `email settings reference +`_. + +Verifying your configuration +---------------------------- + +As seen in the tutorial, Django ships a management command that sends a test message +through whatever backend you configured:: + + ./manage.py sendtestemail you@example.org + +To try the SMTP configuration without involving a real relay, you can run a small SMTP server on +your own machine: the ``aiosmtpd`` package (``pip install aiosmtpd``) accepts SMTP connections and +prints each received message to its terminal. Run it in one terminal:: + + python -m aiosmtpd -n -l localhost:8025 + +point your ``settings.py`` at it (``EMAIL_HOST = 'localhost'``, ``EMAIL_PORT = 8025``), and +``sendtestemail`` — and every email your TOM sends — appears in that terminal. + +What happens when email is not configured or when sends fail +---------------------------------------------------------------- + +The email features degrade rather than break: + +- TOM Toolkit determines whether email is configured with a heuristic predicate + (``tom_common.accounts.email.email_is_configured``). Any backend other than Django's SMTP + default counts as configured. (The SMTP default pointed at ``localhost`` with no credentials is + treated as "not configured". +- ``manage.py check`` warns (``tom_common.W002``) when approval-required registration or password + reset is enabled without a configured email backend. +- Approving a registration always succeeds even when the notice cannot be sent. Under those + circumstances, the approver is told in the UI to notify the user directly. Additionally, the + *Pending users* table indicates when email is not configured. +- A failed password-reset send is reported on the page, with the failure logged for the operator. + + +Customizing the emails +---------------------- + +Each email renders from a pair of templates (``*_subject.txt`` and ``*_message.txt``). The +templates can be customized (overridden) by placing your own copy in your TOM's +``templates/`` directory (sibling to ``manage.py``). The sender address is ``DEFAULT_FROM_EMAIL``. + +- **Registration request** email goes to the addresses in ``MANAGERS``. The default templates are + set by TOM Toolkit. To customize, put your overriding templates in + ``account/email/registration_requested_subject.txt`` and ``_message.txt``. +- **Approval notice** email goes to the approved user. The default templates are set by TOM Toolkit. + To customize, put your overriding templates in + ``account/email/registration_approved_subject.txt`` and ``_message.txt``. +- **Password reset** email goes to the email address of the account being reset. The default templates + are set by ``django-allauth``. To customize, put your overriding templates in + ``account/email/password_reset_key_subject.txt`` and ``_message.txt``. + diff --git a/docs/common/index.rst b/docs/common/index.rst index 8d6b32a9d..86cc7dab0 100644 --- a/docs/common/index.rst +++ b/docs/common/index.rst @@ -8,6 +8,7 @@ Configuring a TOM Custom settings Permissions + Email configuration TOM Settings ------------ From 3962683b41509fa86a7fc277c11b58c31083d5a2 Mon Sep 17 00:00:00 2001 From: "William (Lindy) Lindstrom" Date: Mon, 21 Sep 2026 12:49:29 -0700 Subject: [PATCH 2/7] fix outline structure --- docs/common/email_configuration.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/common/email_configuration.rst b/docs/common/email_configuration.rst index b839049c5..e7db14e4d 100644 --- a/docs/common/email_configuration.rst +++ b/docs/common/email_configuration.rst @@ -75,6 +75,7 @@ Now, in the *console*, you should see something like this:: .. _email-configuration-in-tom-toolkit: +--------------------------------- Configuring Email in TOM Toolkit --------------------------------- @@ -131,8 +132,9 @@ prints each received message to its terminal. Run it in one terminal:: point your ``settings.py`` at it (``EMAIL_HOST = 'localhost'``, ``EMAIL_PORT = 8025``), and ``sendtestemail`` — and every email your TOM sends — appears in that terminal. +------------------------------------------------------------------- What happens when email is not configured or when sends fail ----------------------------------------------------------------- +------------------------------------------------------------------- The email features degrade rather than break: @@ -147,9 +149,9 @@ The email features degrade rather than break: *Pending users* table indicates when email is not configured. - A failed password-reset send is reported on the page, with the failure logged for the operator. - +------------------------ Customizing the emails ----------------------- +------------------------ Each email renders from a pair of templates (``*_subject.txt`` and ``*_message.txt``). The templates can be customized (overridden) by placing your own copy in your TOM's From 6a6162cf040062ec82aaad6d088e6804b071b726 Mon Sep 17 00:00:00 2001 From: "William (Lindy) Lindstrom" Date: Mon, 21 Sep 2026 12:54:54 -0700 Subject: [PATCH 3/7] doc tweaks --- docs/common/email_configuration.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/common/email_configuration.rst b/docs/common/email_configuration.rst index e7db14e4d..7d0f9f749 100644 --- a/docs/common/email_configuration.rst +++ b/docs/common/email_configuration.rst @@ -118,19 +118,19 @@ in Django's `email settings reference Verifying your configuration ---------------------------- -As seen in the tutorial, Django ships a management command that sends a test message +As seen in the tutorial, Django includes a management command that sends a test message through whatever backend you configured:: ./manage.py sendtestemail you@example.org To try the SMTP configuration without involving a real relay, you can run a small SMTP server on your own machine: the ``aiosmtpd`` package (``pip install aiosmtpd``) accepts SMTP connections and -prints each received message to its terminal. Run it in one terminal:: +prints each received message to its terminal. To run it, in a terminal type:: python -m aiosmtpd -n -l localhost:8025 -point your ``settings.py`` at it (``EMAIL_HOST = 'localhost'``, ``EMAIL_PORT = 8025``), and -``sendtestemail`` — and every email your TOM sends — appears in that terminal. +Configure your ``settings.py`` to point at it (``EMAIL_HOST = 'localhost'``, ``EMAIL_PORT = 8025``), +and ``sendtestemail`` output should appear in the (`aiosmtpd`) terminal. ------------------------------------------------------------------- What happens when email is not configured or when sends fail From 5fd12e9924e4d563d4d961c0a2690f125085b30f Mon Sep 17 00:00:00 2001 From: "William (Lindy) Lindstrom" Date: Mon, 21 Sep 2026 14:02:01 -0700 Subject: [PATCH 4/7] more typo tweaks --- docs/common/email_configuration.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/common/email_configuration.rst b/docs/common/email_configuration.rst index 7d0f9f749..10ca2044a 100644 --- a/docs/common/email_configuration.rst +++ b/docs/common/email_configuration.rst @@ -141,7 +141,7 @@ The email features degrade rather than break: - TOM Toolkit determines whether email is configured with a heuristic predicate (``tom_common.accounts.email.email_is_configured``). Any backend other than Django's SMTP default counts as configured. (The SMTP default pointed at ``localhost`` with no credentials is - treated as "not configured". + treated as "not configured"). - ``manage.py check`` warns (``tom_common.W002``) when approval-required registration or password reset is enabled without a configured email backend. - Approving a registration always succeeds even when the notice cannot be sent. Under those From ef761d74c7766394fdcc7ca92a7521e84c52a25e Mon Sep 17 00:00:00 2001 From: "William (Lindy) Lindstrom" Date: Wed, 23 Sep 2026 11:27:22 -0700 Subject: [PATCH 5/7] clean up email configuration docs --- docs/common/email_configuration.rst | 41 +++++++++++++++++------------ 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/docs/common/email_configuration.rst b/docs/common/email_configuration.rst index 10ca2044a..22a7719fc 100644 --- a/docs/common/email_configuration.rst +++ b/docs/common/email_configuration.rst @@ -6,19 +6,20 @@ registration approval notices, and password resets. Email delivery is a Django feature. There are no TOM Toolkit-specific email settings. See Django's `Sending email `_ -topic guide for all the details. This section aims to put the Django configuration in +topic guide for details. This section aims to put the Django configuration in a TOM Toolkit context. Email is not configured out of the box. .. Note:: **These instructions will change**. This page describes email configuration as of - Django 5.2. Django's email framework being moderized over the current (6.1) and + Django 5.2. Django's email framework is being moderized over the current (6.1) and future releases: - Django 6.0 rebuilt ``django.core.mail`` on Python's modern email API and deprecated the ``(name, address)`` tuple form of ``MANAGERS`` and ``ADMINS`` that we show below. - Django 6.1 introduced a ``MAILERS`` setting dictionary, similar to the ``DATABASES`` and ``CACHES`` configuration dictionaries. - - Django 7.0 removes ``EMAIL_BACKEND`` and the other ``EMAIL_*`` settings. + - Django 7.0 removes ``EMAIL_BACKEND`` and the other ``EMAIL_*`` settings, the settings + we describe here. Expect this page to change as TOM Toolkit moves to those releases (see the `Django 6.0 `_ and @@ -42,17 +43,17 @@ configuration, that didn't work. Try this:: ./manage.py sendtestemail --help Notice the ``--managers`` and ``--admins`` options and their references to ``settings.MANAGERS`` and -``settings.ADMINS``, respectively. +``settings.ADMINS``. Let's configure those in your ``settings.py``. We'll configure an ``EMAIL_BACKEND`` while -we're at it):: +we're at it:: MANAGERS = [("Mary", "mary@example.com"),] ADMINS = [("John", "john@example.com"),] EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' The `console.EMAIL_BACKEND `_ -we've configured doesn't send email. Rather, it outputs to stdout. With those settings, try it again:: +we've configured doesn't send email. Rather, it outputs to stdout. With those settings, let's try again:: ./manage.py sendtestemail --managers @@ -79,7 +80,7 @@ Now, in the *console*, you should see something like this:: Configuring Email in TOM Toolkit --------------------------------- -Your TOM uses email for a small set of optional features: +Your TOM uses email for these optional features: - **Registration requests**: with ``TOM_REGISTRATION_STRATEGY = 'approval_required'``, the addresses in Django's `MANAGERS `_ setting are @@ -87,27 +88,29 @@ Your TOM uses email for a small set of optional features: - **Approval notices**: the new user is notified when a superuser approves their account. - **Password reset**: ``TOM_PASSWORD_RESET_ENABLED = True`` adds the "Forgot your password?" flow. -(See :doc:`Accounts and Authentication ` for the features themselves.) +(See :doc:`Accounts and Authentication ` for descriptions of the features themselves.) -When email is not configured, or a send fails, these features degrade with guidance in the UI -rather than breaking (see `What happens when email is not configured or when sends fail`_ below). +When email is not configured, or a send fails, guidance is provided in the UI. +(See `What happens when email is not configured or when sends fail`_ below). Configuring the backend ------------------------- -Email delivery is standard Django — TOM Toolkit adds no email settings of its own. The complete +Email delivery is standard Django and TOM Toolkit adds no email settings of its own. The complete reference is Django's `Sending email `_ -topic guide; this section only puts the settings in TOM context. +topic guide. In the tutorial section above, we configured an email backend that prints to stdout, which is useful for development. For production, point Django's SMTP backend (the default) at your mail relay, and say who your TOM's mail comes from and who its administrators are:: - EMAIL_HOST = 'smtp.example.org' # your institution's or provider's SMTP relay + # from your email provider + EMAIL_HOST = 'smtp.example.org' # your institution's or provider's SMTP relay EMAIL_PORT = 587 EMAIL_HOST_USER = 'tom@example.org' EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD', '') # keep secrets out of settings.py EMAIL_USE_TLS = True + # DEFAULT_FROM_EMAIL = 'tom@example.org' # the From: address on everything the TOM sends MANAGERS = [('TOM admins', 'admins@example.org')] # registration requests go here @@ -118,7 +121,7 @@ in Django's `email settings reference Verifying your configuration ---------------------------- -As seen in the tutorial, Django includes a management command that sends a test message +As seen in the tutorial above, Django includes a management command that sends a test message through whatever backend you configured:: ./manage.py sendtestemail you@example.org @@ -136,7 +139,7 @@ and ``sendtestemail`` output should appear in the (`aiosmtpd`) terminal. What happens when email is not configured or when sends fail ------------------------------------------------------------------- -The email features degrade rather than break: +Your TOM's Email configuration can be verifiied in code and at the command line: - TOM Toolkit determines whether email is configured with a heuristic predicate (``tom_common.accounts.email.email_is_configured``). Any backend other than Django's SMTP @@ -144,6 +147,9 @@ The email features degrade rather than break: treated as "not configured"). - ``manage.py check`` warns (``tom_common.W002``) when approval-required registration or password reset is enabled without a configured email backend. + +When sending email fails, feedback is given in the UI and logs: + - Approving a registration always succeeds even when the notice cannot be sent. Under those circumstances, the approver is told in the UI to notify the user directly. Additionally, the *Pending users* table indicates when email is not configured. @@ -153,9 +159,10 @@ The email features degrade rather than break: Customizing the emails ------------------------ -Each email renders from a pair of templates (``*_subject.txt`` and ``*_message.txt``). The +Each email is composed from a pair of templates (``*_subject.txt`` and ``*_message.txt``). The templates can be customized (overridden) by placing your own copy in your TOM's -``templates/`` directory (sibling to ``manage.py``). The sender address is ``DEFAULT_FROM_EMAIL``. +``templates/`` directory (sibling to ``manage.py``). +The sender address is set by ``DEFAULT_FROM_EMAIL``. - **Registration request** email goes to the addresses in ``MANAGERS``. The default templates are set by TOM Toolkit. To customize, put your overriding templates in From 959771e59594bc16a4fc28d3f13b3f71dba90eda Mon Sep 17 00:00:00 2001 From: "William (Lindy) Lindstrom" Date: Wed, 23 Sep 2026 12:50:06 -0700 Subject: [PATCH 6/7] more email config doc changes --- docs/common/email_configuration.rst | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/docs/common/email_configuration.rst b/docs/common/email_configuration.rst index 22a7719fc..413752396 100644 --- a/docs/common/email_configuration.rst +++ b/docs/common/email_configuration.rst @@ -57,7 +57,7 @@ we've configured doesn't send email. Rather, it outputs to stdout. With those se ./manage.py sendtestemail --managers -Now, in the *console*, you should see something like this:: +Now, in the console, you should see something like this:: Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 @@ -72,6 +72,16 @@ Now, in the *console*, you should see something like this:: This email was sent to the site managers. ------------------------------------------------------------------------------- +Try these other possibilities:: + + ./manage.py sendtestemail --admins + ./manage.py sendtestemail test@example.com + +So, at this point we've configured email recipients and a backend that writes to stdout. +Below, we'll see how to write to stdout through a real SMTP server (:ref:`verifying-your-configuration`). +To send actual email, you'll need settings from your actual email provider +(:ref:`configuring-the-backend`). + ------------------------ .. _email-configuration-in-tom-toolkit: @@ -93,6 +103,8 @@ Your TOM uses email for these optional features: When email is not configured, or a send fails, guidance is provided in the UI. (See `What happens when email is not configured or when sends fail`_ below). +.. _configuring-the-backend: + Configuring the backend ------------------------- @@ -102,7 +114,8 @@ topic guide. In the tutorial section above, we configured an email backend that prints to stdout, which is useful for development. For production, point Django's SMTP backend (the default) at your mail relay, and -say who your TOM's mail comes from and who its administrators are:: +say who your TOM's mail comes from (`DEFAULT_FROM_EMAIL`) and who receives registration +requests (`MANAGERS`):: # from your email provider EMAIL_HOST = 'smtp.example.org' # your institution's or provider's SMTP relay @@ -112,11 +125,13 @@ say who your TOM's mail comes from and who its administrators are:: EMAIL_USE_TLS = True # DEFAULT_FROM_EMAIL = 'tom@example.org' # the From: address on everything the TOM sends - MANAGERS = [('TOM admins', 'admins@example.org')] # registration requests go here + MANAGERS = [('TOM managers', 'admins@example.org')] # registration requests go here + +The host, port, credentials and TLS mode come from your email provider. See Django's +`email settings reference `_ +for details on individual setttings. -The host, port, credentials and TLS mode come from your email provider; what each setting means is -in Django's `email settings reference -`_. +.. _verifying-your-configuration: Verifying your configuration ---------------------------- @@ -153,7 +168,7 @@ When sending email fails, feedback is given in the UI and logs: - Approving a registration always succeeds even when the notice cannot be sent. Under those circumstances, the approver is told in the UI to notify the user directly. Additionally, the *Pending users* table indicates when email is not configured. -- A failed password-reset send is reported on the page, with the failure logged for the operator. +- A failed password-reset send is reported on the page, with the failure logged. ------------------------ Customizing the emails From 16de13742ed49a28bd8eaf659990a558a70ac548 Mon Sep 17 00:00:00 2001 From: "William (Lindy) Lindstrom" Date: Thu, 24 Sep 2026 12:05:51 -0700 Subject: [PATCH 7/7] remove obscure phrases --- docs/common/email_configuration.rst | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/common/email_configuration.rst b/docs/common/email_configuration.rst index 413752396..f3596d8ef 100644 --- a/docs/common/email_configuration.rst +++ b/docs/common/email_configuration.rst @@ -156,10 +156,8 @@ What happens when email is not configured or when sends fail Your TOM's Email configuration can be verifiied in code and at the command line: -- TOM Toolkit determines whether email is configured with a heuristic predicate - (``tom_common.accounts.email.email_is_configured``). Any backend other than Django's SMTP - default counts as configured. (The SMTP default pointed at ``localhost`` with no credentials is - treated as "not configured"). +- TOM Toolkit determines whether email is configured using + ``tom_common.accounts.email.email_is_configured`` - ``manage.py check`` warns (``tom_common.W002``) when approval-required registration or password reset is enabled without a configured email backend.