From 0cd979d9a90d1905fa33b9edd789cf4695fe1e61 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Sat, 14 Nov 2015 14:44:06 +0100 Subject: [PATCH 1/3] Move language to User model. see #766 --- modoboa/core/__init__.py | 3 +- modoboa/core/app_settings.py | 30 ------------------- modoboa/core/constants.py | 18 +++++++++++ modoboa/core/forms.py | 5 +++- modoboa/core/migrations/0005_user_language.py | 20 +++++++++++++ modoboa/core/models.py | 9 ++++++ .../core/templates/core/user_preferences.html | 19 ++++++++---- modoboa/core/views/auth.py | 6 ++-- modoboa/core/views/user.py | 15 +++++++--- .../lib/migrations/0004_auto_20151114_1409.py | 25 ++++++++++++++++ modoboa/lib/parameters.py | 20 ++++++------- 11 files changed, 115 insertions(+), 55 deletions(-) create mode 100644 modoboa/core/constants.py create mode 100644 modoboa/core/migrations/0005_user_language.py create mode 100644 modoboa/lib/migrations/0004_auto_20151114_1409.py diff --git a/modoboa/core/__init__.py b/modoboa/core/__init__.py index 7b9f6ba3f..641809b03 100644 --- a/modoboa/core/__init__.py +++ b/modoboa/core/__init__.py @@ -54,10 +54,9 @@ def load_core_settings(): This function must be manually called (see :file:`urls.py`) in order to load base settings. """ - from modoboa.core.app_settings import GeneralParametersForm, UserSettings + from modoboa.core.app_settings import GeneralParametersForm parameters.register(GeneralParametersForm, ugettext_lazy("General")) - parameters.register(UserSettings, ugettext_lazy("General")) events.declare(BASE_EVENTS) diff --git a/modoboa/core/app_settings.py b/modoboa/core/app_settings.py index e73a5fa53..648f02215 100644 --- a/modoboa/core/app_settings.py +++ b/modoboa/core/app_settings.py @@ -357,33 +357,3 @@ class GeneralParametersForm(parameters.AdminParametersForm): }) else: settings.AUTH_LDAP_GLOBAL_OPTIONS[ldap.OPT_REFERRALS] = False - - -def translate_language_code(value): - if "-" in value: - return value.split("-")[0] - return value - - -class UserSettings(parameters.UserParametersForm): - app = "core" - - sep = SeparatorField(label=ugettext_lazy("Display")) - - lang = forms.ChoiceField( - initial=translate_language_code(settings.LANGUAGE_CODE), - label=ugettext_lazy("Prefered language"), - choices=[("cs", "čeština"), - ("de", "deutsch"), - ("en", "english"), - ("es", "español"), - ("fr", "français"), - ("it", "italiano"), - ("nl", "nederlands"), - ("pt_PT", "português"), - ("pt_BR", "português (BR)"), - ("ru", "русский"), - ("sv", "svenska")], - help_text=ugettext_lazy("Prefered language to display pages"), - widget=forms.Select(attrs={"class": "form-control"}) - ) diff --git a/modoboa/core/constants.py b/modoboa/core/constants.py new file mode 100644 index 000000000..4a7f669ea --- /dev/null +++ b/modoboa/core/constants.py @@ -0,0 +1,18 @@ +# coding: utf-8 + +"""Core application constants.""" + + +LANGUAGES = ( + ("cs", u"čeština"), + ("de", u"deutsch"), + ("en", u"english"), + ("es", u"español"), + ("fr", u"français"), + ("it", u"italiano"), + ("nl", u"nederlands"), + ("pt_PT", u"português"), + ("pt_BR", u"português (BR)"), + ("ru", u"русский"), + ("sv", u"svenska"), +) diff --git a/modoboa/core/forms.py b/modoboa/core/forms.py index 2a35c7b6e..c1d96aa28 100644 --- a/modoboa/core/forms.py +++ b/modoboa/core/forms.py @@ -27,6 +27,9 @@ class LoginForm(forms.Form): class ProfileForm(forms.ModelForm): + + """Form to update User profile.""" + oldpassword = forms.CharField( label=ugettext_lazy("Old password"), required=False, widget=forms.PasswordInput(attrs={"class": "form-control"}) @@ -42,7 +45,7 @@ class ProfileForm(forms.ModelForm): class Meta: model = User - fields = ("first_name", "last_name") + fields = ("first_name", "last_name", "language") widgets = { 'first_name': forms.TextInput(attrs={'class': 'form-control'}), 'last_name': forms.TextInput(attrs={'class': 'form-control'}) diff --git a/modoboa/core/migrations/0005_user_language.py b/modoboa/core/migrations/0005_user_language.py new file mode 100644 index 000000000..3f61578ca --- /dev/null +++ b/modoboa/core/migrations/0005_user_language.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0004_auto_20150728_1228'), + ] + + operations = [ + migrations.AddField( + model_name='user', + name='language', + field=models.CharField(default=b'en', help_text='Prefered language to display pages.', max_length=10, choices=[(b'cs', '\u010de\u0161tina'), (b'de', 'deutsch'), (b'en', 'english'), (b'es', 'espa\xf1ol'), (b'fr', 'fran\xe7ais'), (b'it', 'italiano'), (b'nl', 'nederlands'), (b'pt_PT', 'portugu\xeas'), (b'pt_BR', 'portugu\xeas (BR)'), (b'ru', '\u0440\u0443\u0441\u0441\u043a\u0438\u0439'), (b'sv', 'svenska')]), + preserve_default=True, + ), + ] diff --git a/modoboa/core/models.py b/modoboa/core/models.py index e407814b3..3b965bc3c 100644 --- a/modoboa/core/models.py +++ b/modoboa/core/models.py @@ -23,6 +23,8 @@ from modoboa.lib.exceptions import ( PermDeniedException, InternalError, BadRequest, Conflict ) +from . import constants + try: from modoboa.lib.ldap_utils import LDAPAuthBackend @@ -60,6 +62,13 @@ class User(PermissionsMixin): ugettext_lazy('last login'), default=timezone.now ) + language = models.CharField( + max_length=10, default="en", choices=constants.LANGUAGES, + help_text=ugettext_lazy( + "Prefered language to display pages." + ) + ) + objects = UserManager() USERNAME_FIELD = 'username' diff --git a/modoboa/core/templates/core/user_preferences.html b/modoboa/core/templates/core/user_preferences.html index ef073e653..8bd449e74 100644 --- a/modoboa/core/templates/core/user_preferences.html +++ b/modoboa/core/templates/core/user_preferences.html @@ -1,9 +1,16 @@ {% load i18n %}{% load url from future %}

