Skip to content

Commit

Permalink
refactor: adds studio_post_paste handler
Browse files Browse the repository at this point in the history
for use after copy/pasting content from the clipboard.
  • Loading branch information
pomegranited committed Mar 5, 2024
1 parent 8b27072 commit 77f8311
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 14 deletions.
21 changes: 9 additions & 12 deletions cms/djangoapps/contentstore/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
from xmodule.contentstore.content import StaticContent
from xmodule.contentstore.django import contentstore
from xmodule.exceptions import NotFoundError
from xmodule.library_content_block import LibraryContentBlock
from xmodule.modulestore.django import modulestore
from xmodule.xml_block import XmlMixin

Expand Down Expand Up @@ -337,20 +336,18 @@ def _import_xml_node_to_parent(
new_xblock = store.update_item(temp_xblock, user_id, allow_not_found=True)
parent_xblock.children.append(new_xblock.location)
store.update_item(parent_xblock, user_id)
if isinstance(new_xblock, LibraryContentBlock):
# Special case handling for library content. If we need this for other blocks in the future, it can be made into
# an API, and we'd call new_block.studio_post_paste() instead of this code.
# In this case, we want to pull the children from the library and let library_tools assign their IDs.
new_xblock.sync_from_library(upgrade_to_latest=False)
else:

children_handled = False
if hasattr(new_xblock, 'studio_post_paste'):
# Allow an XBlock to do anything fancy it may need to when pasted from the clipboard.
# These blocks may handle their own children or parenting if needed. Let them return booleans to
# let us know if we need to handle these or not.
children_handed = new_xblock.studio_post_paste(store, node)

if not children_handled:
for child_node in child_nodes:
_import_xml_node_to_parent(child_node, new_xblock, store, user_id=user_id)

from cms.lib.xblock.tagging.tagged_block_mixin import TaggedBlockMixin
if isinstance(new_xblock, TaggedBlockMixin):
new_xblock.read_tags_from_node(node)
new_xblock.add_tags_from_field()

return new_xblock


Expand Down
15 changes: 15 additions & 0 deletions cms/lib/xblock/tagging/tagged_block_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,18 @@ def studio_post_duplicate(self, store, source_item) -> bool:
self.tags_v1 = self.serialize_tag_data(source_item.scope_ids.usage_id)
self.add_tags_from_field()
return False

def studio_post_paste(self, store, source_node) -> bool:
"""
Copies content tags from the source_node.
Returns False to indicate the children have not been handled.
"""
if hasattr(super(), 'studio_post_paste'):
super().studio_post_paste()

if 'tags-v1' in source_node.attrib:
self.tags_v1 = str(source_node.attrib['tags-v1'])

self.add_tags_from_field()
return False
13 changes: 13 additions & 0 deletions xmodule/library_content_block.py
Original file line number Diff line number Diff line change
Expand Up @@ -593,10 +593,23 @@ def studio_post_duplicate(self, store, source_block):
Otherwise we'll end up losing data on the next refresh.
"""
if hasattr(super(), 'studio_post_duplicate'):
super().studio_post_duplicate(store, source_block)

self._validate_sync_permissions()
self.get_tools(to_read_library_content=True).trigger_duplication(source_block=source_block, dest_block=self)
return True # Children have been handled.

def studio_post_paste(self, store, source_node) -> bool:
"""
Pull the children from the library and let library_tools assign their IDs.
"""
if hasattr(super(), 'studio_post_paste'):
super().studio_post_paste(store, source_node)

self.sync_from_library(upgrade_to_latest=False)
return True # Children have been handled

def _validate_library_version(self, validation, lib_tools, version, library_key):
"""
Validates library version
Expand Down
15 changes: 13 additions & 2 deletions xmodule/studio_editable.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def get_preview_view_name(block):
"""
return AUTHOR_VIEW if has_author_view(block) else STUDENT_VIEW

# Some parts of the code use getattr to dynamically check for the following three methods on subclasses.
# Some parts of the code use getattr to dynamically check for the following methods on subclasses.
# We'd like to refactor so that we can actually declare them here as overridable methods.
# For now, we leave them here as documentation.
# See https://github.com/openedx/edx-platform/issues/33715.
Expand All @@ -68,7 +68,7 @@ def get_preview_view_name(block):
# By default, is a no-op. Can be overriden in subclasses.
# """
#
# def studio_post_duplicate(self, dest_block) -> bool: # pylint: disable=unused-argument
# def studio_post_duplicate(self, store, source_block) -> bool: # pylint: disable=unused-argument
# """
# Called when a the block is duplicated. Can be used, e.g., for special handling of child duplication.
#
Expand All @@ -78,6 +78,17 @@ def get_preview_view_name(block):
# By default, is a no-op. Can be overriden in subclasses.
# """
# return False
#
# def studio_post_paste(self, store, source_node) -> bool: # pylint: disable=unused-argument
# """
# Called after a block is copy-pasted. Can be used, e.g., for special handling of child duplication.
#
# Returns 'True' if children have been handled and thus shouldn't be handled by the standard
# duplication logic.
#
# By default, is a no-op. Can be overriden in subclasses.
# """
# return False


def has_author_view(block):
Expand Down

0 comments on commit 77f8311

Please sign in to comment.