Skip to content
Closed
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
3 changes: 2 additions & 1 deletion lms/djangoapps/branding/api_urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@

from django.urls import path

from lms.djangoapps.branding.views import footer
from lms.djangoapps.branding.views import footer, LMSFrontendParamsView

urlpatterns = [
path('footer', footer, name="branding_footer"),
path('frontend-params', LMSFrontendParamsView.as_view(), name="frontend_params"),
]
123 changes: 122 additions & 1 deletion lms/djangoapps/branding/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
import six
from django.conf import settings
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
from django.test import TestCase
from django.test import TestCase, override_settings
from django.urls import reverse
from edx_toggles.toggles.testutils import override_waffle_switch

from common.djangoapps.student.tests.factories import UserFactory
from lms.djangoapps.branding.models import BrandingApiConfig
Expand All @@ -18,6 +19,7 @@
from openedx.core.djangoapps.site_configuration.tests.mixins import SiteMixin
from openedx.core.djangoapps.theming.tests.test_util import with_comprehensive_theme_context
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase
from openedx.features.course_experience.waffle import ENABLE_COURSE_ABOUT_SIDEBAR_HTML


@ddt.ddt
Expand Down Expand Up @@ -286,3 +288,122 @@ def test_header_logo_links_to_marketing_site_with_site_override(self):
self.client.login(username=self.user.username, password="password")
response = self.client.get(reverse("dashboard"))
assert self.site_configuration_other.site_values['MKTG_URLS']['ROOT'] in response.content.decode('utf-8')


@ddt.ddt
class TestLMSFrontendParams(SiteMixin, TestCase):
"""Test the view that returns parameters for the LMS frontend."""

def setUp(self):
super().setUp()
self.url = reverse("frontend_params")

def test_params_include_all_expected_keys(self):
"""Ensure the params response includes all required keys."""

response = self.client.get(self.url)
assert response.status_code == 200
assert response['Content-Type'] == 'application/json'

params = json.loads(response.content.decode('utf-8'))

assert 'enable_course_sorting_by_start_date' in params
assert 'homepage_overlay_html' in params
assert 'show_partners' in params
assert 'show_homepage_promo_video' in params
assert 'homepage_course_max' in params
assert 'homepage_promo_video_youtube_id' in params
assert 'sidebar_html_enabled' in params
assert 'course_about_show_social_links' in params
assert 'course_about_twitter_account' in params
assert 'is_cosmetic_price_enabled' in params
assert 'courses_are_browsable' in params

@ddt.data(
('ENABLE_COURSE_SORTING_BY_START_DATE', False),
('homepage_overlay_html', '<p>Test overlay</p>'),
('show_partners', False),
('show_homepage_promo_video', True),
('HOMEPAGE_COURSE_MAX', 5),
('homepage_promo_video_youtube_id', 'test-youtube-id'),
('course_about_show_social_links', False),
('course_about_twitter_account', '@TestTwitterAccount'),
)
@ddt.unpack
def test_params_use_site_configuration_values(self, key, value):
"""Ensure params are correctly retrieved from site configuration."""

# Configure site with the test value
self.use_site(self.site)
self.site_configuration.site_values[key] = value
self.site_configuration.save()

response = self.client.get(self.url)
params = json.loads(response.content.decode('utf-8'))

response_key = key.lower()
assert params[response_key] == value

def test_params_use_defaults_if_no_site_configuration(self):
"""Ensure default values are returned when site configuration is missing."""

response = self.client.get(self.url)
params = json.loads(response.content.decode('utf-8'))

assert params['show_partners'] is True
assert params['show_homepage_promo_video'] is False
assert params['homepage_promo_video_youtube_id'] == 'your-youtube-id'
assert params['enable_course_sorting_by_start_date'] == settings.FEATURES.get(
'ENABLE_COURSE_SORTING_BY_START_DATE'
)
assert params['homepage_course_max'] == settings.HOMEPAGE_COURSE_MAX
assert params['course_about_show_social_links'] is True
assert params['course_about_twitter_account'] == settings.PLATFORM_TWITTER_ACCOUNT

def test_params_use_settings_if_no_site_configuration(self):
"""Ensure Django settings are used as fallback when no site configuration is provided."""

self.use_site(self.site)
self.site_configuration.site_values.clear()
self.site_configuration.save()

expected_settings = {
'HOMEPAGE_COURSE_MAX': 10,
'PLATFORM_TWITTER_ACCOUNT': '@TestTwitterAccount',
'FEATURES': settings.FEATURES | {'ENABLE_COURSE_SORTING_BY_START_DATE': False},
}

with override_settings(**expected_settings):
response = self.client.get(self.url)

params = json.loads(response.content.decode('utf-8'))

