diff --git a/common/djangoapps/user_api/middleware.py b/common/djangoapps/user_api/middleware.py index 77c885e125..97abe476cd 100644 --- a/common/djangoapps/user_api/middleware.py +++ b/common/djangoapps/user_api/middleware.py @@ -2,8 +2,6 @@ Middleware for UserPreferences """ -from django.utils.translation.trans_real import parse_accept_lang_header - from user_api.models import UserPreference, LANGUAGE_KEY @@ -17,11 +15,10 @@ class UserPreferenceMiddleware(object): def process_request(self, request): """ - If a user's UserPreference contains a language preference, - stick that preference in the session. + If a user's UserPreference contains a language preference and there is + no language set on the session, use the user's preference. """ - - query = UserPreference.objects.filter(user=request.user, key=LANGUAGE_KEY) - if query.exists(): - # there should only be one result for query - request.session['django_language'] = query[0].value + if 'django_language' not in request.session and request.user.is_authenticated(): + user_pref = UserPreference.get_preference(request.user, LANGUAGE_KEY) + if user_pref: + request.session['django_language'] = user_pref diff --git a/common/djangoapps/user_api/models.py b/common/djangoapps/user_api/models.py index 3450c03aa3..91e0a77209 100644 --- a/common/djangoapps/user_api/models.py +++ b/common/djangoapps/user_api/models.py @@ -1,6 +1,8 @@ from django.contrib.auth.models import User from django.db import models +LANGUAGE_KEY = 'pref-lang' + class UserPreference(models.Model): """A user's preference, stored as generic text to be processed by client""" @@ -10,3 +12,26 @@ class UserPreference(models.Model): class Meta: unique_together = ("user", "key") + + @classmethod + def set_preference(cls, user, preference_key, preference_value): + """ + Sets the user preference for a given key + """ + user_pref, _ = cls.objects.get_or_create(user=user, key=preference_key) + user_pref.value = preference_value + user_pref.save() + + @classmethod + def get_preference(cls, user, preference_key): + """ + Gets the user preference value for a given key + + Returns None if there isn't a preference for the given key + """ + + try: + user_pref = cls.objects.get(user=user, key=preference_key) + return user_pref.value + except cls.DoesNotExist: + return None diff --git a/common/djangoapps/user_api/tests/test_middleware.py b/common/djangoapps/user_api/tests/test_middleware.py new file mode 100644 index 0000000000..36f59363b1 --- /dev/null +++ b/common/djangoapps/user_api/tests/test_middleware.py @@ -0,0 +1,42 @@ +from django.test import TestCase +from django.test.client import RequestFactory +from django.contrib.sessions.middleware import SessionMiddleware + +from user_api.middleware import UserPreferenceMiddleware +from user_api.models import UserPreference, LANGUAGE_KEY +from student.tests.factories import UserFactory + + +class TestUserPreferenceMiddleware(TestCase): + """ + Tests to make sure user preferences are getting properly set in the middleware + """ + + def setUp(self): + self.middleware = UserPreferenceMiddleware() + self.request_factory = RequestFactory() + self.session_middleware = SessionMiddleware() + self.user = UserFactory.create() + self.request = self.request_factory.get('/somewhere') + self.request.user = self.user + self.session_middleware.process_request(self.request) + + def test_no_language_set_in_session_or_prefs(self): + # nothing set in the session or the prefs + self.middleware.process_request(self.request) + self.assertNotIn('django_language', self.request.session) + + def test_language_in_user_prefs(self): + # language set in the user preferences and not the session + UserPreference.set_preference(self.user, LANGUAGE_KEY, 'eo') + self.middleware.process_request(self.request) + self.assertEquals(self.request.session['django_language'], 'eo') + + def test_language_in_session(self): + # language set in both the user preferences and session, + # session should get precedence + self.request.session['django_language'] = 'en' + UserPreference.set_preference(self.user, LANGUAGE_KEY, 'eo') + self.middleware.process_request(self.request) + + self.assertEquals(self.request.session['django_language'], 'en') diff --git a/common/djangoapps/user_api/tests/test_models.py b/common/djangoapps/user_api/tests/test_models.py index db1af152b7..6e3e8c21a4 100644 --- a/common/djangoapps/user_api/tests/test_models.py +++ b/common/djangoapps/user_api/tests/test_models.py @@ -2,6 +2,7 @@ from django.db import IntegrityError from django.test import TestCase from student.tests.factories import UserFactory from user_api.tests.factories import UserPreferenceFactory +from user_api.models import UserPreference class UserPreferenceModelTest(TestCase): @@ -26,3 +27,18 @@ class UserPreferenceModelTest(TestCase): key="testkey3", value="\xe8\xbf\x99\xe6\x98\xaf\xe4\xb8\xad\xe5\x9b\xbd\xe6\x96\x87\xe5\xad\x97'" ) + + def test_get_set_preference(self): + user = UserFactory.create() + key = 'testkey' + value = 'testvalue' + + # does a round trip + UserPreference.set_preference(user, key, value) + pref = UserPreference.get_preference(user, key) + + self.assertEqual(pref, value) + + # get preference for key that doesn't exist for user + pref = UserPreference.get_preference(user, 'testkey_none') + self.assertIsNone(pref) diff --git a/common/djangoapps/user_api/tests/test_views.py b/common/djangoapps/user_api/tests/test_views.py index 451b167050..fbbe86a0d0 100644 --- a/common/djangoapps/user_api/tests/test_views.py +++ b/common/djangoapps/user_api/tests/test_views.py @@ -1,13 +1,13 @@ import base64 -from django.contrib.auth.models import User from django.test import TestCase from django.test.utils import override_settings +from django.core.urlresolvers import reverse import json import re from student.tests.factories import UserFactory from unittest import SkipTest -from user_api.models import UserPreference +from user_api.models import UserPreference, LANGUAGE_KEY from user_api.tests.factories import UserPreferenceFactory @@ -17,21 +17,9 @@ USER_PREFERENCE_LIST_URI = "/user_api/v1/user_prefs/" @override_settings(EDX_API_KEY=TEST_API_KEY) -class UserApiTestCase(TestCase): - def setUp(self): - super(UserApiTestCase, self).setUp() - self.users = [ - UserFactory.create( - email="test{0}@test.org".format(i), - profile__name="Test {0}".format(i) - ) - for i in range(5) - ] - self.prefs = [ - UserPreferenceFactory.create(user=self.users[0], key="key0"), - UserPreferenceFactory.create(user=self.users[0], key="key1"), - UserPreferenceFactory.create(user=self.users[1], key="key0") - ] +class ApiTestCase(TestCase): + + LIST_URI = USER_LIST_URI def basic_auth(self, username, password): return {'HTTP_AUTHORIZATION': 'Basic ' + base64.b64encode('%s:%s' % (username, password))} @@ -100,6 +88,32 @@ class UserApiTestCase(TestCase): self.assertEqual(response.status_code, 405) +class NoUserApiTestCase(ApiTestCase): + def test_get_list_empty(self): + result = self.get_json(self.LIST_URI) + self.assertEqual(result["count"], 0) + self.assertIsNone(result["next"]) + self.assertIsNone(result["previous"]) + self.assertEqual(result["results"], []) + + +class UserApiTestCase(ApiTestCase): + def setUp(self): + super(UserApiTestCase, self).setUp() + self.users = [ + UserFactory.create( + email="test{0}@test.org".format(i), + profile__name="Test {0}".format(i) + ) + for i in range(5) + ] + self.prefs = [ + UserPreferenceFactory.create(user=self.users[0], key="key0"), + UserPreferenceFactory.create(user=self.users[0], key="key1"), + UserPreferenceFactory.create(user=self.users[1], key="key0") + ] + + class UserViewSetTest(UserApiTestCase): LIST_URI = USER_LIST_URI @@ -137,17 +151,10 @@ class UserViewSetTest(UserApiTestCase): def test_basic_auth(self): # ensure that having basic auth headers in the mix does not break anything self.assertHttpOK( - self.request_with_auth("get", self.LIST_URI, **self.basic_auth('someuser', 'somepass'))) + self.request_with_auth("get", self.LIST_URI, + **self.basic_auth('someuser', 'somepass'))) self.assertHttpForbidden( - self.client.get(self.LIST_URI, **self.basic_auth('someuser', 'somepass'))) - - def test_get_list_empty(self): - User.objects.all().delete() - result = self.get_json(self.LIST_URI) - self.assertEqual(result["count"], 0) - self.assertIsNone(result["next"]) - self.assertIsNone(result["previous"]) - self.assertEqual(result["results"], []) + self.client.get(self.LIST_URI, **self.basic_auth('someuser', 'somepass'))) def test_get_list_nonempty(self): result = self.get_json(self.LIST_URI) @@ -245,14 +252,6 @@ class UserPreferenceViewSetTest(UserApiTestCase): def test_debug_auth(self): self.assertHttpOK(self.client.get(self.LIST_URI)) - def test_get_list_empty(self): - UserPreference.objects.all().delete() - result = self.get_json(self.LIST_URI) - self.assertEqual(result["count"], 0) - self.assertIsNone(result["next"]) - self.assertIsNone(result["previous"]) - self.assertEqual(result["results"], []) - def test_get_list_nonempty(self): result = self.get_json(self.LIST_URI) self.assertEqual(result["count"], 3) @@ -357,3 +356,29 @@ class UserPreferenceViewSetTest(UserApiTestCase): "url": uri, } ) + + +class TestLanguageSetting(TestCase): + """ + Test setting languages + """ + def test_set_preference_happy(self): + user = UserFactory.create() + self.client.login(username=user.username, password='test') + + lang = 'en' + response = self.client.post(reverse('user_api_set_language'), {'language': lang}) + + self.assertEqual(response.status_code, 200) + user_pref = UserPreference.get_preference(user, LANGUAGE_KEY) + self.assertEqual(user_pref, lang) + + def test_set_preference_missing_lang(self): + user = UserFactory.create() + self.client.login(username=user.username, password='test') + + response = self.client.post(reverse('user_api_set_language')) + + self.assertEqual(response.status_code, 400) + + self.assertIsNone(UserPreference.get_preference(user, LANGUAGE_KEY)) diff --git a/common/djangoapps/user_api/urls.py b/common/djangoapps/user_api/urls.py index de24b67f02..9dac1e31c0 100644 --- a/common/djangoapps/user_api/urls.py +++ b/common/djangoapps/user_api/urls.py @@ -9,4 +9,5 @@ user_api_router.register(r'user_prefs', user_api_views.UserPreferenceViewSet) urlpatterns = patterns( '', url(r'^v1/', include(user_api_router.urls)), + url(r'^setlang/', 'user_api.views.set_language', name='user_api_set_language') ) diff --git a/common/djangoapps/user_api/views.py b/common/djangoapps/user_api/views.py index c64a5a4d23..8c6b5b2ec9 100644 --- a/common/djangoapps/user_api/views.py +++ b/common/djangoapps/user_api/views.py @@ -1,10 +1,12 @@ from django.conf import settings from django.contrib.auth.models import User +from django.contrib.auth.decorators import login_required +from django.http import HttpResponse, HttpResponseBadRequest from rest_framework import authentication from rest_framework import filters from rest_framework import permissions from rest_framework import viewsets -from user_api.models import UserPreference +from user_api.models import UserPreference, LANGUAGE_KEY from user_api.serializers import UserSerializer, UserPreferenceSerializer @@ -43,3 +45,18 @@ class UserPreferenceViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = UserPreferenceSerializer paginate_by = 10 paginate_by_param = "page_size" + + +@login_required +def set_language(request): + """ + This view is called when the user would like to set a language preference + """ + user = request.user + lang_pref = request.POST.get('language', None) + + if lang_pref: + UserPreference.set_preference(user, LANGUAGE_KEY, lang_pref) + return HttpResponse('{"success": true}') + + return HttpResponseBadRequest('no language provided') diff --git a/lms/envs/common.py b/lms/envs/common.py index a1a8ad89e6..71c53a33d0 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -698,6 +698,10 @@ MIDDLEWARE_CLASSES = ( # Allows us to dark-launch particular languages 'dark_lang.middleware.DarkLangMiddleware', + # Allows us to set user preferences + # should be after DarkLangMiddleware + 'user_api.middleware.UserPreferenceMiddleware', + # Detects user-requested locale from 'accept-language' header in http request 'django.middleware.locale.LocaleMiddleware', diff --git a/lms/templates/dashboard.html b/lms/templates/dashboard.html index e3b484bd24..13ab35fef9 100644 --- a/lms/templates/dashboard.html +++ b/lms/templates/dashboard.html @@ -83,15 +83,17 @@ }); }); - $("#change_language_form").submit(function(event, xhr) { - $.post('/i18n/setlang/', - {"language": language,}, - function(data) { - if (data.success){ - location.reload(); - } - }) - }); + $("#submit-lang").click(function(event, xhr) { + event.preventDefault(); + $.post('/user_api/setlang/', + {"language": $('#settings-language-value').val()}) + .done( + function(data){ + // submit form as normal + $('.settings-language-form').submit(); + } + ); + }); $("#change_email_form").submit(function(){ var new_email = $('#new_email_field').val(); diff --git a/lms/templates/dashboard/_dashboard_info_language.html b/lms/templates/dashboard/_dashboard_info_language.html index c544a372b0..9314c4c735 100644 --- a/lms/templates/dashboard/_dashboard_info_language.html +++ b/lms/templates/dashboard/_dashboard_info_language.html @@ -1,7 +1,4 @@ <%! from django.utils.translation import ugettext as _ %> -<%! - from django.core.urlresolvers import reverse -%> <%namespace name='static' file='../static_content.html'/>