From e1a801a700f170bb3c5cd607605ff87ca7e18bbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B4mulo=20Penido?= Date: Mon, 21 Jul 2025 17:35:51 -0300 Subject: [PATCH] feat: link section/subsection to course Allows adding and syncing containers from libraries into courses --- .../xblock_storage_handlers/view_handlers.py | 44 +++++++++++++++---- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/cms/djangoapps/contentstore/xblock_storage_handlers/view_handlers.py b/cms/djangoapps/contentstore/xblock_storage_handlers/view_handlers.py index 31a1746676..ef190cece1 100644 --- a/cms/djangoapps/contentstore/xblock_storage_handlers/view_handlers.py +++ b/cms/djangoapps/contentstore/xblock_storage_handlers/view_handlers.py @@ -20,7 +20,7 @@ from django.core.exceptions import PermissionDenied from django.http import HttpResponse, HttpResponseBadRequest from django.utils.translation import gettext as _ from edx_django_utils.plugins import pluggable_override -from openedx.core.djangoapps.content_libraries.api import LibraryXBlockMetadata +from openedx.core.djangoapps.content_libraries.api import ContainerMetadata, ContainerType, LibraryXBlockMetadata from openedx.core.djangoapps.content_tagging.api import get_object_tag_counts from edx_proctoring.api import ( does_backend_support_onboarding, @@ -533,6 +533,7 @@ def sync_library_content(downstream: XBlock, request, store) -> StaticFileNotice Handle syncing library content for given xblock depending on its upstream type. It can sync unit containers and lower level xblocks. """ + # CHECK: Sync library content for given xblock depending on its upstream type. link = UpstreamLink.get_for_block(downstream) upstream_key = link.upstream_key if isinstance(upstream_key, LibraryUsageLocatorV2): @@ -548,28 +549,54 @@ def sync_library_content(downstream: XBlock, request, store) -> StaticFileNotice notices = [] # Store final children keys to update order of components in unit children = [] + for i, upstream_child in enumerate(upstream_children): - assert isinstance(upstream_child, LibraryXBlockMetadata) # for now we only support units - if upstream_child.usage_key not in downstream_children_keys: + if isinstance(upstream_child, LibraryXBlockMetadata): + upstream_key = upstream_child.usage_key + block_type = upstream_child.usage_key.block_type + elif isinstance(upstream_child, ContainerMetadata): + upstream_key = upstream_child.container_key + match upstream_child.container_type: + case ContainerType.Unit: + block_type = "vertical" + case ContainerType.Subsection: + block_type = "sequential" + case _: + # We don't support other container types for now. + log.error( + "Unexpected upstream child container type: %s", + upstream_child.container_type, + ) + continue + else: + log.error( + "Unexpected type of upstream child: %s", + type(upstream_child), + ) + continue + + if upstream_key not in downstream_children_keys: # This upstream_child is new, create it. downstream_child = store.create_child( parent_usage_key=downstream.usage_key, position=i, user_id=request.user.id, - block_type=upstream_child.usage_key.block_type, + block_type=block_type, # TODO: Can we generate a unique but friendly block_id, perhaps using upstream block_id - block_id=f"{upstream_child.usage_key.block_type}{uuid4().hex[:8]}", + block_id=f"{block_type}{uuid4().hex[:8]}", fields={ - "upstream": str(upstream_child.usage_key), + "upstream": str(upstream_key), }, ) else: - downstream_child_old_index = downstream_children_keys.index(upstream_child.usage_key) + downstream_child_old_index = downstream_children_keys.index(upstream_key) downstream_child = downstream_children[downstream_child_old_index] - result = sync_library_content(downstream=downstream_child, request=request, store=store) children.append(downstream_child.usage_key) + + result = sync_library_content(downstream=downstream_child, request=request, store=store) notices.append(result) + for child in downstream_children: if child.usage_key not in children: # This downstream block was added, or deleted from upstream block. @@ -634,6 +661,7 @@ def _create_block(request): status=400, ) + # CHECK: Add container to course created_block = create_xblock( parent_locator=parent_locator, user=request.user,