diff --git a/cms/djangoapps/contentstore/signals/handlers.py b/cms/djangoapps/contentstore/signals/handlers.py index b8be6ee84f..857ee04575 100644 --- a/cms/djangoapps/contentstore/signals/handlers.py +++ b/cms/djangoapps/contentstore/signals/handlers.py @@ -17,12 +17,14 @@ from openedx_events.content_authoring.data import ( CourseData, CourseScheduleData, LibraryBlockData, + LibraryContainerData, XBlockData, ) from openedx_events.content_authoring.signals import ( COURSE_CATALOG_INFO_CHANGED, COURSE_IMPORT_COMPLETED, LIBRARY_BLOCK_DELETED, + LIBRARY_CONTAINER_DELETED, XBLOCK_CREATED, XBLOCK_DELETED, XBLOCK_UPDATED, @@ -49,6 +51,7 @@ from ..tasks import ( create_or_update_upstream_links, handle_create_or_update_xblock_upstream_link, handle_unlink_upstream_block, + handle_unlink_upstream_container, ) from .signals import GRADING_POLICY_CHANGED @@ -314,3 +317,16 @@ def unlink_upstream_block_handler(**kwargs): return handle_unlink_upstream_block.delay(str(library_block.usage_key)) + + +@receiver(LIBRARY_CONTAINER_DELETED) +def unlink_upstream_container_handler(**kwargs): + """ + Handle unlinking the upstream (library) container from any downstream (course) blocks. + """ + library_container = kwargs.get("library_container", None) + if not library_container or not isinstance(library_container, LibraryContainerData): # pragma: no cover + log.error("Received null or incorrect data for event") + return + + handle_unlink_upstream_container.delay(str(library_container.container_key)) diff --git a/cms/djangoapps/contentstore/tasks.py b/cms/djangoapps/contentstore/tasks.py index b6cb2af53d..9a0b04d243 100644 --- a/cms/djangoapps/contentstore/tasks.py +++ b/cms/djangoapps/contentstore/tasks.py @@ -34,7 +34,7 @@ from olxcleaner.exceptions import ErrorLevel from olxcleaner.reporting import report_error_summary, report_errors from opaque_keys import InvalidKeyError from opaque_keys.edx.keys import CourseKey, UsageKey -from opaque_keys.edx.locator import LibraryLocator +from opaque_keys.edx.locator import LibraryLocator, LibraryContainerLocator from organizations.api import add_organization_course, ensure_organization from organizations.exceptions import InvalidOrganizationException from organizations.models import Organization, OrganizationCourse @@ -1506,7 +1506,23 @@ def handle_unlink_upstream_block(upstream_usage_key_string: str) -> None: upstream_usage_key=upstream_usage_key, ): make_copied_tags_editable(str(link.downstream_usage_key)) + + +@shared_task +@set_code_owner_attribute +def handle_unlink_upstream_container(upstream_container_key_string: str) -> None: + """ + Handle updates needed to downstream blocks when the upstream link is severed. + """ + ensure_cms("handle_unlink_upstream_container may only be executed in a CMS context") + + try: + upstream_container_key = LibraryContainerLocator.from_string(upstream_container_key_string) + except (InvalidKeyError): + LOGGER.exception(f'Invalid upstream container_key: {upstream_container_key_string}') + return + for link in ContainerLink.objects.filter( - upstream_usage_key=upstream_usage_key, + upstream_container_key=upstream_container_key, ): make_copied_tags_editable(str(link.downstream_usage_key))