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:
Jillian Vogel
2022-02-21 12:52:20 +10:30
parent 8b77638bf0
commit 732d8cb337
6 changed files with 11 additions and 246 deletions

View File

@@ -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

View File

@@ -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>

View File

@@ -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&hellip;</span>
</p>
</div>

View File

@@ -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;

View File

@@ -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.

View File

@@ -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)