assert params['homepage_course_max'] == 10
assert params['course_about_twitter_account'] == '@TestTwitterAccount'
assert params['enable_course_sorting_by_start_date'] is False

def test_params_use_waffle_switches(self):
"""Ensure params are correctly retrieved from waffle switches."""

with override_waffle_switch(ENABLE_COURSE_ABOUT_SIDEBAR_HTML, active=True):
response = self.client.get(self.url)

params = json.loads(response.content.decode('utf-8'))

assert params['sidebar_html_enabled'] is True

def test_params_use_settings_features(self):
"""Ensure params are correctly retrieved from Django settings."""

extra_feature_flags = {
'ENABLE_COSMETIC_DISPLAY_PRICE': True,
'COURSES_ARE_BROWSABLE': False
}

with override_settings(FEATURES=settings.FEATURES | extra_feature_flags):
response = self.client.get(self.url)

params = json.loads(response.content.decode('utf-8'))

assert params['is_cosmetic_price_enabled'] is True
assert params['courses_are_browsable'] is False
75 changes: 75 additions & 0 deletions lms/djangoapps/branding/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
from django.utils.translation.trans_real import get_supported_language_variant
from django.views.decorators.cache import cache_control
from django.views.decorators.csrf import ensure_csrf_cookie
from rest_framework.response import Response
from rest_framework.views import APIView

import lms.djangoapps.branding.api as branding_api
import lms.djangoapps.courseware.views.views as courseware_views
Expand All @@ -23,6 +25,7 @@
from common.djangoapps.util.json_request import JsonResponse
from openedx.core.djangoapps.lang_pref.api import released_languages
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
from openedx.features.course_experience.waffle import ENABLE_COURSE_ABOUT_SIDEBAR_HTML

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -312,3 +315,75 @@ def footer(request):

else:
return HttpResponse(status=406)


class LMSFrontendParamsView(APIView):
"""
**Use Case**

* Get configuration parameters to be consumed by the frontend components of various LMS pages.

**Example GET Request**

GET /api/branding/v1/frontend-params

**Example GET Response**

{
"enable_course_sorting_by_start_date": true,
"homepage_overlay_html": "<div>Custom HTML Overlay</div>",
"show_partners": true,
"show_homepage_promo_video": false,
"homepage_course_max": 12,
"homepage_promo_video_youtube_id": "your-youtube-id",
"sidebar_html_enabled": true,
"course_about_show_social_links": true,
"course_about_twitter_account": "@YourTwitterAccount",
"is_cosmetic_price_enabled": true,
"courses_are_browsable": true
}
"""

def get(self, request):
"""
Returns parameters retrieved from the site configuration (with fallback defaults provided),
from Django settings (including feature flags), or from Waffle Switches.
"""

enable_course_sorting_by_start_date = configuration_helpers.get_value(
'ENABLE_COURSE_SORTING_BY_START_DATE',
settings.FEATURES['ENABLE_COURSE_SORTING_BY_START_DATE'],
)
homepage_overlay_html = configuration_helpers.get_value('homepage_overlay_html')
show_partners = configuration_helpers.get_value('show_partners', True)
show_homepage_promo_video = configuration_helpers.get_value('show_homepage_promo_video', False)
homepage_course_max = configuration_helpers.get_value(
'HOMEPAGE_COURSE_MAX', settings.HOMEPAGE_COURSE_MAX
)
homepage_promo_video_youtube_id = configuration_helpers.get_value(
'homepage_promo_video_youtube_id', 'your-youtube-id'
)
sidebar_html_enabled = ENABLE_COURSE_ABOUT_SIDEBAR_HTML.is_enabled()
course_about_show_social_links = configuration_helpers.get_value(
'course_about_show_social_links', True
)
course_about_twitter_account = configuration_helpers.get_value(
'course_about_twitter_account', settings.PLATFORM_TWITTER_ACCOUNT
)
is_cosmetic_price_enabled = settings.FEATURES.get('ENABLE_COSMETIC_DISPLAY_PRICE')
courses_are_browsable = settings.FEATURES.get('COURSES_ARE_BROWSABLE')

data = {
"enable_course_sorting_by_start_date": enable_course_sorting_by_start_date,
"homepage_overlay_html": homepage_overlay_html,
"show_partners": show_partners,
"show_homepage_promo_video": show_homepage_promo_video,
"homepage_course_max": homepage_course_max,
"homepage_promo_video_youtube_id": homepage_promo_video_youtube_id,
"sidebar_html_enabled": sidebar_html_enabled,
"course_about_show_social_links": course_about_show_social_links,
"course_about_twitter_account": course_about_twitter_account,
"is_cosmetic_price_enabled": is_cosmetic_price_enabled,
"courses_are_browsable": courses_are_browsable,
}
return Response(data)
Loading