From 100bc79bf622cadf8323fc6578eb64803536b6a7 Mon Sep 17 00:00:00 2001 From: Andrzej Pragacz Date: Tue, 24 Sep 2019 11:38:27 +0200 Subject: [PATCH] Fix issue #68 Add VERIFICATION_TEMPLATE_CONTEXT_BUILDER settings key --- rest_registration/api/views/register.py | 15 +++++-- rest_registration/api/views/register_email.py | 14 ++++-- rest_registration/api/views/reset_password.py | 15 +++++-- rest_registration/notifications/email.py | 44 ++++++++++++------- rest_registration/notifications/enums.py | 11 +++++ rest_registration/settings_fields.py | 34 ++++++++++++++ rest_registration/utils/verification.py | 13 ++++++ 7 files changed, 117 insertions(+), 29 deletions(-) create mode 100644 rest_registration/notifications/enums.py diff --git a/rest_registration/api/views/register.py b/rest_registration/api/views/register.py index 5272020..cee1db6 100644 --- a/rest_registration/api/views/register.py +++ b/rest_registration/api/views/register.py @@ -11,7 +11,10 @@ api_view_serializer_class_getter ) from rest_registration.exceptions import BadRequest -from rest_registration.notifications import send_verification_notification +from rest_registration.notifications.email import ( + send_verification_notification +) +from rest_registration.notifications.enums import NotificationType from rest_registration.settings import registration_settings from rest_registration.utils.responses import get_ok_response from rest_registration.utils.users import ( @@ -92,9 +95,13 @@ def register(request): signer = RegisterSigner({ 'user_id': get_user_verification_id(user), }, request=request) - template_config = ( - registration_settings.REGISTER_VERIFICATION_EMAIL_TEMPLATES) - send_verification_notification(user, signer, template_config) + template_config_data = registration_settings.REGISTER_VERIFICATION_EMAIL_TEMPLATES # noqa: E501 + notification_data = { + 'params_signer': signer, + } + send_verification_notification( + NotificationType.REGISTER_VERIFICATION, user, notification_data, + template_config_data) return Response(user_data, status=status.HTTP_201_CREATED) diff --git a/rest_registration/api/views/register_email.py b/rest_registration/api/views/register_email.py index d8c836d..629629b 100644 --- a/rest_registration/api/views/register_email.py +++ b/rest_registration/api/views/register_email.py @@ -9,7 +9,10 @@ api_view_serializer_class_getter ) from rest_registration.exceptions import BadRequest -from rest_registration.notifications import send_verification_notification +from rest_registration.notifications.email import ( + send_verification_notification +) +from rest_registration.notifications.enums import NotificationType from rest_registration.settings import registration_settings from rest_registration.utils.responses import get_ok_response from rest_registration.utils.users import ( @@ -55,15 +58,18 @@ def register_email(request): if user_with_email_exists(email): raise BadRequest("This email is already registered.") - template_config = ( - registration_settings.REGISTER_EMAIL_VERIFICATION_EMAIL_TEMPLATES) + template_config_data = registration_settings.REGISTER_EMAIL_VERIFICATION_EMAIL_TEMPLATES # noqa: E501 if registration_settings.REGISTER_EMAIL_VERIFICATION_ENABLED: signer = RegisterEmailSigner({ 'user_id': get_user_verification_id(user), 'email': email, }, request=request) + notification_data = { + 'params_signer': signer, + } send_verification_notification( - user, signer, template_config, email=email) + NotificationType.REGISTER_EMAIL_VERIFICATION, user, + notification_data, template_config_data, custom_user_address=email) else: email_field_name = get_user_email_field_name() old_email = getattr(user, email_field_name) diff --git a/rest_registration/api/views/reset_password.py b/rest_registration/api/views/reset_password.py index b0c5aa8..6fe46a9 100644 --- a/rest_registration/api/views/reset_password.py +++ b/rest_registration/api/views/reset_password.py @@ -10,7 +10,10 @@ api_view_serializer_class_getter ) from rest_registration.exceptions import UserNotFound -from rest_registration.notifications import send_verification_notification +from rest_registration.notifications.email import ( + send_verification_notification +) +from rest_registration.notifications.enums import NotificationType from rest_registration.settings import registration_settings from rest_registration.utils.responses import get_ok_response from rest_registration.utils.users import ( @@ -71,9 +74,13 @@ def send_reset_password_link(request): 'user_id': get_user_verification_id(user), }, request=request) - template_config = ( - registration_settings.RESET_PASSWORD_VERIFICATION_EMAIL_TEMPLATES) - send_verification_notification(user, signer, template_config) + template_config_data = registration_settings.RESET_PASSWORD_VERIFICATION_EMAIL_TEMPLATES # noqa: E501 + notification_data = { + 'params_signer': signer, + } + send_verification_notification( + NotificationType.RESET_PASSWORD_VERIFICATION, user, notification_data, + template_config_data) return get_ok_response('Reset link sent') diff --git a/rest_registration/notifications/email.py b/rest_registration/notifications/email.py index c6a0a66..2285d8c 100644 --- a/rest_registration/notifications/email.py +++ b/rest_registration/notifications/email.py @@ -5,6 +5,7 @@ from django.template.exceptions import TemplateDoesNotExist from django.template.loader import get_template, render_to_string +from rest_registration.notifications.enums import NotificationMethod from rest_registration.settings import registration_settings from rest_registration.utils.common import identity from rest_registration.utils.users import get_user_email_field_name @@ -18,26 +19,28 @@ def send_verification_notification( - user, params_signer, template_config_data, email=None): + notification_type, user, data, template_config_data, + custom_user_address=None): + if custom_user_address is None: + user_address = get_user_address(user) + else: + user_address = custom_user_address notification = create_verification_notification( - user, params_signer, template_config_data, email=email) + notification_type, user, user_address, data, template_config_data) send_notification(notification) def create_verification_notification( - user, params_signer, template_config_data, email=None): - if email is None: - email_field_name = get_user_email_field_name() - email = getattr(user, email_field_name) - + notification_type, user, user_address, data, + template_config_data): from_email = registration_settings.VERIFICATION_FROM_EMAIL reply_to_email = (registration_settings.VERIFICATION_REPLY_TO_EMAIL or from_email) - context = { - 'user': user, - 'email': email, - 'verification_url': params_signer.get_url(), - } + template_context_builder = registration_settings.VERIFICATION_TEMPLATE_CONTEXT_BUILDER # noqa: E501 + context = template_context_builder( + user, user_address, data, + notification_type=notification_type, + notification_method=NotificationMethod.EMAIL) template_config = parse_template_config(template_config_data) subject = render_to_string( @@ -47,7 +50,8 @@ def create_verification_notification( template_config.text_body_template_name, context=context)) email_msg = EmailMultiAlternatives( - subject, text_body, from_email, [email], reply_to=[reply_to_email]) + subject, text_body, from_email, [user_address], + reply_to=[reply_to_email]) if template_config.html_body_template_name: html_body = render_to_string( @@ -57,6 +61,16 @@ def create_verification_notification( return email_msg +def send_notification(notification): + notification.send() + + +def get_user_address(user): + email_field_name = get_user_email_field_name() + email = getattr(user, email_field_name) + return email + + def parse_template_config(template_config_data): """ >>> from tests import doctest_utils @@ -203,7 +217,3 @@ def _validate_template_name_existence(template_name): raise ImproperlyConfigured( 'Template {template_name!r} does not exists'.format( template_name=template_name)) - - -def send_notification(notification): - notification.send() diff --git a/rest_registration/notifications/enums.py b/rest_registration/notifications/enums.py new file mode 100644 index 0000000..3ab7491 --- /dev/null +++ b/rest_registration/notifications/enums.py @@ -0,0 +1,11 @@ +from enum import Enum, auto + + +class NotificationType(Enum): + REGISTER_VERIFICATION = auto() + REGISTER_EMAIL_VERIFICATION = auto() + RESET_PASSWORD_VERIFICATION = auto() + + +class NotificationMethod(Enum): + EMAIL = auto() diff --git a/rest_registration/settings_fields.py b/rest_registration/settings_fields.py index 7bc19a3..800755b 100644 --- a/rest_registration/settings_fields.py +++ b/rest_registration/settings_fields.py @@ -247,7 +247,41 @@ def __new__( It is be solely up to the implementer of custom builder function to encode the signed values properly in the URL. """), + ), + Field( + 'VERIFICATION_TEMPLATE_CONTEXT_BUILDER', + default='rest_registration.utils.verification.build_default_template_context', # noqa: E501 + import_string=True, + help=dedent("""\ + The builder function receives these parameters as + positional arguments: + * ``user`` - the user which is to be notified. + * ``user_address`` - the user address, which can be the + user e-mail, phone number, etc. + * ``data`` - dictionary; in most cases it contains only + one entry, which is the ``param_signer`` + under ``'param_signer'`` key. The implementer of the + custom builder function should be aware that the contents + of the dictionary are dynamic and write defensive code + to account that. + + and these parameters as keyword arguments: + + * ``notification_type`` - value of + ``rest_registration.notifications.enums.NotificationType`` + enum. + * ``notification_method`` - value of + ``rest_registration.notifications.enums.NotificationMethod`` + enum. Currently there is only one choice which is + ``NotificationMethod.EMAIL``. + + It is possible that in the future, additional keyword arguments + may be added. Therefore the implementer + of the custom builder function should take account of that, + for instance by adding ``**kwargs`` in the signature + of the function. + """), ), ] diff --git a/rest_registration/utils/verification.py b/rest_registration/utils/verification.py index 56cfa89..5c5b882 100644 --- a/rest_registration/utils/verification.py +++ b/rest_registration/utils/verification.py @@ -21,3 +21,16 @@ def build_default_verification_url(signer): if signer.request: url = signer.request.build_absolute_uri(url) return url + + +def build_default_template_context( + user, user_address, data, + notification_type=None, notification_method=None): + context = { + 'user': user, + 'email': user_address, + } + params_signer = data.get('params_signer') + if params_signer: + context['verification_url'] = params_signer.get_url() + return context