Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"""

from django.apps import AppConfig
from edx_django_utils.plugins import PluginSettings
from edx_django_utils.plugins import PluginSettings, PluginURLs
from openedx.core.djangoapps.plugins.constants import ProjectType, SettingsType


Expand All @@ -15,9 +15,19 @@ class OLOpenedXCourseTranslationsConfig(AppConfig):
name = "ol_openedx_course_translations"

plugin_app = {
PluginURLs.CONFIG: {
ProjectType.LMS: {
PluginURLs.NAMESPACE: "",
PluginURLs.REGEX: "^api/ol-openedx-course-translations/",
PluginURLs.RELATIVE_PATH: "urls",
}
},
PluginSettings.CONFIG: {
ProjectType.CMS: {
SettingsType.COMMON: {PluginSettings.RELATIVE_PATH: "settings.common"},
},
ProjectType.LMS: {
SettingsType.COMMON: {PluginSettings.RELATIVE_PATH: "settings.common"},
},
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# python
import re

from django.conf import settings
from django.http import HttpResponseRedirect
from django.utils.deprecation import MiddlewareMixin
from opaque_keys.edx.keys import CourseKey
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.lang_pref import LANGUAGE_KEY
from openedx.core.djangoapps.user_api.preferences.api import set_user_preference


class CourseLanguageCookieMiddleware(MiddlewareMixin):
"""
Sets language preference cookie and user preference based on course language.
Also ensures that for admin URLs, the language is always set to 'en'.
"""

COURSE_URL_REGEX = re.compile(
rf"^/courses/(?P<course_key>{settings.COURSE_KEY_REGEX})(?:/|$)",
re.IGNORECASE,
)
COOKIE_NAME = "openedx-language-preference"

def process_response(self, request, response):
path = getattr(request, "path_info", request.path)

# Force 'en' language for admin, sysadmin, and instructor dashboard URLs
if (
path.startswith("/admin")
or path.startswith("/sysadmin")
or "instructor" in path
):
cookie_val = request.COOKIES.get(self.COOKIE_NAME)
needs_reload = cookie_val and cookie_val != "en"
if needs_reload:
response.set_cookie(
self.COOKIE_NAME,
"en",
max_age=60 * 60 * 24 * 180,
httponly=False,
samesite="Lax",
)
if hasattr(request, "user") and request.user.is_authenticated:
set_user_preference(request.user, LANGUAGE_KEY, "en")
return HttpResponseRedirect(request.get_full_path())
return response

match = self.COURSE_URL_REGEX.match(path)
if not match:
return response

course_key_str = match.group("course_key")
try:
course_key = CourseKey.from_string(course_key_str)
overview = CourseOverview.get_from_id(course_key)
except Exception: # noqa: BLE001
return response

language = getattr(overview, "language", None)
response.set_cookie(
self.COOKIE_NAME,
language or "",
max_age=60 * 60 * 24 * 180,
httponly=False,
samesite="Lax",
)

# Set user preference if authenticated
if language and hasattr(request, "user") and request.user.is_authenticated:
set_user_preference(request.user, LANGUAGE_KEY, language)

# Redirect if cookie is not present or is different from the desired language
cookie_val = request.COOKIES.get(self.COOKIE_NAME)
needs_reload = language and (cookie_val != language)
if needs_reload:
url = request.get_full_path()
return HttpResponseRedirect(url)

return response
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"""
URL configuration for ol_openedx_course_translations app.
"""

from django.conf import settings
from django.urls import path, re_path

from ol_openedx_course_translations.views import (
CourseLanguageView,
ResetUserLanguageView,
)

urlpatterns = [
re_path(
rf"course-language/{settings.COURSE_KEY_PATTERN}$",
CourseLanguageView.as_view(),
name="ol_openedx_course_language",
),
path(
"user/reset-language/",
ResetUserLanguageView.as_view(),
name="ol_openedx_reset_user_language",
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""
API Views for ol_openedx_course_translations App
"""

from opaque_keys.edx.keys import CourseKey
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.lang_pref import LANGUAGE_KEY
from openedx.core.djangoapps.user_api.preferences.api import set_user_preference
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView


class CourseLanguageView(APIView):
"""
API View to retrieve the language of a specified course.
"""

def get(self, request, course_key_string): # noqa: ARG002
if not course_key_string:
return Response(
{"error": "Missing course_key parameter."},
status=status.HTTP_400_BAD_REQUEST,
)
try:
course_key = CourseKey.from_string(course_key_string)
course = CourseOverview.get_from_id(course_key)
except Exception: # noqa: BLE001
return Response(
{"error": "Invalid course_key or course not found."},
status=status.HTTP_404_NOT_FOUND,
)
return Response({"language": course.language})


class ResetUserLanguageView(APIView):
"""
API endpoint to reset user's language preference and cookie to English.
"""

permission_classes = [IsAuthenticated]
COOKIE_NAME = "openedx-language-preference"

def post(self, request):
# Set user preference
set_user_preference(request.user, LANGUAGE_KEY, "en")

# Prepare response
response = Response(
{"detail": "Language reset to English."}, status=status.HTTP_200_OK
)

# Set cookie like middleware
response.set_cookie(
self.COOKIE_NAME,
"en",
max_age=60 * 60 * 24 * 180,
httponly=False,
samesite="Lax",
)

return response
5 changes: 4 additions & 1 deletion src/ol_openedx_course_translations/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "ol-openedx-course-translations"
version = "0.1.0"
version = "0.1.2"
description = "An Open edX plugin to translate courses"
authors = [
{name = "MIT Office of Digital Learning"}
Expand All @@ -18,6 +18,9 @@ dependencies = [
[project.entry-points."cms.djangoapp"]
ol_openedx_course_translations = "ol_openedx_course_translations.apps:OLOpenedXCourseTranslationsConfig"

[project.entry-points."lms.djangoapp"]
ol_openedx_course_translations = "ol_openedx_course_translations.apps:OLOpenedXCourseTranslationsConfig"

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
Expand Down