diff --git a/eox_tenant/edxapp_wrapper/backends/dark_lang_middleware_o_test_v1.py b/eox_tenant/edxapp_wrapper/backends/dark_lang_middleware_o_test_v1.py new file mode 100644 index 00000000..fa8a0be9 --- /dev/null +++ b/eox_tenant/edxapp_wrapper/backends/dark_lang_middleware_o_test_v1.py @@ -0,0 +1,9 @@ +""" +DarkLangMiddleware Backend for tests. +""" +from unittest.mock import MagicMock + + +def get_dark_lang_middleware(): + """Backend to get the DarkLangMiddleware from openedx.""" + return MagicMock() diff --git a/eox_tenant/edxapp_wrapper/backends/dark_lang_middleware_o_v1.py b/eox_tenant/edxapp_wrapper/backends/dark_lang_middleware_o_v1.py new file mode 100644 index 00000000..4504131a --- /dev/null +++ b/eox_tenant/edxapp_wrapper/backends/dark_lang_middleware_o_v1.py @@ -0,0 +1,9 @@ +""" +DarkLangMiddleware Backend. +""" +from openedx.core.djangoapps.dark_lang.middleware import DarkLangMiddleware # pylint: disable=import-error + + +def get_dark_lang_middleware(): + """Backend to get the DarkLangMiddleware from openedx.""" + return DarkLangMiddleware diff --git a/eox_tenant/edxapp_wrapper/dark_lang_middleware.py b/eox_tenant/edxapp_wrapper/dark_lang_middleware.py new file mode 100644 index 00000000..7ee55bf2 --- /dev/null +++ b/eox_tenant/edxapp_wrapper/dark_lang_middleware.py @@ -0,0 +1,11 @@ +""" Backend abstraction. """ +from importlib import import_module + +from django.conf import settings + + +def get_dark_lang_middleware(*args, **kwargs): + """ Get DarkLangMiddleware. """ + backend_function = settings.DARK_LANG_MIDDLEWARE + backend = import_module(backend_function) + return backend.get_dark_lang_middleware(*args, **kwargs) diff --git a/eox_tenant/settings/common.py b/eox_tenant/settings/common.py index a866ec6c..789911fd 100644 --- a/eox_tenant/settings/common.py +++ b/eox_tenant/settings/common.py @@ -48,6 +48,7 @@ def plugin_settings(settings): settings.EDXMAKO_MODULE_BACKEND = 'eox_tenant.edxapp_wrapper.backends.edxmako_l_v1' settings.UTILS_MODULE_BACKEND = 'eox_tenant.edxapp_wrapper.backends.util_h_v1' settings.CHANGE_DOMAIN_DEFAULT_SITE_NAME = "stage.edunext.co" + settings.DARK_LANG_MIDDLEWARE = 'eox_tenant.edxapp_wrapper.backends.dark_lang_middleware_o_v1' settings.EOX_TENANT_LOAD_PERMISSIONS = True settings.EOX_TENANT_APPEND_LMS_MIDDLEWARE_CLASSES = False settings.USE_EOX_TENANT = False diff --git a/eox_tenant/settings/test.py b/eox_tenant/settings/test.py index 61771c7b..4f13d6de 100644 --- a/eox_tenant/settings/test.py +++ b/eox_tenant/settings/test.py @@ -33,6 +33,7 @@ class SettingsClass: GET_THEMING_HELPERS = 'eox_tenant.edxapp_wrapper.backends.theming_helpers_test_v1' EOX_TENANT_USERS_BACKEND = 'eox_tenant.edxapp_wrapper.backends.users_test_v1' EOX_TENANT_BEARER_AUTHENTICATION = 'eox_tenant.edxapp_wrapper.backends.bearer_authentication_test_v1' +DARK_LANG_MIDDLEWARE = 'eox_tenant.edxapp_wrapper.backends.dark_lang_middleware_o_test_v1' COURSE_KEY_PATTERN = r'(?P[^/+]+(/|\+)[^/+]+(/|\+)[^/?]+)' COURSE_ID_PATTERN = COURSE_KEY_PATTERN.replace('course_key_string', 'course_id') diff --git a/eox_tenant/tenant_aware_functions/released_languages.py b/eox_tenant/tenant_aware_functions/released_languages.py new file mode 100644 index 00000000..f04553fe --- /dev/null +++ b/eox_tenant/tenant_aware_functions/released_languages.py @@ -0,0 +1,42 @@ +""" +Site/Tenant aware languages filter. +""" +from collections import namedtuple + +from django.conf import settings + +Language = namedtuple('Language', 'code name') + + +def tenant_languages(): + """Retrieve the list of released languages by tenant. + + Constructs a list of Language tuples by intersecting the + list of valid language tuples with the list of released + language codes. + + Returns: + list of Language: Languages in which full translations are available. + + Example: + + >>> print released_languages() + [Language(code='en', name=u'English'), Language(code='fr', name=u'Français')] + + """ + released_languages = getattr(settings, "released_languages", "") + released_language_codes = [lang.lower().strip() for lang in released_languages.split(',')] + default_language_code = settings.LANGUAGE_CODE + + if default_language_code not in released_language_codes: + released_language_codes.append(default_language_code) + + released_language_codes.sort() + + # Intersect the list of valid language tuples with the list + # of released language codes + return [ + Language(language_info[0], language_info[1]) + for language_info in settings.LANGUAGES + if language_info[0] in released_language_codes + ] diff --git a/eox_tenant/tenant_wise/__init__.py b/eox_tenant/tenant_wise/__init__.py index df545f71..ae98acb9 100644 --- a/eox_tenant/tenant_wise/__init__.py +++ b/eox_tenant/tenant_wise/__init__.py @@ -10,7 +10,8 @@ from django.conf import settings from eox_tenant.constants import LMS_ENVIRONMENT -from eox_tenant.tenant_wise.proxies import TenantSiteConfigProxy +from eox_tenant.tenant_aware_functions.released_languages import tenant_languages +from eox_tenant.tenant_wise.proxies import DarkLangMiddlewareProxy, TenantSiteConfigProxy def load_tenant_wise_overrides(): @@ -32,6 +33,18 @@ def load_tenant_wise_overrides(): proxy=TenantSiteConfigProxy ) + if settings.FEATURES.get("EDNX_SITE_AWARE_LOCALE", False): + set_as_proxy( + modules='openedx.core.djangoapps.lang_pref.api', + model='released_languages', + proxy=tenant_languages + ) + set_as_proxy( + modules='openedx.core.djangoapps.dark_lang.middleware', + model='DarkLangMiddleware', + proxy=DarkLangMiddlewareProxy + ) + def set_as_proxy(modules, model, proxy): """ diff --git a/eox_tenant/tenant_wise/proxies.py b/eox_tenant/tenant_wise/proxies.py index 9ee036ce..641d3867 100644 --- a/eox_tenant/tenant_wise/proxies.py +++ b/eox_tenant/tenant_wise/proxies.py @@ -9,11 +9,13 @@ from django.conf import settings from django.core.cache import cache +from eox_tenant.edxapp_wrapper.dark_lang_middleware import get_dark_lang_middleware from eox_tenant.edxapp_wrapper.site_configuration_module import get_site_configuration_models from eox_tenant.models import Microsite, TenantConfig, TenantOrganization from eox_tenant.utils import clean_serializable_values SiteConfigurationModels = get_site_configuration_models() +DarkLangMiddleware = get_dark_lang_middleware() TENANT_ALL_ORGS_CACHE_KEY = "tenant.all_orgs_list" EOX_TENANT_CACHE_KEY_TIMEOUT = getattr( @@ -196,3 +198,39 @@ def pre_load_values_by_org(cls, val_name): cls.set_key_to_cache(key, result) cls.set_key_to_cache(pre_load_value_key, True) + + +class DarkLangMiddlewareProxy(DarkLangMiddleware): + """This Middleware will be used if you have FEATURES["EDNX_SITE_AWARE_LOCALE"] in True. + This take the released_languages from the site aware settings, and set a HTTP_ACCEPT_LANGUAGE if + you don't have one.""" + + class Meta: + """ Set as a proxy model. """ + proxy = True + + @property + def released_langs(self): + """ + Current list of released languages from settings. + """ + + get_language_options = getattr(settings, "released_languages", "") + language_options = [lang.lower().strip() for lang in get_language_options.split(',')] + if settings.LANGUAGE_CODE not in language_options: + language_options.append(settings.LANGUAGE_CODE) + return language_options + + def process_request(self, request): + """ + This will be run when you do a request, and prevent user from requesting un-released languages. + """ + + # If the request doesn't have HTTP_ACCEPT_LANGUAGE, eduNEXT set it to + # settings.LANGUAGE_CODE that is site aware so that + # django.utils.locale.LocaleMiddleware can pick it up + accept = request.META.get('HTTP_ACCEPT_LANGUAGE', None) + if not accept: + request.META['HTTP_ACCEPT_LANGUAGE'] = f"{settings.LANGUAGE_CODE};q=0.1" + + self._clean_accept_headers(request)