Skip to content

Commit

Permalink
feat: duplicate tags when duplicating a tagged XBlock. (#637)
Browse files Browse the repository at this point in the history
* feat: duplicate tags when duplicating a tagged XBlock.

* test: fixes flaky test
  • Loading branch information
pomegranited authored Feb 29, 2024
1 parent 6f76a82 commit f1c806f
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 5 deletions.
52 changes: 52 additions & 0 deletions cms/djangoapps/contentstore/views/tests/test_block.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
from common.djangoapps.xblock_django.user_service import DjangoXBlockUserService
from lms.djangoapps.lms_xblock.mixin import NONSENSICAL_ACCESS_RESTRICTION
from openedx.core.djangoapps.discussions.models import DiscussionsConfiguration
from openedx.core.djangoapps.content_tagging import api as tagging_api

from ..component import component_handler, get_component_templates
from cms.djangoapps.contentstore.xblock_storage_handlers.view_handlers import (
Expand Down Expand Up @@ -1106,6 +1107,57 @@ def test_duplicate_library_content_block(self): # pylint: disable=too-many-stat
assert dupe_html_2.display_name == "HTML 2 Title (Lib Update)"
assert dupe_html_2.data == "HTML 2 Content (Lib Update)"

def test_duplicate_tags(self):
"""
Test that duplicating a tagged XBlock also duplicates its content tags.
"""
source_course = CourseFactory()
user = UserFactory.create()
source_chapter = BlockFactory(
parent=source_course, category="chapter", display_name="Source Chapter"
)
source_block = BlockFactory(parent=source_chapter, category="html", display_name="Child")

# Create a couple of taxonomies with tags
taxonomyA = tagging_api.create_taxonomy(name="A", export_id="A")
taxonomyB = tagging_api.create_taxonomy(name="B", export_id="B")
tagging_api.set_taxonomy_orgs(taxonomyA, all_orgs=True)
tagging_api.set_taxonomy_orgs(taxonomyB, all_orgs=True)
tagging_api.add_tag_to_taxonomy(taxonomyA, "one")
tagging_api.add_tag_to_taxonomy(taxonomyA, "two")
tagging_api.add_tag_to_taxonomy(taxonomyB, "three")
tagging_api.add_tag_to_taxonomy(taxonomyB, "four")

# Tag the chapter
tagging_api.tag_object(taxonomyA, ["one", "two"], str(source_chapter.location))
tagging_api.tag_object(taxonomyB, ["three", "four"], str(source_chapter.location))

# Tag the child block
tagging_api.tag_object(taxonomyA, ["two"], str(source_block.location))

# Refresh.
source_chapter = self.store.get_item(source_chapter.location)
expected_chapter_tags = 'A:one,two;B:four,three'
assert source_chapter.serialize_tag_data() == expected_chapter_tags

source_block = self.store.get_item(source_block.location)
expected_block_tags = 'A:two'
assert source_block.serialize_tag_data() == expected_block_tags

# Duplicate the chapter (and its children)
dupe_location = duplicate_block(
parent_usage_key=source_course.location,
duplicate_source_usage_key=source_chapter.location,
user=user,
)
dupe_chapter = self.store.get_item(dupe_location)
self.assertEqual(len(dupe_chapter.get_children()), 1)
dupe_block = dupe_chapter.get_children()[0]

# Check that the duplicated blocks also duplicated tags
assert dupe_chapter.serialize_tag_data() == expected_chapter_tags
assert dupe_block.serialize_tag_data() == expected_block_tags


@ddt.ddt
class TestMoveItem(ItemTest):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,10 +203,8 @@ def test_copy_and_paste_unit_with_tags(self):
# Only tags from the taxonomy that is associated with the dest org should be copied
tags = list(tagging_api.get_object_tags(str(dest_unit_key)))
assert len(tags) == 2
assert str(tags[0]) == '<ObjectTag> ' \
'block-v1:org.2025+course_2025+Destination_Course+type@vertical+block@vertical1: test_taxonomy=tag_1'
assert str(tags[1]) == '<ObjectTag> ' \
'block-v1:org.2025+course_2025+Destination_Course+type@vertical+block@vertical1: test_taxonomy=tag_2'
assert str(tags[0]) == f'<ObjectTag> {dest_unit_key}: test_taxonomy=tag_1'
assert str(tags[1]) == f'<ObjectTag> {dest_unit_key}: test_taxonomy=tag_2'

def test_paste_with_assets(self):
"""
Expand Down
15 changes: 14 additions & 1 deletion cms/lib/xblock/tagging/tagged_block_mixin.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
# lint-amnesty, pylint: disable=missing-module-docstring
"""
Content tagging functionality for XBlocks.
"""
from urllib.parse import quote, unquote


class TaggedBlockMixin:
"""
Mixin containing XML serializing and parsing functionality for tagged blocks
"""
def studio_post_duplicate(self, store, source_item):
"""
Duplicates content tags from the source_item.
"""
if hasattr(super(), 'studio_post_duplicate'):
super().studio_post_duplicate()

if hasattr(source_item, 'serialize_tag_data'):
tags = source_item.serialize_tag_data()
self.xml_attributes['tags-v1'] = tags
self.add_tags_from_xml()

def serialize_tag_data(self):
"""
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 @@ -203,3 +203,4 @@ def set_object_tags(
resync_object_tags = oel_tagging.resync_object_tags
get_object_tags = oel_tagging.get_object_tags
tag_object = oel_tagging.tag_object
add_tag_to_taxonomy = oel_tagging.add_tag_to_taxonomy

0 comments on commit f1c806f

Please sign in to comment.