Make copied tags editable again after breaking the upstream link to library content [FC-0076] (#36228)

When deleting an upstream library block, ensure that any tags that may have been copied to downstream blocks are made editable again. This is achieved by un-setting the `is_copied` flag on the downstream tags.
This commit is contained in:
Jillian
2025-02-14 03:23:05 +10:30
committed by GitHub
parent 0bede61246
commit c02e567201
9 changed files with 77 additions and 8 deletions

View File

@@ -12,10 +12,17 @@ from django.db import transaction
from django.dispatch import receiver
from edx_toggles.toggles import SettingToggle
from opaque_keys.edx.keys import CourseKey
from openedx_events.content_authoring.data import CourseCatalogData, CourseData, CourseScheduleData, XBlockData
from openedx_events.content_authoring.data import (
CourseCatalogData,
CourseData,
CourseScheduleData,
LibraryBlockData,
XBlockData,
)
from openedx_events.content_authoring.signals import (
COURSE_CATALOG_INFO_CHANGED,
COURSE_IMPORT_COMPLETED,
LIBRARY_BLOCK_DELETED,
XBLOCK_CREATED,
XBLOCK_DELETED,
XBLOCK_UPDATED,
@@ -38,7 +45,11 @@ from xmodule.modulestore.django import SignalHandler, modulestore
from xmodule.modulestore.exceptions import ItemNotFoundError
from ..models import PublishableEntityLink
from ..tasks import create_or_update_upstream_links, handle_create_or_update_xblock_upstream_link
from ..tasks import (
create_or_update_upstream_links,
handle_create_or_update_xblock_upstream_link,
handle_unlink_upstream_block,
)
from .signals import GRADING_POLICY_CHANGED
log = logging.getLogger(__name__)
@@ -287,3 +298,16 @@ def handle_new_course_import(**kwargs):
force=True,
replace=True
)
@receiver(LIBRARY_BLOCK_DELETED)
def unlink_upstream_block_handler(**kwargs):
"""
Handle unlinking the upstream (library) block from any downstream (course) blocks.
"""
library_block = kwargs.get("library_block", None)
if not library_block or not isinstance(library_block, LibraryBlockData):
log.error("Received null or incorrect data for event")
return
handle_unlink_upstream_block.delay(str(library_block.usage_key))

View File

@@ -67,6 +67,7 @@ from common.djangoapps.student.roles import CourseInstructorRole, CourseStaffRol
from common.djangoapps.util.monitoring import monitor_import_failure
from openedx.core.djangoapps.content.learning_sequences.api import key_supports_outlines
from openedx.core.djangoapps.content_libraries import api as v2contentlib_api
from openedx.core.djangoapps.content_tagging.api import make_copied_tags_editable
from openedx.core.djangoapps.course_apps.toggles import exams_ida_enabled
from openedx.core.djangoapps.discussions.config.waffle import ENABLE_NEW_STRUCTURE_DISCUSSIONS
from openedx.core.djangoapps.discussions.models import DiscussionsConfiguration, Provider
@@ -1466,3 +1467,23 @@ def create_or_update_upstream_links(
for xblock in xblocks:
create_or_update_xblock_upstream_link(xblock, course_key_str, created)
course_status.update_status(LearningContextLinksStatusChoices.COMPLETED)
@shared_task
@set_code_owner_attribute
def handle_unlink_upstream_block(upstream_usage_key_string: str) -> None:
"""
Handle updates needed to downstream blocks when the upstream link is severed.
"""
ensure_cms("handle_unlink_upstream_block may only be executed in a CMS context")
try:
upstream_usage_key = UsageKey.from_string(upstream_usage_key_string)
except (InvalidKeyError):
LOGGER.exception(f'Invalid upstream usage_key: {upstream_usage_key_string}')
return
for link in PublishableEntityLink.objects.filter(
upstream_usage_key=upstream_usage_key,
):
make_copied_tags_editable(str(link.downstream_usage_key))

View File

@@ -6,6 +6,13 @@ APIs.
import ddt
from opaque_keys.edx.keys import UsageKey
from rest_framework.test import APIClient
from openedx_events.content_authoring.signals import (
LIBRARY_BLOCK_DELETED,
XBLOCK_CREATED,
XBLOCK_DELETED,
XBLOCK_UPDATED,
)
from openedx_events.tests.utils import OpenEdxEventsTestMixin
from openedx_tagging.core.tagging.models import Tag
from organizations.models import Organization
from xmodule.modulestore.django import contentstore, modulestore
@@ -393,10 +400,16 @@ class ClipboardPasteTestCase(ModuleStoreTestCase):
assert source_pic2_hash != dest_pic2_hash # Because there was a conflict, this file was unchanged.
class ClipboardPasteFromV2LibraryTestCase(ModuleStoreTestCase):
class ClipboardPasteFromV2LibraryTestCase(OpenEdxEventsTestMixin, ModuleStoreTestCase):
"""
Test Clipboard Paste functionality with a "new" (as of Sumac) library
"""
ENABLED_OPENEDX_EVENTS = [
LIBRARY_BLOCK_DELETED.event_type,
XBLOCK_CREATED.event_type,
XBLOCK_DELETED.event_type,
XBLOCK_UPDATED.event_type,
]
def setUp(self):
"""
@@ -477,6 +490,16 @@ class ClipboardPasteFromV2LibraryTestCase(ModuleStoreTestCase):
assert object_tag.value in self.lib_block_tags
assert object_tag.is_copied
# If we delete the upstream library block...
library_api.delete_library_block(self.lib_block_key)
# ...the copied tags remain, but should no longer be marked as "copied"
object_tags = tagging_api.get_object_tags(new_block_key)
assert len(object_tags) == len(self.lib_block_tags)
for object_tag in object_tags:
assert object_tag.value in self.lib_block_tags
assert not object_tag.is_copied
def test_paste_from_library_copies_asset(self):
"""
Assets from a library component copied into a subdir of Files & Uploads.

View File

@@ -441,3 +441,4 @@ resync_object_tags = oel_tagging.resync_object_tags
get_object_tags = oel_tagging.get_object_tags
add_tag_to_taxonomy = oel_tagging.add_tag_to_taxonomy
copy_tags_as_read_only = oel_tagging.copy_tags
make_copied_tags_editable = oel_tagging.unmark_copied_tags

View File

@@ -131,7 +131,7 @@ optimizely-sdk<5.0
# Date: 2023-09-18
# pinning this version to avoid updates while the library is being developed
# Issue for unpinning: https://github.com/openedx/edx-platform/issues/35269
openedx-learning==0.18.2
openedx-learning==0.18.3
# Date: 2023-11-29
# Open AI version 1.0.0 dropped support for openai.ChatCompletion which is currently in use in enterprise.

View File

@@ -827,7 +827,7 @@ openedx-filters==1.12.0
# ora2
openedx-forum==0.1.6
# via -r requirements/edx/kernel.in
openedx-learning==0.18.2
openedx-learning==0.18.3
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in

View File

@@ -1381,7 +1381,7 @@ openedx-forum==0.1.6
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
openedx-learning==0.18.2
openedx-learning==0.18.3
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt

View File

@@ -1000,7 +1000,7 @@ openedx-filters==1.12.0
# ora2
openedx-forum==0.1.6
# via -r requirements/edx/base.txt
openedx-learning==0.18.2
openedx-learning==0.18.3
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt

View File

@@ -1048,7 +1048,7 @@ openedx-filters==1.12.0
# ora2
openedx-forum==0.1.6
# via -r requirements/edx/base.txt
openedx-learning==0.18.2
openedx-learning==0.18.3
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt