From 472801b774c9c02beefbcf13260b09d8176da875 Mon Sep 17 00:00:00 2001 From: Irtaza Akram <51848298+irtazaakram@users.noreply.github.com> Date: Tue, 12 Aug 2025 18:04:34 +0500 Subject: [PATCH] fix: add resourcetemplate to xblock_mixins (#37184) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add ResourceTemplates to XBLOCK_MIXINS so it’s applied to all CMS XBlocks at runtime instead of being directly inherited. This keeps the Studio-only feature in edx-platform (where it belongs), while still making it available to built-in and extracted XBlocks. When we extract built-in blocks from the platform, they will not be able to inherit ResourcesTemplates directly; they will get it from XBLOCK_MIXINS. So, we also needed to update a few template-related tests to use the mixed block class (or an instance of it) rather than the unmixed base class, because the unmixed base classes will soon be extracted and thus lack ResourceTemplates. Related to https://github.com/openedx/edx-platform/issues/34827 --- cms/djangoapps/contentstore/tests/test_crud.py | 12 +++++++----- .../contentstore/views/tests/test_block.py | 5 +++-- .../xblock_storage_handlers/create_xblock.py | 7 ++++--- cms/envs/common.py | 3 ++- xmodule/templates.py | 11 ++++++++--- 5 files changed, 24 insertions(+), 14 deletions(-) diff --git a/cms/djangoapps/contentstore/tests/test_crud.py b/cms/djangoapps/contentstore/tests/test_crud.py index a7d283e466..1c1f051524 100644 --- a/cms/djangoapps/contentstore/tests/test_crud.py +++ b/cms/djangoapps/contentstore/tests/test_crud.py @@ -4,11 +4,10 @@ from xmodule import templates from xmodule.capa_block import ProblemBlock from xmodule.course_block import CourseBlock -from xmodule.html_block import HtmlBlock from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.exceptions import DuplicateCourseError from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase -from xmodule.modulestore.tests.factories import CourseFactory, BlockFactory +from xmodule.modulestore.tests.factories import BlockFactory, CourseFactory from xmodule.seq_block import SequenceBlock @@ -40,11 +39,14 @@ class TemplateTests(ModuleStoreTestCase): self.assertRegex(dropdown['data'], r'\s*\s*

.*dropdown problems.*') def test_get_some_templates(self): + course = CourseFactory.create() + htmlblock = BlockFactory.create(category="html", parent_location=course.location) + self.assertEqual(len(SequenceBlock.templates()), 0) - self.assertGreater(len(HtmlBlock.templates()), 0) + self.assertGreater(len(htmlblock.templates()), 0) self.assertIsNone(SequenceBlock.get_template('doesntexist.yaml')) - self.assertIsNone(HtmlBlock.get_template('doesntexist.yaml')) - self.assertIsNotNone(HtmlBlock.get_template('announcement.yaml')) + self.assertIsNone(htmlblock.get_template('doesntexist.yaml')) + self.assertIsNotNone(htmlblock.get_template('announcement.yaml')) def test_factories(self): test_course = CourseFactory.create( diff --git a/cms/djangoapps/contentstore/views/tests/test_block.py b/cms/djangoapps/contentstore/views/tests/test_block.py index 5447e6e596..01aff3d613 100644 --- a/cms/djangoapps/contentstore/views/tests/test_block.py +++ b/cms/djangoapps/contentstore/views/tests/test_block.py @@ -33,7 +33,6 @@ from xblock.fields import Scope, ScopeIds, String from xblock.runtime import DictKeyValueStore, KvsFieldData from xblock.test.tools import TestRuntime from xblock.validation import ValidationMessage -from xmodule.capa_block import ProblemBlock from xmodule.course_block import DEFAULT_START_DATE from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.django import modulestore @@ -619,7 +618,9 @@ class TestCreateItem(ItemTest): prob_usage_key = self.response_usage_key(resp) problem = self.get_item_from_modulestore(prob_usage_key) # check against the template - template = ProblemBlock.get_template(template_id) + course = CourseFactory.create() + problem_block = BlockFactory.create(category="problem", parent_location=course.location) + template = problem_block.get_template(template_id) self.assertEqual(problem.data, template["data"]) self.assertEqual(problem.display_name, template["metadata"]["display_name"]) self.assertEqual(problem.markdown, template["metadata"]["markdown"]) diff --git a/cms/djangoapps/contentstore/xblock_storage_handlers/create_xblock.py b/cms/djangoapps/contentstore/xblock_storage_handlers/create_xblock.py index 235510f9ed..64b5c442bc 100644 --- a/cms/djangoapps/contentstore/xblock_storage_handlers/create_xblock.py +++ b/cms/djangoapps/contentstore/xblock_storage_handlers/create_xblock.py @@ -30,9 +30,10 @@ def create_xblock(parent_locator, user, category, display_name, boilerplate=None data = None template_id = boilerplate if template_id: - clz = parent.runtime.load_block_type(category) - if clz is not None: - template = clz.get_template(template_id) + base_clz = parent.runtime.load_block_type(category) + dynamic_clz = parent.runtime.mixologist.mix(base_clz) + if dynamic_clz is not None: + template = dynamic_clz.get_template(template_id) if template is not None: metadata = template.get('metadata', {}) data = template.get('data') diff --git a/cms/envs/common.py b/cms/envs/common.py index a6896160b6..9dfa2f107e 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -828,7 +828,7 @@ P3P_HEADER = 'CP="Open EdX does not have a P3P policy."' # Import after sys.path fixup from xmodule.modulestore.inheritance import InheritanceMixin -from xmodule.x_module import XModuleMixin +from xmodule.x_module import XModuleMixin, ResourceTemplates # These are the Mixins that will be added to every Blocklike upon instantiation. # DO NOT EXPAND THIS LIST!! We want it eventually to be EMPTY. Why? Because dynamically adding functions/behaviors to @@ -841,6 +841,7 @@ XBLOCK_MIXINS = ( # (b) refactor their functionality out of the Blocklike objects and into the edx-platform block runtimes. LmsBlockMixin, InheritanceMixin, + ResourceTemplates, XModuleMixin, EditInfoMixin, AuthoringMixin, diff --git a/xmodule/templates.py b/xmodule/templates.py index 550d84ae43..ccbf6a80a2 100644 --- a/xmodule/templates.py +++ b/xmodule/templates.py @@ -16,6 +16,8 @@ from collections import defaultdict from xblock.core import XBlock +from xmodule.modulestore.tests.factories import BlockFactory, CourseFactory + log = logging.getLogger(__name__) @@ -25,9 +27,12 @@ def all_templates(): """ # TODO use memcache to memoize w/ expiration templates = defaultdict(list) - for category, block in XBlock.load_classes(): - if not hasattr(block, 'templates'): + course = CourseFactory.create() + + for category, _ in XBlock.load_classes(): + loaded_block = BlockFactory.create(category=category, parent_location=course.location) + if not hasattr(loaded_block, 'templates'): continue - templates[category] = block.templates() + templates[category] = loaded_block.templates() return templates