Skip to content

Commit

Permalink
Make copied tags editable again after breaking the upstream link to l…
Browse files Browse the repository at this point in the history
…ibrary 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.
  • Loading branch information
pomegranited authored Feb 13, 2025
1 parent 0bede61 commit c02e567
Show file tree
Hide file tree
Showing 9 changed files with 77 additions and 8 deletions.
28 changes: 26 additions & 2 deletions cms/djangoapps/contentstore/signals/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,17 @@
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,
Expand All @@ -38,7 +45,11 @@
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__)
Expand Down Expand Up @@ -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))
21 changes: 21 additions & 0 deletions cms/djangoapps/contentstore/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
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
Expand Down Expand Up @@ -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))
25 changes: 24 additions & 1 deletion cms/djangoapps/contentstore/views/tests/test_clipboard_paste.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@
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
Expand Down Expand Up @@ -393,10 +400,16 @@ def test_paste_with_assets(self):
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):
"""
Expand Down Expand Up @@ -477,6 +490,16 @@ def test_paste_from_library_read_only_tags(self):
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.
Expand Down
1 change: 1 addition & 0 deletions openedx/core/djangoapps/content_tagging/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -441,3 +441,4 @@ def tag_object(
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
2 changes: 1 addition & 1 deletion requirements/constraints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion requirements/edx/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion requirements/edx/development.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion requirements/edx/doc.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion requirements/edx/testing.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit c02e567

Please sign in to comment.