locale: Adds preferred language for logged in user

- UserProfile model is created, it has one-to-one relationship with User.

- Language selection dropdown added to UserCreate and UserUpdate forms.

- Adds None to language selection dropdown to explicitly unselect.

- LANGUAGE_SESSION_KEY is set to User's preferred language on LogIn activity.

- LANGUAGE_SESSION_KEY is deleted on User's LogOut activity.

Signed-off-by: Sai Kiran Naragam <saikiran.rguiiit@gmail.com>
Reviewed-by: Sunil Mohan Adapa <sunil@medhas.org>
This commit is contained in:
Sai Kiran Naragam 2018-01-30 11:51:07 +05:30 committed by James Valleroy
parent 5cff5629d8
commit 837e0b8384
No known key found for this signature in database
GPG Key ID: 77C0C75E7B650808
7 changed files with 82 additions and 8 deletions

View File

@ -60,7 +60,7 @@ class LanguageSelectionForm(forms.Form):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
supported_languages = []
supported_languages = [(None, '-----------')]
for language_code, language_name in settings.LANGUAGES:
locale_code = translation.to_locale(language_code)
plinth_dir = os.path.dirname(plinth.__file__)
@ -70,4 +70,4 @@ class LanguageSelectionForm(forms.Form):
self.fields['language'].choices = supported_languages
language = forms.ChoiceField(label='Language', choices=[])
language = forms.ChoiceField(label='Language', choices=[], required=False)

View File

