diff --git a/src/backoffice/tests/auth.py b/src/backoffice/tests/auth.py index 7881dd10700257c4713ca7d36e3c2347fe711618..e6db6cd5ee566065786590ec68bfe0974fc59272 100644 --- a/src/backoffice/tests/auth.py +++ b/src/backoffice/tests/auth.py @@ -38,7 +38,7 @@ class RegistrationTests(BackOfficeTestCase): self.assertEqual(len(mail.outbox), 1) sent_mail = mail.outbox[0] self.assertEqual(sent_mail.to, ['unittest@example.com']) - self.assertEqual(sent_mail.subject, 'Activate your testserver account') + self.assertEqual(sent_mail.subject, f'Activate your {self.conf.name} account') reset_url_pattern = reverse('backoffice:signup_activate', kwargs={'uid_b64': 'uidb64', 'channel_id': 42, 'token': 'token-token'}) reset_url_pattern = re.escape(reset_url_pattern) reset_url_pattern = reset_url_pattern.replace('uidb64', '([^/]+)') diff --git a/src/core/locale/de/LC_MESSAGES/django.po b/src/core/locale/de/LC_MESSAGES/django.po index 568e1721cd065aebb5c4fff854b203814440c926..02de937a0d6ac4cb74efccd193d8c29723e7d3e8 100644 --- a/src/core/locale/de/LC_MESSAGES/django.po +++ b/src/core/locale/de/LC_MESSAGES/django.po @@ -2525,6 +2525,12 @@ msgstr "Das Bild muss die Abmessungen von minimal %(min_width)spx/%(min_height)s msgid "Validation__error_image_square" msgstr "Das Bild muss quadratisch sein." +msgid "PasswordReset__email__sending__success" +msgstr "Sofern uns die angegebene E-Mail-Adresse bekannt ist, haben wir dir gerade eine E-Mail gesendet um dein Kennwort zurückzusetzen. Bitte klicke den darin enthaltenen Link!" + +msgid "PasswordReset__email__sending__failure" +msgstr "Leider gab es ein Problem beim Senden der Kennwort-Zurücksetz-E-Mail, bitte versuche es später erneut." + msgid "Registration" msgstr "Registrieren" @@ -2532,7 +2538,7 @@ msgid "Registration__email__sending__success" msgstr "Wir haben dir eine E-Mail gesendet um die angegebene Adresse zu bestätigen und das Konto zu aktivieren. Bitte klicke den darin enthaltenen Link!" msgid "Registration__email__sending__failure" -msgstr "Leider gab es ein Problem beim senden der Registrierungs-Email, bitte versuche es später erneut." +msgstr "Leider gab es ein Problem beim Senden der Registrierungs-E-Mail, bitte versuche es später erneut." msgid "registration__activation__user_does_not_exist" msgstr "Der zu aktivierende Account wurde nicht gefunden." diff --git a/src/core/locale/en/LC_MESSAGES/django.po b/src/core/locale/en/LC_MESSAGES/django.po index a1d567be4043905581a5bc95bd6735e1f1eb2dc8..d448ab5d68dc824eb73bdd120bedd2ef55428651 100644 --- a/src/core/locale/en/LC_MESSAGES/django.po +++ b/src/core/locale/en/LC_MESSAGES/django.po @@ -2507,6 +2507,12 @@ msgstr "The image must have the dimensions of minimal %(min_width)spx/%(min_heig msgid "Validation__error_image_square" msgstr "The image must be square" +msgid "PasswordReset__email__sending__success" +msgstr "If the address entered matches our records, we have just sent you an e-mail to reset your password. Please click the link in it!" + +msgid "PasswordReset__email__sending__failure" +msgstr "Unfortunately there was a problem sending the password reset email, please try again later." + msgid "Registration" msgstr "Sign Up" diff --git a/src/core/templates/registration/core_password_reset_subject.txt b/src/core/templates/registration/core_password_reset_subject.txt index e2aebe02983e5d9269292d9cc3e95be5adca2852..0709f18f5a26d248a7f6ad78196a6b4f2044f516 100644 --- a/src/core/templates/registration/core_password_reset_subject.txt +++ b/src/core/templates/registration/core_password_reset_subject.txt @@ -1,2 +1,2 @@ {% load i18n %} -{% blocktranslate with site_name_safe=site_name|safe%}Password reset on {{ site_name_safe }}{% endblocktranslate %} +{% blocktranslate with site_name_safe=location|safe%}Password reset on {{ site_name_safe }}{% endblocktranslate %} diff --git a/src/core/templates/registration/registration_subject.txt b/src/core/templates/registration/registration_subject.txt index de30b71daa931f98354d0cccf9a811e376d3a569..b74b899937f1d29a2537be95611b49eb56983254 100644 --- a/src/core/templates/registration/registration_subject.txt +++ b/src/core/templates/registration/registration_subject.txt @@ -1,2 +1,2 @@ {% load i18n %} -{% blocktranslate with safe_site_name=site_name|safe %}Activate your {{ safe_site_name }} account{% endblocktranslate %} +{% blocktranslate with safe_site_name=location|safe %}Activate your {{ safe_site_name }} account{% endblocktranslate %} diff --git a/src/core/views/auth.py b/src/core/views/auth.py index a5ac41e65620789f151136238ad991eeb22da6db..82f8e0a7e13c87502f05ec22178cf4710c15c3fb 100644 --- a/src/core/views/auth.py +++ b/src/core/views/auth.py @@ -5,6 +5,7 @@ from typing import Any from django_ratelimit.decorators import ratelimit +from django.conf import settings from django.contrib import messages from django.contrib.auth.views import LoginView, PasswordResetConfirmView, PasswordResetView from django.core.exceptions import NON_FIELD_ERRORS, ImproperlyConfigured, ValidationError @@ -20,12 +21,36 @@ from django.views.decorators.debug import sensitive_post_parameters from django.views.generic import FormView, View from core.forms import LoginForm, PasswordResetForm, RegistrationForm -from core.models import PlatformUser, UserCommunicationChannel +from core.models import Conference, PlatformUser, UserCommunicationChannel from core.tokens import channel_activation_token logger = logging.getLogger(__name__) +class AuthLocationMixin: + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + + # try to guess the conference name from available context + # TODO: refactor with conference selection overhaul + conference_name = None + with contextlib.suppress(ImproperlyConfigured): + # try conference or conf properties + if (hasattr(self, 'conference') and (c := getattr(self, 'conference'))) or (hasattr(self, 'conf') and (c := getattr(self, 'conf'))): + conference_name = c.name + + # try configured public conference + else: + c = Conference.objects.filter(pk=settings.SELECTED_CONFERENCE_ID).first() + if c is not None: + conference_name = c.name + + # if all fails use the configured DJANGO_HOST + context['location'] = conference_name or settings.DJANGO_HOST + + return context + + @method_decorator(ratelimit(key='ip', rate='5/m', method=ratelimit.UNSAFE, block=False), name='post') @method_decorator(ratelimit(key='post:username', rate='5/m', method=ratelimit.UNSAFE, block=False), name='post') class BaseLoginView(LoginView): @@ -46,7 +71,7 @@ class BaseLoginView(LoginView): return super().form_valid(form) -class BasePasswordResetView(PasswordResetView): +class BasePasswordResetView(AuthLocationMixin, PasswordResetView): email_template_name = 'registration/core_password_reset_email.html' extra_email_context = None form_class = PasswordResetForm @@ -59,6 +84,25 @@ class BasePasswordResetView(PasswordResetView): context.setdefault('retry', self.request.GET.get('retry', 'False') == 'True' if hasattr(self.request, 'GET') else False) return context + def form_valid(self, form): + opts = { + 'request': self.request, + 'email_template_name': self.email_template_name, + 'html_email_template_name': self.html_email_template_name, + 'subject_template_name': self.subject_template_name, + 'extra_email_context': (self.extra_email_context or {}) | self.get_context_data(), + } + try: + form.save(**opts) + logger.info('Sending password reset mail to "%(email)s".', {'email': form.cleaned_data['email']}) + messages.success(self.request, _('PasswordReset__email__sending__success')) + except SMTPException: + logger.error('Failed to send password reset mail to "%(email)s".', {'email': form.cleaned_data['email']}) + messages.error(self.request, _('PasswordReset__email__sending__failure')) + form.errors[NON_FIELD_ERRORS] = ValidationError(_('PasswordReset__email__sending__failure')) + return self.form_invalid(form) + return super().form_valid(form) + class BasePasswordResetConfirmView(PasswordResetConfirmView): @method_decorator(sensitive_post_parameters()) @@ -72,7 +116,7 @@ class BasePasswordResetConfirmView(PasswordResetConfirmView): @method_decorator(ratelimit(key='ip', rate='5/m', method=ratelimit.UNSAFE, block=False), name='post') @method_decorator(ratelimit(key='post:username', rate='5/m', method=ratelimit.UNSAFE, block=False), name='post') -class BaseRegistrationView(FormView): +class BaseRegistrationView(AuthLocationMixin, FormView): email_template_name = 'registration/registration_email.html' extra_email_context = None form_class = RegistrationForm @@ -100,14 +144,14 @@ class BaseRegistrationView(FormView): 'subject_template_name': self.subject_template_name, 'request': self.request, 'html_email_template_name': self.html_email_template_name, - 'extra_email_context': self.extra_email_context, + 'extra_email_context': (self.extra_email_context or {}) | self.get_context_data(), } try: form.save(**opts) logger.info('Sent account activation mail to "%(email)s" for %(user)s.', {'email': form.instance.email, 'user': form.instance.username}) messages.success(self.request, _('Registration__email__sending__success')) except SMTPException: - logger.info('Failed to send account activation mail to "%(email)s" for %(user)s.', {'email': form.instance.email, 'user': form.instance.username}) + logger.error('Failed to send account activation mail to "%(email)s" for %(user)s.', {'email': form.instance.email, 'user': form.instance.username}) messages.error(self.request, _('Registration__email__sending__failure')) form.errors[NON_FIELD_ERRORS] = ValidationError(_('Registration__email__sending__failure')) return self.form_invalid(form) diff --git a/src/plainui/tests/test_auth.py b/src/plainui/tests/test_auth.py index ec55b78c36f849b3326141570dd9fee245a592b0..7dc7cc95a3a0c79328000852bbb576152f453491 100644 --- a/src/plainui/tests/test_auth.py +++ b/src/plainui/tests/test_auth.py @@ -80,7 +80,7 @@ class AuthViewTest(ViewsTestBase): self.assertEqual(len(mail.outbox), 1) sent_mail = mail.outbox[0] self.assertEqual(sent_mail.to, ['no@where.test']) - self.assertEqual(sent_mail.subject, 'Password reset on testserver') + self.assertEqual(sent_mail.subject, f'Password reset on {self.conf.name}') reset_url_pattern = reverse('plainui:password_reset_confirm', kwargs={'uidb64': 'uidb64', 'token': 'token'}) reset_url_pattern = re.escape(reset_url_pattern) reset_url_pattern = reset_url_pattern.replace('uidb64', '([^/]+)')