{% trans "Preferences" %} {% trans "Customize Modoboa" %}


-
{% csrf_token %} - {% include "common/parameters.html" %} -
- -
+{% if forms %} +
+ {% csrf_token %} + {% include "common/parameters.html" %} +
+ +
+{% else %} +
+ {% trans "No preferences available." %} +
+{% endif %} diff --git a/modoboa/core/views/auth.py b/modoboa/core/views/auth.py index 44325a366..1f173e194 100644 --- a/modoboa/core/views/auth.py +++ b/modoboa/core/views/auth.py @@ -4,6 +4,7 @@ import logging from django.contrib.auth import authenticate, login, logout from django.core.urlresolvers import reverse from django.http import HttpResponse, HttpResponseRedirect +from django.utils import translation from django.utils.translation import ugettext as _ from django.views.decorators.cache import never_cache @@ -26,8 +27,9 @@ def dologin(request): if not form.cleaned_data["rememberme"]: request.session.set_expiry(0) - request.session["django_language"] = \ - parameters.get_user(request.user, "LANG") + translation.activate(request.user.language) + request.session[translation.LANGUAGE_SESSION_KEY] = ( + request.user.language) logger.info( _("User '%s' successfully logged in" % user.username) diff --git a/modoboa/core/views/user.py b/modoboa/core/views/user.py index 7c444bbd6..91583856b 100644 --- a/modoboa/core/views/user.py +++ b/modoboa/core/views/user.py @@ -1,15 +1,17 @@ """Simple user views.""" -from django.contrib.auth.decorators import login_required from django.shortcuts import render +from django.utils import translation from django.utils.translation import ugettext as _ -from modoboa.core.forms import ProfileForm +from django.contrib.auth.decorators import login_required + from modoboa.lib import events, parameters from modoboa.lib.cryptutils import encrypt from modoboa.lib.web_utils import ( _render_to_string, render_to_json_response ) +from ..forms import ProfileForm @login_required @@ -23,6 +25,7 @@ def index(request, tplname="core/user_index.html"): @login_required def profile(request, tplname='core/user_profile.html'): + """Profile detail/update view.""" update_password = True if True in events.raiseQueryEvent("PasswordChange", request.user): update_password = False @@ -36,8 +39,12 @@ def profile(request, tplname='core/user_profile.html'): if update_password and form.cleaned_data["confirmation"] != "": request.session["password"] = encrypt( form.cleaned_data["confirmation"]) + translation.activate(request.user.language) + request.session[translation.LANGUAGE_SESSION_KEY] = ( + request.user.language) return render_to_json_response(_("Profile updated")) - return render_to_json_response({'form_errors': form.errors}, status=400) + return render_to_json_response( + {'form_errors': form.errors}, status=400) form = ProfileForm(update_password, instance=request.user) return render_to_json_response({ @@ -50,7 +57,7 @@ def profile(request, tplname='core/user_profile.html'): @login_required def preferences(request): if request.method == "POST": - for formdef in parameters.get_user_forms(request.user, request.POST)(): + for formdef in parameters.get_user_forms(request.user, request.POST): form = formdef["form"] if form.is_valid(): form.save() diff --git a/modoboa/lib/migrations/0004_auto_20151114_1409.py b/modoboa/lib/migrations/0004_auto_20151114_1409.py new file mode 100644 index 000000000..e66d38167 --- /dev/null +++ b/modoboa/lib/migrations/0004_auto_20151114_1409.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +def move_language_preference(apps, schema_editor): + """Move language from parameters to User model.""" + UserParameter = apps.get_model("lib", "UserParameter") + for uparam in UserParameter.objects.filter(name="core.LANG"): + uparam.user.language = uparam.value + uparam.user.save() + uparam.delete() + + +class Migration(migrations.Migration): + + dependencies = [ + ('lib', '0003_rename_parameters'), + ('core', '0005_user_language'), + ] + + operations = [ + migrations.RunPython(move_language_preference), + ] diff --git a/modoboa/lib/parameters.py b/modoboa/lib/parameters.py index a18463030..1ea5eadc2 100644 --- a/modoboa/lib/parameters.py +++ b/modoboa/lib/parameters.py @@ -332,18 +332,18 @@ def get_admin_forms(*args, **kwargs): def get_user_forms(user, *args, **kwargs): + """Return an instance of each user-level forms.""" kwargs["user"] = user sorted_apps = get_sorted_apps('U', first="general") - - def realfunc(): - for app in sorted_apps: - formdef = _params['U'][app] - if not formdef["form"].has_access(user): - continue - yield {"label": formdef["label"], - "form": formdef["form"](*args, **kwargs)} - - return realfunc + result = [] + for app in sorted_apps: + formdef = _params["U"][app] + if not formdef["form"].has_access(user): + continue + result.append({ + "label": formdef["label"], + "form": formdef["form"](*args, **kwargs)}) + return result def get_parameter_form(level, name, app=None): From 2ca759531cd0493fb595912d0a3e8c41f2959ecf Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Sat, 14 Nov 2015 14:49:18 +0100 Subject: [PATCH 2/3] Fix unit test. see #766 --- modoboa/core/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modoboa/core/tests.py b/modoboa/core/tests.py index 3a8e0aa42..c64432991 100644 --- a/modoboa/core/tests.py +++ b/modoboa/core/tests.py @@ -22,7 +22,7 @@ class ProfileTestCase(ModoTestCase): * A normal user changes his password """ self.ajax_post(reverse("core:user_profile"), - {"oldpassword": "password", + {"language": "en", "oldpassword": "password", "newpassword": "12345Toi", "confirmation": "12345Toi"}) self.clt.logout() From 4d73d91fce5943cb08c666458d8464940b93ed55 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Sat, 14 Nov 2015 14:59:03 +0100 Subject: [PATCH 3/3] Fix unit test. see #766 --- modoboa/core/tests.py | 2 +- modoboa/core/views/auth.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modoboa/core/tests.py b/modoboa/core/tests.py index c64432991..aaeca7d03 100644 --- a/modoboa/core/tests.py +++ b/modoboa/core/tests.py @@ -42,7 +42,7 @@ class ProfileTestCase(ModoTestCase): self.ajax_post( reverse("core:user_profile"), - {"oldpassword": "toto", + {"language": "en", "oldpassword": "toto", "newpassword": "Toto1234", "confirmation": "Toto1234"} ) self.clt.logout() diff --git a/modoboa/core/views/auth.py b/modoboa/core/views/auth.py index 1f173e194..2fe6a4062 100644 --- a/modoboa/core/views/auth.py +++ b/modoboa/core/views/auth.py @@ -9,7 +9,7 @@ from django.utils.translation import ugettext as _ from django.views.decorators.cache import never_cache from modoboa.core.forms import LoginForm -from modoboa.lib import events, parameters +from modoboa.lib import events from modoboa.lib.web_utils import _render_to_string