From 3687b1c260eccb2ec559fe794b7a898eacf127d1 Mon Sep 17 00:00:00 2001 From: nasief Date: Mon, 11 Sep 2017 14:19:16 +0300 Subject: [PATCH] Support language on user level Fix issue use ser prefered language Some fixes support translating password reset email Use custom password form in the view --- README.rst | 5 +++++ accountsplus/__init__.py | 2 +- accountsplus/forms.py | 24 +++++++++++++++++++++++- accountsplus/middleware.py | 16 ++++++++++++++++ accountsplus/models.py | 4 ++++ accountsplus/settings.py | 19 +++++++++++++++++++ accountsplus/views.py | 10 +++++++++- 7 files changed, 77 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 201bcf9..363df6a 100644 --- a/README.rst +++ b/README.rst @@ -104,3 +104,8 @@ Quick start 3. For more information on those libraries, check the following docs:: 1. [django-axes](https://django-axes.readthedocs.io/en/latest/) 2. [django-recaptcha](https://github.com/praekelt/django-recaptcha) + + +8. If you have i18n enabled within your application, you can set a preferred language for the user + 1. If you define a default language `LANGUAGE_CODE` it will be used as default or `en` + 2. Languages supported are those languages you define in your application in `LANGUAGES` setting diff --git a/accountsplus/__init__.py b/accountsplus/__init__.py index 8f6b514..55fbdd6 100644 --- a/accountsplus/__init__.py +++ b/accountsplus/__init__.py @@ -1,3 +1,3 @@ -__version__ = '1.4.2' +__version__ = '1.4.3' default_app_config = 'accountsplus.apps.AccountsConfig' diff --git a/accountsplus/forms.py b/accountsplus/forms.py index 6c9fbbc..c9700a6 100644 --- a/accountsplus/forms.py +++ b/accountsplus/forms.py @@ -1,14 +1,27 @@ from __future__ import unicode_literals +from contextlib import contextmanager + import django.forms from django.conf import settings from django.apps import apps -from django.contrib.auth.forms import AuthenticationForm +from django.contrib.auth.forms import AuthenticationForm, PasswordResetForm from django.contrib.admin.forms import AdminAuthenticationForm +from django.utils import translation from captcha.fields import ReCaptchaField +@contextmanager +def language(lang): + old_language = translation.get_language() + try: + translation.activate(lang) + yield + finally: + translation.activate(old_language) + + class CaptchaForm(django.forms.Form): captcha = ReCaptchaField() username = django.forms.CharField() @@ -31,3 +44,12 @@ class EmailBasedAdminAuthenticationForm(AdminAuthenticationForm): def clean_username(self): return self.data['username'].lower() + + +class CustomPasswordResetForm(PasswordResetForm): + + def send_mail(self, subject_template_name, email_template_name, + context, from_email, to_email, html_email_template_name=None): + with language(context['email_lang']): + super(CustomPasswordResetForm, self).send_mail(subject_template_name, email_template_name, context, + from_email, to_email, html_email_template_name) diff --git a/accountsplus/middleware.py b/accountsplus/middleware.py index 3caa62b..2546b74 100644 --- a/accountsplus/middleware.py +++ b/accountsplus/middleware.py @@ -1,11 +1,27 @@ from __future__ import unicode_literals import django.utils.timezone from django.utils.deprecation import MiddlewareMixin +from django.utils import translation class TimezoneMiddleware(MiddlewareMixin): + def process_request(self, request): if request.user.is_authenticated() and request.user.timezone: django.utils.timezone.activate(request.user.timezone) else: django.utils.timezone.deactivate() + + +class UserLanguageMiddleware(MiddlewareMixin): + + # Update user preferred language each time a request has a new language and activate translation for that user. + # Should be added after LocaleMiddleware as it depends on having request.LANGUAGE_CODE configured there. + def process_request(self, request): + user = request.user + if not user.preferred_language: + user.preferred_language = request.LANGUAGE_CODE + user.save() + else: + translation.activate(user.preferred_language) + request.LANGUAGE_CODE = translation.get_language() diff --git a/accountsplus/models.py b/accountsplus/models.py index 2f6d487..c8a9856 100644 --- a/accountsplus/models.py +++ b/accountsplus/models.py @@ -16,6 +16,8 @@ import timezone_field import localflavor.us.models +import settings + logger = logging.getLogger(__name__) @@ -103,6 +105,8 @@ class BaseUser(django.contrib.auth.base_user.AbstractBaseUser, django.contrib.au last_name = django.db.models.CharField(_('Last Name'), max_length=50) email = django.db.models.EmailField(_('Email'), unique=True) timezone = timezone_field.TimeZoneField(default='America/New_York') + preferred_language = django.db.models.CharField(_('Preferred Language'), choices=settings.SUPPORTED_LANGUAGES, + blank=True, null=True, max_length=10) objects = UserManager() diff --git a/accountsplus/settings.py b/accountsplus/settings.py index d281463..6457653 100644 --- a/accountsplus/settings.py +++ b/accountsplus/settings.py @@ -1,11 +1,26 @@ from django.conf import settings from django.core.exceptions import ImproperlyConfigured from django.apps import apps +from django.utils.translation import ugettext_lazy as _ # Default values LOCKOUT_TEMPLATE = 'accounts/locked_out.html' +ENGLISH_LANGUAGE = 'en-us' +SPANISH_LANGUAGE = 'es' +FRENCH_LANGUAGE = 'fr' +PORTUGUESE_LANGUAGE = 'pt' +ARABIC_LANGUAGE = 'ar' +DEFAULT_SUPPORTED_LANGUAGES = ( + (ENGLISH_LANGUAGE, _('English')), + (SPANISH_LANGUAGE, _('Spanish')), + (FRENCH_LANGUAGE, _('French')), + (PORTUGUESE_LANGUAGE, _('Portuguese')), + (ARABIC_LANGUAGE, _('Arabic')), +) + + def get_setting(setting_str, is_required, default_value=None): try: return getattr(settings, setting_str) @@ -60,3 +75,7 @@ def get_lockout_template(): LOGIN_FAILURE_LIMIT = int(get_login_failure_limit()) LOCKOUT_URL = str(get_lockout_url()) LOCKOUT_TEMPLATE = get_lockout_template() + + +SUPPORTED_LANGUAGES = get_setting('LANGUAGES', False, DEFAULT_SUPPORTED_LANGUAGES) +DEFAULT_LANGUAGE = get_setting('LANGUAGE_CODE', False, ENGLISH_LANGUAGE) diff --git a/accountsplus/views.py b/accountsplus/views.py index 015afae..5cb7ee8 100644 --- a/accountsplus/views.py +++ b/accountsplus/views.py @@ -163,7 +163,7 @@ def password_reset(request, template_name='registration/password_reset_form.html', email_template_name='registration/password_reset_email.html', subject_template_name='registration/password_reset_subject.txt', - password_reset_form=django.contrib.auth.forms.PasswordResetForm, + password_reset_form=forms.CustomPasswordResetForm, token_generator=django.contrib.auth.views.default_token_generator, post_reset_redirect=None, from_email=None, @@ -172,6 +172,14 @@ def password_reset(request, html_email_template_name=None, extra_email_context=None): User = django.contrib.auth.get_user_model() + # We set this always in the middleware to the preferred language of the user + if request.method == 'POST': + email = request.POST['email'] + try: + user = User.objects.get(email=email) + extra_email_context['email_lang'] = user.preferred_language or request.LANGUAGE_CODE + except User.DoesNotExist: + pass response = django.contrib.auth.views.password_reset( request, template_name, email_template_name,