Skip to content

Commit

Permalink
feat: Add course-wide custom scripts
Browse files Browse the repository at this point in the history
Imlements OEP-15 by adding two fields to the course settings:
- Course-wide Custom JS
- Course-wide Custom CSS
The resources defined in these fields will be rendered in all course pages.

Rebase b6cb629..0578e1c4c6 onto b6cb629:
- Add course-wide resources to API for MFE use
- Revert "Add course-wide resources to API for MFE use" reverts commit 53648dcf0afe3cd171c9dc2eb5e56b871b2bcfb2

Signed-off-by: Gabor Boros <gabor.brs@gmail.com>
  • Loading branch information
ha-D authored and gabor-boros committed Oct 4, 2021
1 parent 9010e99 commit e482b85
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 1 deletion.
13 changes: 13 additions & 0 deletions common/lib/xmodule/xmodule/course_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,19 @@ class CourseFields: # lint-amnesty, pylint: disable=missing-class-docstring
),
scope=Scope.settings, default=False
)

course_wide_js = List(
display_name=_("Course-wide Custom JS"),
help=_('Enter Javascript resource URLs you want to be loaded globally throughout the course pages.'),
scope=Scope.settings,
)

course_wide_css = List(
display_name=_("Course-wide Custom CSS"),
help=_('Enter CSS resource URLs you want to be loaded globally throughout the course pages.'),
scope=Scope.settings,
)

other_course_settings = Dict(
display_name=_("Other Course Settings"),
help=_(
Expand Down
63 changes: 63 additions & 0 deletions lms/djangoapps/courseware/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import html
import itertools
import json
import re
from datetime import datetime, timedelta
from uuid import uuid4

Expand Down Expand Up @@ -65,6 +66,7 @@
from lms.djangoapps.courseware.user_state_client import DjangoXBlockUserStateClient
from lms.djangoapps.grades.config.waffle import ASSUME_ZERO_GRADE_IF_ABSENT
from lms.djangoapps.grades.config.waffle import waffle_switch as grades_waffle_switch
from lms.djangoapps.instructor.access import allow_access
from lms.djangoapps.verify_student.models import VerificationDeadline
from lms.djangoapps.verify_student.services import IDVerificationService
from openedx.core.djangoapps.catalog.tests.factories import CourseFactory as CatalogCourseFactory
Expand Down Expand Up @@ -3743,3 +3745,64 @@ def test_mathjax_detection_disabled(self):
response = self.client.get(url)
assert response.status_code == 200
assert b"MathJax.Hub.Config" in response.content


@ddt.ddt
class TestCourseWideResources(ModuleStoreTestCase):
"""
Tests that custom course-wide resources are rendered in course pages
"""

@ddt.data(
('courseware', 'course_id', False, True),
('dates', 'course_id', False, False),
('progress', 'course_id', False, False),
('instructor_dashboard', 'course_id', True, False),
('forum_form_discussion', 'course_id', False, False),
('render_xblock', 'usage_key_string', False, True),
)
@ddt.unpack
def test_course_wide_resources(self, url_name, param, is_instructor, is_rendered):
"""
Tests that the <script> and <link> tags are created for course-wide custom resources.
Also, test that the order which the resources are added match the given order.
"""
user = UserFactory()

js = ['/test.js', 'https://testcdn.com/js/lib.min.js', '//testcdn.com/js/lib2.js']
css = ['https://testcdn.com/css/lib.min.css', '//testcdn.com/css/lib2.css', '/test.css']

course = CourseFactory.create(course_wide_js=js, course_wide_css=css)
chapter = ItemFactory.create(parent=course, category='chapter')
sequence = ItemFactory.create(parent=chapter, category='sequential', display_name='Sequence')

CourseOverview.load_from_module_store(course.id)
CourseEnrollmentFactory(user=user, course_id=course.id)
if is_instructor:
allow_access(course, user, 'instructor')
assert self.client.login(username=user.username, password='test')

kwargs = None
if param == 'course_id':
kwargs = {'course_id': str(course.id)}
elif param == 'usage_key_string':
kwargs = {'usage_key_string': str(sequence.location)}
response = self.client.get(reverse(url_name, kwargs=kwargs))

content = response.content.decode('utf-8')
js_match = [re.search(f'<script .*src=[\'"]{j}[\'"].*>', content) for j in js]
css_match = [re.search(f'<link .*href=[\'"]{c}[\'"].*>', content) for c in css]

# custom resources are included
if is_rendered:
assert None not in js_match
assert None not in css_match

# custom resources are added in order
for i in range(len(js) - 1):
assert js_match[i].start() < js_match[i + 1].start()
for i in range(len(css) - 1):
assert css_match[i].start() < css_match[i + 1].start()
else:
assert js_match == [None, None, None]
assert css_match == [None, None, None]
3 changes: 2 additions & 1 deletion lms/djangoapps/courseware/views/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,8 @@ def _create_courseware_context(self, request):
'section_title': None,
'sequence_title': None,
'disable_accordion': not DISABLE_COURSE_OUTLINE_PAGE_FLAG.is_enabled(self.course.id),
'show_search': show_search
'show_search': show_search,
'render_course_wide_assets': True,
}
courseware_context.update(
get_experiment_user_metadata_context(
Expand Down
1 change: 1 addition & 0 deletions lms/djangoapps/courseware/views/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1806,6 +1806,7 @@ def render_xblock(request, usage_key_string, check_if_enrolled=True):
'is_learning_mfe': is_learning_mfe,
'is_mobile_app': is_request_from_mobile_app(request),
'reset_deadlines_url': reverse(RESET_COURSE_DEADLINES_NAME),
'render_course_wide_assets': True,

**optimization_flags,
}
Expand Down
11 changes: 11 additions & 0 deletions lms/templates/main.html
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,11 @@
</script>
% endif

% if course and course is not UNDEFINED and render_course_wide_assets:
% for css in course.course_wide_css:
<link rel="stylesheet" href="${css}" type="text/css">
% endfor
% endif
</head>

<body class="${static.dir_rtl()} <%block name='bodyclass'/> lang_${LANGUAGE_CODE}">
Expand Down Expand Up @@ -215,6 +220,12 @@
<%static:optional_include_mako file="body-extra.html" is_theming_enabled="True" />
<script type="text/javascript" src="${static.url('js/src/jquery_extend_patch.js')}"></script>
<div id="lean_overlay"></div>

% if course and course is not UNDEFINED and render_course_wide_assets:
% for js in course.course_wide_js:
<script type="text/javascript" src="${js}"></script>
% endfor
% endif
</body>
</html>

Expand Down
12 changes: 12 additions & 0 deletions lms/templates/main_django.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@
{% block headextra %}{% endblock %}
{% render_block "css" %}

{% if request.course and request.render_course_wide_assets %}
{% for css in request.course.course_wide_css %}
<link rel="stylesheet" href="{{css}}" type="text/css">
{% endfor %}
{% endif %}

{% optional_include "head-extra.html"|microsite_template_path %}

<meta name="path_prefix" content="{{EDX_ROOT_URL}}">
Expand All @@ -46,6 +52,12 @@
{% javascript 'base_application' %}

{% render_block "js" %}

{% if request.course and request.render_course_wide_assets %}
{% for js in request.course.course_wide_js %}
<script type="text/javascript" src="{{js}}"></script>
{% endfor %}
{% endif %}
</body>
</html>

Expand Down

0 comments on commit e482b85

Please sign in to comment.