diff --git a/cms/urls.py b/cms/urls.py index 5631c3aa957b..440c8bcebb50 100644 --- a/cms/urls.py +++ b/cms/urls.py @@ -76,8 +76,8 @@ path('api/user/', include('openedx.core.djangoapps.user_api.urls')), # Update session view - path('lang_pref/session_language', openedx.core.djangoapps.lang_pref.views.update_session_language, - name='session_language' + path('lang_pref/update_language', openedx.core.djangoapps.lang_pref.views.update_language, + name='update_language' ), # Darklang View to change the preview language (or dark language) diff --git a/lms/urls.py b/lms/urls.py index 8b8462034d86..dc673054df9b 100644 --- a/lms/urls.py +++ b/lms/urls.py @@ -164,7 +164,7 @@ namespace='catalog')), # Update session view - path('lang_pref/session_language', lang_pref_views.update_session_language, name='session_language'), + path('lang_pref/update_language', lang_pref_views.update_language, name='update_language'), # Multiple course modes and identity verification path( diff --git a/openedx/core/djangoapps/dark_lang/middleware.py b/openedx/core/djangoapps/dark_lang/middleware.py index d2764a36a07f..817d544e9ff4 100644 --- a/openedx/core/djangoapps/dark_lang/middleware.py +++ b/openedx/core/djangoapps/dark_lang/middleware.py @@ -10,7 +10,6 @@ from django.conf import settings -from django.utils.translation import LANGUAGE_SESSION_KEY from django.utils.translation.trans_real import parse_accept_lang_header from django.utils.deprecation import MiddlewareMixin @@ -155,6 +154,5 @@ def _activate_preview_language(self, request, response): if not preview_lang: return - # Set the session key to the requested preview lang - request.session[LANGUAGE_SESSION_KEY] = preview_lang + # Set the response language_cookie to the requested preview lang set_language_cookie(request, response, preview_lang) diff --git a/openedx/core/djangoapps/dark_lang/tests.py b/openedx/core/djangoapps/dark_lang/tests.py index 836376ee7dbd..b58367767159 100644 --- a/openedx/core/djangoapps/dark_lang/tests.py +++ b/openedx/core/djangoapps/dark_lang/tests.py @@ -10,7 +10,6 @@ from django.conf import settings from django.http import HttpRequest from django.test.client import Client -from django.utils.translation import LANGUAGE_SESSION_KEY from openedx.core.djangoapps.dark_lang.middleware import DarkLangMiddleware from openedx.core.djangoapps.dark_lang.models import DarkLangConfig @@ -50,23 +49,22 @@ def setUp(self): enabled=True ).save() - def process_middleware_request(self, language_session_key=UNSET, accept=UNSET): + def process_middleware_request(self, cookie_language=UNSET, accept=UNSET): """ Build a request and then process it using the ``DarkLangMiddleware``. Args: - language_session_key (str): The language code to set in request.session[LANUGAGE_SESSION_KEY] + cookie_language (str): The language code to set in self.client.cookies[LANGUAGE_COOKIE_NAME] accept (str): The accept header to set in request.META['HTTP_ACCEPT_LANGUAGE'] """ - session = {} - set_if_set(session, LANGUAGE_SESSION_KEY, language_session_key) - meta = {} set_if_set(meta, 'HTTP_ACCEPT_LANGUAGE', accept) + # Setting language cookie + set_if_set(self.client.cookies, settings.LANGUAGE_COOKIE_NAME, cookie_language) request = Mock( spec=HttpRequest, - session=session, + session={}, META=meta, GET={}, method='GET', @@ -235,11 +233,19 @@ def test_partial_match_es_la(self, latin_america_code): self.process_middleware_request(accept=b'{};q=1.0, pt;q=0.5'.format(latin_america_code)) # pylint:disable=no-member ) - def assert_session_lang_equals(self, value, session): + def _set_client_cookie_language(self, cookie_language): + """ + Set the cookie language in the Client + """ + self.client.cookies[settings.LANGUAGE_COOKIE_NAME] = cookie_language + + def assert_cookie_lang_equals(self, value): """ - Assert that the LANGUAGE_SESSION_KEY set in session is equal to value + Assert that the language set in cookies is equal to value """ - assert value == session.get(LANGUAGE_SESSION_KEY, UNSET) + lang_cookie = self.client.cookies.get(settings.LANGUAGE_COOKIE_NAME) + lang_cookie_value = lang_cookie.value if lang_cookie is not None and lang_cookie.value != '' else UNSET + assert value == lang_cookie_value def _post_set_preview_lang(self, preview_language): """ @@ -253,77 +259,49 @@ def _post_clear_preview_lang(self): """ return self.client.post('/update_lang/', {'action': 'reset_preview_language'}) - def _set_client_session_language(self, session_language): - """ - Set the session language in the Client - """ - session = self.client.session - session[LANGUAGE_SESSION_KEY] = session_language - session.save() - def test_preview_lang_with_released_language(self): # Preview lang should always override selection self._post_set_preview_lang('rel') # Refresh the page with a get request to confirm the preview language was set self.client.get('/home') - self.assert_session_lang_equals( - 'rel', - self.client.session - ) + self.assert_cookie_lang_equals('rel') # Set the session language and ensure that the preview language overrides - self._set_client_session_language('notrel') + self._set_client_cookie_language('notrel') self._post_set_preview_lang('rel') self.client.get('/home') - self.assert_session_lang_equals( - 'rel', - self.client.session - ) + self.assert_cookie_lang_equals('rel') def test_preview_lang_with_dark_language(self): self._post_set_preview_lang('unrel') self.client.get('/home') - self.assert_session_lang_equals( - 'unrel', - self.client.session - ) + self.assert_cookie_lang_equals('unrel') # Test a clear and then a set of the preview language self._post_clear_preview_lang() self._post_set_preview_lang('unrel') self.client.get('/home') - self.assert_session_lang_equals( - 'unrel', - self.client.session - ) + self.assert_cookie_lang_equals('unrel') def test_empty_preview_language(self): # When posting an empty preview_language the currently set language should not change - self._set_client_session_language('rel') + self._set_client_cookie_language('rel') self._post_set_preview_lang(' ') self.client.get('/home') - self.assert_session_lang_equals( - 'rel', - self.client.session - ) + self.assert_cookie_lang_equals('rel') def test_clear_lang(self): # Clear a language when no language was set self._post_clear_preview_lang() self.client.get('/home') - self.assert_session_lang_equals( - UNSET, - self.client.session - ) + self.assert_cookie_lang_equals(UNSET) # Set a language and clear it to ensure the clear is working as expected self._post_set_preview_lang('notclear') + self.assert_cookie_lang_equals('notclear') self._post_clear_preview_lang() self.client.get('/home') - self.assert_session_lang_equals( - UNSET, - self.client.session - ) + self.assert_cookie_lang_equals(UNSET) def test_disabled(self): DarkLangConfig(enabled=False, changed_by=self.user).save() @@ -334,22 +312,16 @@ def test_disabled(self): ) # With DarkLang disabled the clear should not change the session language - self._set_client_session_language('rel') + self._set_client_cookie_language('rel') self._post_clear_preview_lang() self.client.get('/home') - self.assert_session_lang_equals( - 'rel', - self.client.session - ) + self.assert_cookie_lang_equals('rel') # Test that setting the preview language with DarkLang disabled does nothing - self._set_client_session_language('unrel') + self._set_client_cookie_language('unrel') self._post_set_preview_lang('rel') self.client.get('/home') - self.assert_session_lang_equals( - 'unrel', - self.client.session - ) + self.assert_cookie_lang_equals('unrel') def test_accept_chinese_language_codes(self): DarkLangConfig( diff --git a/openedx/core/djangoapps/dark_lang/views.py b/openedx/core/djangoapps/dark_lang/views.py index 0865406408e3..4d0ce352b3ba 100644 --- a/openedx/core/djangoapps/dark_lang/views.py +++ b/openedx/core/djangoapps/dark_lang/views.py @@ -8,7 +8,6 @@ from django.shortcuts import redirect from django.template.loader import render_to_string from django.utils.decorators import method_decorator -from django.utils.translation import LANGUAGE_SESSION_KEY from django.utils.translation import gettext as _ from web_fragments.fragment import Fragment @@ -104,8 +103,6 @@ def _clear_preview_language(self, request): Clears the preview language for the current user. """ delete_user_preference(request.user, DARK_LANGUAGE_KEY) - if LANGUAGE_SESSION_KEY in request.session: - del request.session[LANGUAGE_SESSION_KEY] PageLevelMessages.register_success_message( request, _('Language reset to the default') diff --git a/openedx/core/djangoapps/lang_pref/middleware.py b/openedx/core/djangoapps/lang_pref/middleware.py index e36685fd0a61..90b0535ec081 100644 --- a/openedx/core/djangoapps/lang_pref/middleware.py +++ b/openedx/core/djangoapps/lang_pref/middleware.py @@ -3,7 +3,6 @@ """ from django.conf import settings from django.utils.deprecation import MiddlewareMixin -from django.utils.translation import LANGUAGE_SESSION_KEY from django.utils.translation.trans_real import parse_accept_lang_header from openedx.core.djangoapps.dark_lang import DARK_LANGUAGE_KEY @@ -53,10 +52,6 @@ def process_request(self, request): accept_header = cookie_lang request.META[LANGUAGE_HEADER] = accept_header - # Allow the new cookie setting to update the language in the user's session - if LANGUAGE_SESSION_KEY in request.session and request.session[LANGUAGE_SESSION_KEY] != cookie_lang: - del request.session[LANGUAGE_SESSION_KEY] - # Apply language specified in SiteConfiguration, ignoring user preferences. if language := get_value('LANGUAGE_CODE'): request.COOKIES[settings.LANGUAGE_COOKIE_NAME] = language diff --git a/openedx/core/djangoapps/lang_pref/tests/test_middleware.py b/openedx/core/djangoapps/lang_pref/tests/test_middleware.py index 6ab218b654ad..d0c047eeae5f 100644 --- a/openedx/core/djangoapps/lang_pref/tests/test_middleware.py +++ b/openedx/core/djangoapps/lang_pref/tests/test_middleware.py @@ -12,7 +12,6 @@ from django.http import HttpResponse from django.test.client import Client, RequestFactory from django.urls import reverse -from django.utils.translation import LANGUAGE_SESSION_KEY from django.utils.translation.trans_real import parse_accept_lang_header from openedx.core.djangoapps.lang_pref import COOKIE_DURATION, LANGUAGE_KEY @@ -88,8 +87,6 @@ def test_preference_setting_changes_cookie(self, lang_pref_out): domain=settings.SESSION_COOKIE_DOMAIN, ) - assert LANGUAGE_SESSION_KEY not in self.request.session - @ddt.data(*itertools.product( (None, 'eo', 'es'), # LANGUAGE_COOKIE_NAME (None, 'es', 'en'), # Language Preference In @@ -115,32 +112,26 @@ def test_preference_cookie_changes_setting(self, lang_cookie, lang_pref_in, mock (logged_in, ) + test_def for logged_in in (True, False) for test_def in [ - # (LANGUAGE_COOKIE_NAME, LANGUAGE_SESSION_KEY, Accept-Language In, - # Accept-Language Out, Session Lang Out) - (None, None, None, None, None), - (None, 'eo', None, None, 'eo'), - (None, 'en', None, None, 'en'), - (None, 'eo', 'en', 'en', 'eo'), - (None, None, 'en', 'en', None), - ('en', None, None, 'en', None), - ('en', 'en', None, 'en', 'en'), - ('en', None, 'eo', 'en;q=1.0,eo', None), - ('en', None, 'en', 'en', None), - ('en', 'eo', 'en', 'en', None), - ('en', 'eo', 'eo', 'en;q=1.0,eo', None) + # (LANGUAGE_COOKIE_NAME, Accept-Language In, Accept-Language Out) + (None, None, None), + (None, None, None), + (None, None, None), + (None, 'en', 'en'), + (None, 'en', 'en'), + ('en', None, 'en'), + ('en', None, 'en'), + ('en', 'eo', 'en;q=1.0,eo'), + ('en', 'en', 'en'), + ('en', 'en', 'en'), + ('en', 'eo', 'en;q=1.0,eo') ] )) @ddt.unpack - def test_preference_cookie_overrides_browser( - self, logged_in, lang_cookie, lang_session_in, accept_lang_in, accept_lang_out, - lang_session_out, - ): + def test_preference_cookie_overrides_browser(self, logged_in, lang_cookie, accept_lang_in, accept_lang_out): if not logged_in: self.request.user = self.anonymous_user if lang_cookie: self.request.COOKIES[settings.LANGUAGE_COOKIE_NAME] = lang_cookie - if lang_session_in: - self.request.session[LANGUAGE_SESSION_KEY] = lang_session_in if accept_lang_in: self.request.META['HTTP_ACCEPT_LANGUAGE'] = accept_lang_in else: @@ -160,8 +151,6 @@ def test_preference_cookie_overrides_browser( else: assert accept_lang_result == accept_lang_out - assert self.request.session.get(LANGUAGE_SESSION_KEY) == lang_session_out - @ddt.data(None, 'es', 'en') def test_logout_preserves_cookie(self, lang_cookie): if lang_cookie: @@ -293,10 +282,6 @@ def test_site_language_ignores_user_preferences(self, user_preference): assert get_user_preference(self.user, LANGUAGE_KEY) == user_preference assert response['Content-Language'] == 'eo' - # `LocaleMiddleware` no longer looks for language in the session since Django 3.2. It checks the cookie instead. - # See: https://docs.djangoproject.com/en/3.2/releases/3.0/#miscellaneous - assert self.client.session.get(LANGUAGE_SESSION_KEY) is None - # Clean up by making a request to a Site without specific configuration. with with_site_configuration_context(): self.client.get('/') diff --git a/openedx/core/djangoapps/lang_pref/tests/test_views.py b/openedx/core/djangoapps/lang_pref/tests/test_views.py index 7f3005a0936c..7eefc747a585 100644 --- a/openedx/core/djangoapps/lang_pref/tests/test_views.py +++ b/openedx/core/djangoapps/lang_pref/tests/test_views.py @@ -5,12 +5,9 @@ import json -from django.contrib.sessions.middleware import SessionMiddleware from django.test import TestCase -from django.test.client import RequestFactory from django.urls import reverse -from django.utils.translation import LANGUAGE_SESSION_KEY, get_language -from common.djangoapps.student.tests.factories import UserFactory +from django.utils.translation import get_language class TestLangPrefView(TestCase): @@ -18,23 +15,14 @@ class TestLangPrefView(TestCase): Language preference view tests. """ - def setUp(self): - super().setUp() - self.session_middleware = SessionMiddleware() - self.user = UserFactory.create() - self.request = RequestFactory().get('/somewhere') - self.request.user = self.user - self.session_middleware.process_request(self.request) - - def test_language_session_update(self): - # test language session updating correctly. - self.request.session[LANGUAGE_SESSION_KEY] = 'ar' - response = self.client.patch(reverse("session_language"), json.dumps({'pref-lang': 'eo'})) + def test_language_update(self): + # test language updating correctly. + response = self.client.patch(reverse("update_language"), json.dumps({'pref-lang': 'eo'})) assert response.status_code == 200 self.client.get('/') assert get_language() == 'eo' - response = self.client.patch(reverse("session_language"), json.dumps({'pref-lang': 'en'})) + response = self.client.patch(reverse("update_language"), json.dumps({'pref-lang': 'en'})) assert response.status_code == 200 self.client.get('/') assert get_language() == 'en' diff --git a/openedx/core/djangoapps/lang_pref/views.py b/openedx/core/djangoapps/lang_pref/views.py index b98b06a33f09..6fafc208cbc7 100644 --- a/openedx/core/djangoapps/lang_pref/views.py +++ b/openedx/core/djangoapps/lang_pref/views.py @@ -7,23 +7,21 @@ from django.conf import settings from django.http import HttpResponse -from django.utils.translation import LANGUAGE_SESSION_KEY from django.views.decorators.csrf import ensure_csrf_cookie from openedx.core.djangoapps.lang_pref import LANGUAGE_KEY -from openedx.core.djangoapps.lang_pref.helpers import set_language_cookie +from openedx.core.djangoapps.lang_pref.helpers import get_language_cookie, set_language_cookie @ensure_csrf_cookie -def update_session_language(request): +def update_language(request): """ - Update the language session key. + Update the language cookie. """ response = HttpResponse(200) if request.method == 'PATCH': data = json.loads(request.body.decode('utf8')) language = data.get(LANGUAGE_KEY, settings.LANGUAGE_CODE) - if request.session.get(LANGUAGE_SESSION_KEY, None) != language: - request.session[LANGUAGE_SESSION_KEY] = str(language) - set_language_cookie(request, response, language) + if get_language_cookie(request) != language: + set_language_cookie(request, response, language) return response