@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2018-01-29 10:21
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
from django.contrib.auth.models import User
from plinth.models import UserProfile
def insert_users(apps, schema_editor):
for user in User.objects.all():
UserProfile(user=user).save()
def truncate_user_profile(apps, schema_editor):
UserProfile.objects.all().delete()
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('plinth', '0003_merge_firstboot_completed_fields'),
]
operations = [
migrations.CreateModel(
name='UserProfile',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('preferred_language', models.CharField(default=None, max_length=10, null=True)),
(
'user',
models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.RunPython(code=insert_users, reverse_code=truncate_user_profile),
]

View File

@ -20,6 +20,7 @@ Django models for the main application
"""
from django.db import models
from django.contrib.auth.models import User
import json
@ -43,3 +44,9 @@ class Module(models.Model):
"""Model to store current setup versions of a module."""
name = models.TextField(primary_key=True)
setup_version = models.IntegerField()
class UserProfile(models.Model):
"""Model that stores User details that are not related to authentication"""
user = models.OneToOneField(User, on_delete=models.CASCADE)
preferred_language = models.CharField(max_length=10, null=True, default=None)

View File

@ -25,6 +25,7 @@ import urllib
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.contrib.auth.views import LoginView, LogoutView
from django.http import HttpResponseRedirect
from django.utils import translation
from axes.decorators import axes_form_invalid
from axes.utils import reset
@ -65,6 +66,7 @@ class SSOLoginView(LoginView):
def dispatch(self, request, *args, **kwargs):
response = super(SSOLoginView, self).dispatch(request, *args, **kwargs)
if request.user.is_authenticated:
request.session[translation.LANGUAGE_SESSION_KEY] = request.user.userprofile.preferred_language
return set_ticket_cookie(request.user, response)
else:
return response
@ -109,6 +111,11 @@ class SSOLogoutView(LogoutView):
def dispatch(self, request, *args, **kwargs):
response = super(SSOLogoutView, self).dispatch(request, *args,
**kwargs)
try:
del request.session[translation.LANGUAGE_SESSION_KEY]
except KeyError:
pass
response.delete_cookie(SSO_COOKIE_NAME)
return response

View File

@ -22,14 +22,17 @@ from django.contrib import auth, messages
from django.contrib.auth.forms import SetPasswordForm, UserCreationForm
from django.contrib.auth.models import Group, User
from django.core.exceptions import ValidationError
from django.utils import translation
from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy
from plinth import actions, module_loader
from plinth import forms as plinthForms
from plinth.errors import ActionError
from plinth.modules import first_boot, users
from plinth.modules.security import set_restricted_access
from plinth.utils import is_user_admin
from plinth.models import UserProfile
def get_group_choices():
@ -72,7 +75,7 @@ class ValidNewUsernameCheckMixin(object):
return True
class CreateUserForm(ValidNewUsernameCheckMixin, UserCreationForm):
class CreateUserForm(ValidNewUsernameCheckMixin, UserCreationForm, plinthForms.LanguageSelectionForm):
"""Custom user create form.
Include options to add user to groups.
@ -89,6 +92,9 @@ class CreateUserForm(ValidNewUsernameCheckMixin, UserCreationForm):
'log in to the system through SSH and have '
'administrative privileges (sudo).'))
class Meta(UserCreationForm.Meta):
fields = ('username', 'password1', 'password2', 'groups', 'language')
def __init__(self, request, *args, **kwargs):
"""Initialize the form with extra request argument."""
self.request = request
@ -98,6 +104,7 @@ class CreateUserForm(ValidNewUsernameCheckMixin, UserCreationForm):
def save(self, commit=True):
"""Save the user model and create LDAP user if required."""
user = super(CreateUserForm, self).save(commit)
UserProfile(user=user, preferred_language=self.cleaned_data['language']).save()
if commit:
try:
@ -125,7 +132,7 @@ class CreateUserForm(ValidNewUsernameCheckMixin, UserCreationForm):
return user
class UserUpdateForm(ValidNewUsernameCheckMixin, forms.ModelForm):
class UserUpdateForm(ValidNewUsernameCheckMixin, forms.ModelForm, plinthForms.LanguageSelectionForm):
"""When user info is changed, also updates LDAP user."""
ssh_keys = forms.CharField(
label=ugettext_lazy('SSH Keys'),
@ -138,7 +145,7 @@ class UserUpdateForm(ValidNewUsernameCheckMixin, forms.ModelForm):
class Meta:
"""Metadata to control automatic form building."""
fields = ('username', 'groups', 'ssh_keys', 'is_active')
fields = ('username', 'groups', 'ssh_keys', 'language', 'is_active')
model = User
widgets = {
'groups': forms.widgets.CheckboxSelectMultiple(),
@ -172,7 +179,14 @@ class UserUpdateForm(ValidNewUsernameCheckMixin, forms.ModelForm):
def save(self, commit=True):
"""Update LDAP user name and groups after saving user model."""
user = super(UserUpdateForm, self).save(commit)
user = super(UserUpdateForm, self).save(commit=False)
user.userprofile.preferred_language = self.cleaned_data['language']
user.userprofile.save()
user.save()
# If user is updating their own profile then only translate the pages
if self.username == self.request.user.username:
self.request.session[translation.LANGUAGE_SESSION_KEY] = user.userprofile.preferred_language
if commit:
output = actions.superuser_run('users',
@ -259,6 +273,7 @@ class FirstBootForm(ValidNewUsernameCheckMixin, auth.forms.UserCreationForm):
def save(self, commit=True):
"""Create and log the user in."""
user = super().save(commit=commit)
UserProfile(user=user).save()
if commit:
first_boot.mark_step_done('users_firstboot')

View File

@ -104,6 +104,8 @@ class UserUpdate(ContextMixin, SuccessMessageMixin, UpdateView):
ssh_keys = actions.superuser_run(
'ssh', ['get-keys', '--username', self.object.username])
initial['ssh_keys'] = ssh_keys.strip()
user_being_edited = User.objects.get(username=self.kwargs['slug'])
initial['language'] = user_being_edited.userprofile.preferred_language
except ActionError:
pass

View File

@ -82,9 +82,12 @@ class LanguageSelectionView(FormView):
form = self.form_class(request.POST)
if form.is_valid():
selected_language = form.cleaned_data['language']
if not selected_language:
response = HttpResponseRedirect(reverse('language-selection'))
response.delete_cookie(settings.LANGUAGE_COOKIE_NAME)
return response
translation.activate(selected_language)
# set selected language in session
request.session[translation.LANGUAGE_SESSION_KEY] = selected_language
response = HttpResponseRedirect(reverse('language-selection'))
# send a cookie for selected language
response.set_cookie(settings.LANGUAGE_COOKIE_NAME, selected_language)