deleted file mode 100644
@@ -1,18 +0,0 @@
-{% extends "base.html" %}
-{% load i18n %}
-
-{% block content %}
-
-{% if account %}
-
-<p>{% trans "Account successfully activated" %}</p>
-
-<p><a href="{% url "login" %}">{% trans "Log in" %}</a></p>
-
-{% else %}
-
-<p>{% trans "Account activation failed" %}</p>
-
-{% endif %}
-
-{% endblock %}
@@ -7,7 +7,7 @@ link is valid for {{ expiration_days }} days.
{% endblocktrans %}
-http://{{ site.domain }}{% url "django_registration_activate" activation_key %}
+http://{{ site.domain }}{% url "django_registration_activate" %}?activation_key={{ activation_key|urlencode }}
{% blocktrans %}
If you did not make this request, please ignore this message.
deleted file mode 100644
@@ -1,13 +0,0 @@
-{% extends "base.html" %}
-{% load i18n %}
-
-{% block content %}
-
-{% blocktrans %}
-
-Activation has failed.
-<br/>
-
-{% endblocktrans %}
-
-{% endblock %}
new file mode 100644
@@ -0,0 +1,21 @@
+{% extends "base.html" %}
+{% load i18n %}
+
+{% block content %}
+
+{% if activation_error %}
+<div class="alert alert-danger">{{ activation_error.message }}</div>
+{% elif form.errors %}
+<div class="alert alert-danger">{% trans "This activation link is invalid or has expired." %}</div>
+{% else %}
+<p>{% trans "Click the button below to activate your account." %}</p>
+<form id="activation_form" method="post" action="">
+ {% for field in form %}
+ <input type="hidden" name="{{ field.html_name }}" value="{{ field.value|default_if_none:'' }}" />
+ {% endfor %}
+ <input type="submit" class="btn btn-default" value="{% trans 'Activate account' %}" />
+ {% csrf_token %}
+</form>
+{% endif %}
+
+{% endblock %}
new file mode 100644
@@ -0,0 +1,52 @@
+# layerindex-web - tests for the account activation email and page
+#
+# Licensed under the MIT license, see COPYING.MIT for details
+#
+# SPDX-License-Identifier: MIT
+
+import re
+
+import pytest
+from django.contrib.auth.models import User
+from django.core import mail
+from django.test import RequestFactory, TestCase
+from django.urls import reverse
+from django_registration.backends.activation.views import RegistrationView
+
+
+@pytest.mark.django_db
+class TestAccountActivation(TestCase):
+
+ def setUp(self):
+ self.user = User.objects.create_user('newuser', 'new@example.com', 'pw', is_active=False)
+
+ def _activation_path(self):
+ view = RegistrationView()
+ view.request = RequestFactory().get('/')
+ view.send_activation_email(self.user)
+ self.assertEqual(len(mail.outbox), 1)
+ self.assertEqual(mail.outbox[0].to, ['new@example.com'])
+ match = re.search(r'https?://[^/\s]+(/\S+)', mail.outbox[0].body)
+ self.assertIsNotNone(match, mail.outbox[0].body)
+ return match.group(1)
+
+ def test_activation_link_activates_account(self):
+ path = self._activation_path()
+ self.assertTrue(path.startswith(reverse('django_registration_activate') + '?activation_key='))
+
+ response = self.client.get(path)
+ self.assertEqual(response.status_code, 200)
+ self.assertContains(response, 'activation_form')
+
+ key = response.context['form']['activation_key'].value()
+ response = self.client.post(reverse('django_registration_activate'), {'activation_key': key})
+ self.assertRedirects(response, reverse('django_registration_activation_complete'))
+ self.user.refresh_from_db()
+ self.assertTrue(self.user.is_active)
+
+ def test_invalid_key_does_not_activate(self):
+ response = self.client.post(reverse('django_registration_activate'), {'activation_key': 'bogus'})
+ self.assertEqual(response.status_code, 200)
+ self.assertContains(response, 'invalid or has expired')
+ self.user.refresh_from_db()
+ self.assertFalse(self.user.is_active)
Since the Django 5.2 upgrade (23fdeac) pulled in django-registration 5.x, sending an activation email fails with NoReverseMatch: the template passed the activation key as a URL argument, but the "django_registration_activate" URL now takes it as the activation_key query parameter. New registrations, and users changing their email address on the profile page, were left deactivated with no email sent. The activation view is now a form that POSTs the key back and renders django_registration/activation_form.html, so replace the old activate.html and activation_failed.html templates with it. AI-Generated: Claude Opus 5.5 Signed-off-by: Tim Orling <tim.orling@konsulko.com> --- templates/django_registration/activate.html | 18 ------- .../activation_email_body.txt | 2 +- .../activation_failed.html | 13 ----- .../django_registration/activation_form.html | 21 ++++++++ tests/test_account_activation.py | 52 +++++++++++++++++++ 5 files changed, 74 insertions(+), 32 deletions(-) delete mode 100644 templates/django_registration/activate.html delete mode 100644 templates/django_registration/activation_failed.html create mode 100644 templates/django_registration/activation_form.html create mode 100644 tests/test_account_activation.py base-commit: e6040597ae0f614581de241fe78f20e34dae13ac