fix: use mako prefix lms. when XBlock runtime renders in Studio/authoring MFE
When the XBlock runtime is used in Studio/authoring mode, it needs to use the template path prefix "lms." in order to locate the block templates. (In LMS view, no prefix is required.) Fixes bug introduced by https://github.com/openedx/edx-platform/pull/29354 and removes template workaround added by https://github.com/openedx/edx-platform/pull/29517 (see Author Notes & Concerns)
This commit is contained in:
@@ -42,7 +42,8 @@ from lms.envs.test import ( # pylint: disable=wrong-import-order
|
||||
REGISTRATION_EXTRA_FIELDS,
|
||||
GRADES_DOWNLOAD,
|
||||
SITE_NAME,
|
||||
WIKI_ENABLED
|
||||
WIKI_ENABLED,
|
||||
XBLOCK_RUNTIME_V2_EPHEMERAL_DATA_CACHE,
|
||||
)
|
||||
|
||||
|
||||
@@ -177,6 +178,12 @@ CACHES = {
|
||||
'course_structure_cache': {
|
||||
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
|
||||
},
|
||||
'blockstore': {
|
||||
'KEY_PREFIX': 'blockstore',
|
||||
'KEY_FUNCTION': 'common.djangoapps.util.memcache.safe_key',
|
||||
'LOCATION': 'edx_loc_mem_cache',
|
||||
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
||||
},
|
||||
}
|
||||
|
||||
############################### BLOCKSTORE #####################################
|
||||
@@ -276,12 +283,6 @@ TEST_ELASTICSEARCH_USE_SSL = os.environ.get(
|
||||
TEST_ELASTICSEARCH_HOST = os.environ.get('EDXAPP_TEST_ELASTICSEARCH_HOST', 'edx.devstack.elasticsearch710')
|
||||
TEST_ELASTICSEARCH_PORT = int(os.environ.get('EDXAPP_TEST_ELASTICSEARCH_PORT', '9200'))
|
||||
|
||||
############################# TEMPLATE CONFIGURATION #############################
|
||||
# Adds mako template dirs for content_libraries tests
|
||||
MAKO_TEMPLATE_DIRS_BASE.append(
|
||||
COMMON_ROOT / 'lib' / 'capa' / 'capa' / 'templates'
|
||||
)
|
||||
|
||||
########################## AUTHOR PERMISSION #######################
|
||||
FEATURES['ENABLE_CREATOR_GROUP'] = False
|
||||
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
<!--
|
||||
common/test/problem.html
|
||||
Placeholder template for openedx content_library tests -->
|
||||
<%page expression_filter="h"/>
|
||||
<%!
|
||||
from django.utils.translation import ngettext, gettext as _
|
||||
from openedx.core.djangolib.markup import HTML
|
||||
%>
|
||||
|
||||
<%namespace name='static' file='static_content.html'/>
|
||||
<h3 class="hd hd-3 problem-header" id="${ short_id }-problem-title" aria-describedby="${ id }-problem-progress" tabindex="-1">
|
||||
${ problem['name'] }
|
||||
</h3>
|
||||
|
||||
<div class="problem-progress" id="${ id }-problem-progress"></div>
|
||||
|
||||
<div class="problem">
|
||||
${ HTML(problem['html']) }
|
||||
<div class="action">
|
||||
<input type="hidden" name="problem_id" value="${ problem['name'] }" />
|
||||
|
||||
<div class="problem-action-buttons-wrapper">
|
||||
% if demand_hint_possible:
|
||||
<span class="problem-action-button-wrapper">
|
||||
<button type="button" class="hint-button problem-action-btn btn-link btn-small" data-value="${_('Hint')}" ${'' if should_enable_next_hint else 'disabled'}>${_('Hint')}</button>
|
||||
</span>
|
||||
% endif
|
||||
% if save_button:
|
||||
<span class="problem-action-button-wrapper">
|
||||
<button type="button" class="save problem-action-btn btn-link btn-small" data-value="${_('Save')}">
|
||||
<span aria-hidden="true">${_('Save')}</span>
|
||||
<span class="sr">${_("Save your answer")}</span>
|
||||
</button>
|
||||
</span>
|
||||
% endif
|
||||
% if reset_button:
|
||||
<span class="problem-action-button-wrapper">
|
||||
<button type="button" class="reset problem-action-btn btn-link btn-small" data-value="${_('Reset')}"><span aria-hidden="true">${_('Reset')}</span><span class="sr">${_("Reset your answer")}</span></button>
|
||||
</span>
|
||||
% endif
|
||||
% if answer_available:
|
||||
<span class="problem-action-button-wrapper">
|
||||
<button type="button" class="show problem-action-btn btn-link btn-small" aria-describedby="${ short_id }-problem-title"><span class="show-label">${_('Show answer')}</span></button>
|
||||
</span>
|
||||
% endif
|
||||
</div>
|
||||
<div class="submit-attempt-container">
|
||||
<button type="button" class="submit btn-brand" data-submitting="${ submit_button_submitting }" data-value="${ submit_button }" data-should-enable-submit-button="${ should_enable_submit_button }" aria-describedby="submission_feedback_${short_id}" ${'' if should_enable_submit_button else 'disabled'}>
|
||||
<span class="submit-label">${ submit_button }</span>
|
||||
</button>
|
||||
|
||||
% if submit_disabled_cta:
|
||||
% if submit_disabled_cta.get('event_data'):
|
||||
<button class="submit-cta-link-button btn-link btn-small" onclick="emit_event(${submit_disabled_cta['event_data']})">
|
||||
${submit_disabled_cta['link_name']}
|
||||
</button>
|
||||
<span class="submit-cta-description" tabindex="0" role="note" aria-label="description">
|
||||
<span data-tooltip="${submit_disabled_cta['description']}" data-tooltip-show-on-click="true"
|
||||
class="fa fa-info-circle fa-lg" aria-hidden="true">
|
||||
</span>
|
||||
</span>
|
||||
<span class="sr">(${submit_disabled_cta['description']})</span>
|
||||
% else:
|
||||
<form class="submit-cta" method="post" action="${submit_disabled_cta['link']}">
|
||||
<input type="hidden" id="csrf_token" name="csrfmiddlewaretoken" value="${csrf_token}">
|
||||
% for form_name, form_value in submit_disabled_cta['form_values'].items():
|
||||
<input type="hidden" name="${form_name}" value="${form_value}">
|
||||
% endfor
|
||||
<button class="submit-cta-link-button btn-link btn-small">
|
||||
${submit_disabled_cta['link_name']}
|
||||
</button>
|
||||
<span class="submit-cta-description" tabindex="0" role="note" aria-label="description">
|
||||
<span data-tooltip="${submit_disabled_cta['description']}" data-tooltip-show-on-click="true"
|
||||
class="fa fa-info-circle fa-lg" aria-hidden="true">
|
||||
</span>
|
||||
</span>
|
||||
<span class="sr">(${submit_disabled_cta['description']})</span>
|
||||
</form>
|
||||
% endif
|
||||
% endif
|
||||
<div class="submission-feedback ${'cta-enabled' if submit_disabled_cta else ''}" id="submission_feedback_${short_id}">
|
||||
## When attempts are not 0, the CTA above will contain a message about the number of used attempts
|
||||
% if attempts_allowed and (not submit_disabled_cta or attempts_used == 0):
|
||||
${ngettext("You have used {num_used} of {num_total} attempt", "You have used {num_used} of {num_total} attempts", attempts_allowed).format(num_used=attempts_used, num_total=attempts_allowed)}
|
||||
% endif
|
||||
<span class="sr">${_("Some problems have options such as save, reset, hints, or show answer. These options follow the Submit button.")}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function emit_event(message) {
|
||||
parent.postMessage(message, '*');
|
||||
}
|
||||
</script>
|
||||
@@ -1,16 +0,0 @@
|
||||
<!--
|
||||
common/tests/problem_ajax.html
|
||||
Placeholder template for openedx content_library tests -->
|
||||
<div id="problem_${element_id}" class="problems-wrapper" role="group"
|
||||
aria-labelledby="${element_id}-problem-title"
|
||||
data-problem-id="${id}" data-url="${ajax_url}"
|
||||
data-problem-score="${current_score}"
|
||||
data-problem-total-possible="${total_possible}"
|
||||
data-attempts-used="${attempts_used}"
|
||||
data-content="${content | h}"
|
||||
data-graded="${graded}">
|
||||
<p class="loading-spinner">
|
||||
<i class="fa fa-spinner fa-pulse fa-2x fa-fw"></i>
|
||||
<span class="sr">Loading…</span>
|
||||
</p>
|
||||
</div>
|
||||
@@ -1,122 +0,0 @@
|
||||
<!--
|
||||
common/test/video.html
|
||||
Placeholder template for openedx content_library tests -->
|
||||
<%page expression_filter="h"/>
|
||||
|
||||
<%!
|
||||
from django.utils.translation import ugettext as _
|
||||
from openedx.core.djangolib.js_utils import (
|
||||
dump_js_escaped_json, js_escaped_string
|
||||
)
|
||||
%>
|
||||
% if display_name is not UNDEFINED and display_name is not None:
|
||||
<h3 class="hd hd-2">${display_name}</h3>
|
||||
% endif
|
||||
|
||||
<div
|
||||
id="video_${id}"
|
||||
class="video closed"
|
||||
data-metadata='${metadata}'
|
||||
data-bumper-metadata='${bumper_metadata}'
|
||||
data-autoadvance-enabled="${autoadvance_enabled}"
|
||||
data-poster='${poster}'
|
||||
tabindex="-1"
|
||||
>
|
||||
<div class="focus_grabber first"></div>
|
||||
|
||||
<div class="tc-wrapper">
|
||||
<div class="video-wrapper">
|
||||
<span tabindex="0" class="spinner" aria-hidden="false" aria-label="${_('Loading video player')}"></span>
|
||||
<span tabindex="-1" class="btn-play fa fa-youtube-play fa-2x is-hidden" aria-hidden="true" aria-label="${_('Play video')}"></span>
|
||||
<div class="video-player-pre"></div>
|
||||
<div class="video-player">
|
||||
<div id="${id}"></div>
|
||||
<h4 class="hd hd-4 video-error is-hidden">${_('No playable video sources found.')}</h4>
|
||||
<h4 class="hd hd-4 video-hls-error is-hidden">
|
||||
${_('Your browser does not support this video format. Try using a different browser.')}
|
||||
</h4>
|
||||
</div>
|
||||
<div class="video-player-post"></div>
|
||||
<div class="closed-captions"></div>
|
||||
<div class="video-controls is-hidden">
|
||||
<div>
|
||||
<div class="vcr"><div class="vidtime">0:00 / 0:00</div></div>
|
||||
<div class="secondary-controls"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="focus_grabber last"></div>
|
||||
|
||||
% if download_video_link or track or handout or branding_info:
|
||||
<h3 class="hd hd-4 downloads-heading sr" id="video-download-transcripts_${id}">${_('Downloads and transcripts')}</h3>
|
||||
<div class="wrapper-downloads" role="region" aria-labelledby="video-download-transcripts_${id}">
|
||||
% if download_video_link:
|
||||
<div class="wrapper-download-video">
|
||||
<h4 class="hd hd-5">${_('Video')}</h4>
|
||||
<a class="btn-link video-sources video-download-button" href="${download_video_link}">
|
||||
${_('Download video file')}
|
||||
</a>
|
||||
</div>
|
||||
% endif
|
||||
% if track:
|
||||
<div class="wrapper-download-transcripts">
|
||||
<h4 class="hd hd-5">${_('Transcripts')}</h4>
|
||||
% if transcript_download_format:
|
||||
<ul class="list-download-transcripts">
|
||||
% for item in transcript_download_formats_list:
|
||||
<li class="transcript-option">
|
||||
<% dname = _("Download {file}").format(file=item['display_name']) %>
|
||||
<a class="btn btn-link" href="${track}" data-value="${item['value']}">${dname}</a>
|
||||
</li>
|
||||
% endfor
|
||||
</ul>
|
||||
% else:
|
||||
<a class="btn-link external-track" href="${track}">${_('Download transcript')}</a>
|
||||
% endif
|
||||
</div>
|
||||
% endif
|
||||
% if handout:
|
||||
<div class="wrapper-handouts">
|
||||
<h4 class="hd hd-5">${_('Handouts')}</h4>
|
||||
<a class="btn-link" href="${handout}">${_('Download Handout')}</a>
|
||||
</div>
|
||||
% endif
|
||||
% if branding_info:
|
||||
<div class="branding">
|
||||
<span class="host-tag">${branding_info['logo_tag']}</span>
|
||||
<a href="${branding_info['url']}"><img class="brand-logo" src="${branding_info['logo_src']}" alt="${branding_info['logo_tag']}" /></a>
|
||||
</div>
|
||||
% endif
|
||||
</div>
|
||||
% endif
|
||||
</div>
|
||||
% if cdn_eval:
|
||||
<script>
|
||||
//TODO: refactor this js into a separate file.
|
||||
function sendPerformanceBeacon(id, expgroup, value, event_name) {
|
||||
var data = {event: event_name, id: id, expgroup: expgroup, value: value, page: "html5vid"};
|
||||
$.ajax({method: "POST", url: "/performance", data: data});
|
||||
}
|
||||
var cdnStartTime;
|
||||
var salt = Math.floor((1 + Math.random()) * 0x100000).toString(36);
|
||||
var id = "${id | n, js_escaped_string}";
|
||||
function initializeCDNExperiment() {
|
||||
sendPerformanceBeacon(id + "_" + salt, ${cdn_exp_group | n, dump_js_escaped_json}, "", "load");
|
||||
cdnStartTime = Date.now();
|
||||
$.each(['loadstart', 'abort', 'error', 'stalled', 'loadedmetadata',
|
||||
'loadeddata', 'canplay', 'canplaythrough', 'seeked'],
|
||||
function(index, eventName) {
|
||||
$("#video_" + id).bind("html5:" + eventName, null, function() {
|
||||
timeElapsed = Date.now() - cdnStartTime;
|
||||
sendPerformanceBeacon(id + "_" + salt, ${cdn_exp_group | n, dump_js_escaped_json}, timeElapsed, eventName);
|
||||
});
|
||||
});
|
||||
}
|
||||
$("#video_" + id).bind("initialize", null, initializeCDNExperiment);
|
||||
if ($("#video_" + id).hasClass("is-initialized")) {
|
||||
initializeCDNExperiment();
|
||||
}
|
||||
</script>
|
||||
% endif;
|
||||
@@ -6,7 +6,7 @@ from gettext import GNUTranslations
|
||||
|
||||
from completion.test_utils import CompletionWaffleTestMixin
|
||||
from django.db import connections
|
||||
from django.test import LiveServerTestCase, TestCase, override_settings
|
||||
from django.test import LiveServerTestCase, TestCase
|
||||
from django.utils.text import slugify
|
||||
from organizations.models import Organization
|
||||
from rest_framework.test import APIClient
|
||||
@@ -182,8 +182,6 @@ class ContentLibraryRuntimeTestMixin(ContentLibraryContentTestMixin):
|
||||
|
||||
|
||||
@requires_blockstore
|
||||
# EphemeralKeyValueStore requires a working cache, and the default test cache doesn't work:
|
||||
@override_settings(XBLOCK_RUNTIME_V2_EPHEMERAL_DATA_CACHE='blockstore')
|
||||
class ContentLibraryRuntimeBServiceTest(ContentLibraryRuntimeTestMixin, TestCase):
|
||||
"""
|
||||
Tests XBlock runtime using XBlocks in a content library using the standalone Blockstore service.
|
||||
@@ -191,8 +189,6 @@ class ContentLibraryRuntimeBServiceTest(ContentLibraryRuntimeTestMixin, TestCase
|
||||
|
||||
|
||||
@requires_blockstore_app
|
||||
# EphemeralKeyValueStore requires a working cache, and the default test cache doesn't work:
|
||||
@override_settings(XBLOCK_RUNTIME_V2_EPHEMERAL_DATA_CACHE='blockstore')
|
||||
class ContentLibraryRuntimeTest(ContentLibraryRuntimeTestMixin, BlockstoreAppTestMixin, LiveServerTestCase):
|
||||
"""
|
||||
Tests XBlock runtime using XBlocks in a content library using the installed Blockstore app.
|
||||
|
||||
@@ -241,6 +241,8 @@ class XBlockRuntime(RuntimeShim, Runtime):
|
||||
anonymous_user_id=self.anonymous_student_id,
|
||||
)
|
||||
elif service_name == "mako":
|
||||
if self.system.student_data_mode == XBlockRuntimeSystem.STUDENT_DATA_EPHEMERAL:
|
||||
return MakoService(namespace_prefix='lms.')
|
||||
return MakoService()
|
||||
elif service_name == "i18n":
|
||||
return ModuleI18nService(block=block)
|
||||
|
||||
Reference in New Issue
Block a user