Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: inheritable authoring mixin callbacks for editing & duplication #33756

Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
536493f
refactor: inheritable studioeditableblock's callbacks for editing & d…
DanielVZ96 Nov 21, 2023
5901e25
refactor: remove hasattr and add editable studio to cms xblock modules
DanielVZ96 Nov 25, 2023
5c2dabb
fix: tests
DanielVZ96 Nov 25, 2023
3e9bb60
fix: library preview
DanielVZ96 Nov 25, 2023
42c2037
style: ran darker
DanielVZ96 Nov 25, 2023
b4a4c96
style: pylint fixes
DanielVZ96 Nov 25, 2023
aaa26bf
style: use *_args and **_kwargs
DanielVZ96 Nov 30, 2023
116b013
refactor: move load_services_for_studio back into
DanielVZ96 Nov 30, 2023
c5cb3f8
refactor: move editor_saved, post_editor_saved, and studio_post_dupli…
DanielVZ96 Nov 30, 2023
6199588
refactor: move duplication logic into authoringmixin
DanielVZ96 Nov 30, 2023
7f6924f
style: pylint and pep8 fixes
DanielVZ96 Nov 30, 2023
adeec3a
refactor: rename source_item to source_block
DanielVZ96 Nov 30, 2023
cd78eb3
refactor: remove redundant hasattrs due to inheritance
DanielVZ96 Dec 1, 2023
893ff58
Merge branch 'master' into dvz/studio-editable-block-callback-refactor
DanielVZ96 Dec 5, 2023
8ad064c
Merge branch 'master' into dvz/studio-editable-block-callback-refactor
DanielVZ96 Dec 5, 2023
e94fa52
Merge branch 'master' into dvz/studio-editable-block-callback-refactor
DanielVZ96 Dec 8, 2023
3cb2769
revert: remove studio duplicate hooks
DanielVZ96 Jul 7, 2024
5312b5e
Merge branch 'master' into dvz/studio-editable-block-callback-refactor
DanielVZ96 Jul 8, 2024
9c85399
style: fix pylint unused imports
DanielVZ96 Jul 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 11 additions & 69 deletions cms/djangoapps/contentstore/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
import logging
from collections import defaultdict
from contextlib import contextmanager
from datetime import datetime, timezone
from uuid import uuid4
from datetime import datetime

from django.conf import settings
from django.core.exceptions import ValidationError
Expand All @@ -18,8 +17,6 @@
from lti_consumer.models import CourseAllowPIISharingInLTIFlag
from opaque_keys.edx.keys import CourseKey, UsageKey
from opaque_keys.edx.locator import LibraryLocator
from openedx_events.content_authoring.data import DuplicatedXBlockData
from openedx_events.content_authoring.signals import XBLOCK_DUPLICATED
from milestones import api as milestones_api
from pytz import UTC
from xblock.fields import Scope
Expand Down Expand Up @@ -1026,77 +1023,22 @@ def duplicate_block(
Duplicate an existing xblock as a child of the supplied parent_usage_key. You can
optionally specify what usage key the new duplicate block will use via dest_usage_key.

If shallow is True, does not copy children. Otherwise, this function calls itself
recursively, and will set the is_child flag to True when dealing with recursed child
blocks.
If shallow is True, does not copy children.
"""
store = modulestore()
with store.bulk_operations(duplicate_source_usage_key.course_key):
source_item = store.get_item(duplicate_source_usage_key)
if not dest_usage_key:
# Change the blockID to be unique.
dest_usage_key = source_item.location.replace(name=uuid4().hex)

category = dest_usage_key.block_type

duplicate_metadata, asides_to_create = gather_block_attributes(
source_item, display_name=display_name, is_child=is_child,
return source_item.studio_duplicate(
parent_usage_key=parent_usage_key,
duplicate_source_usage_key=duplicate_source_usage_key,
user=user,
store=store,
dest_usage_key=dest_usage_key,
display_name=display_name,
shallow=shallow,
is_child=is_child,
)

dest_block = store.create_item(
user.id,
dest_usage_key.course_key,
dest_usage_key.block_type,
block_id=dest_usage_key.block_id,
definition_data=source_item.get_explicitly_set_fields_by_scope(Scope.content),
metadata=duplicate_metadata,
runtime=source_item.runtime,
asides=asides_to_create
)

children_handled = False

if hasattr(dest_block, 'studio_post_duplicate'):
# Allow an XBlock to do anything fancy it may need to when duplicated from another block.
# 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.
load_services_for_studio(dest_block.runtime, user)
children_handled = dest_block.studio_post_duplicate(store, source_item)

# Children are not automatically copied over (and not all xblocks have a 'children' attribute).
# Because DAGs are not fully supported, we need to actually duplicate each child as well.
if source_item.has_children and not shallow and not children_handled:
dest_block.children = dest_block.children or []
for child in source_item.children:
dupe = duplicate_block(dest_block.location, child, user=user, is_child=True)
if dupe not in dest_block.children: # _duplicate_block may add the child for us.
dest_block.children.append(dupe)
store.update_item(dest_block, user.id)

# pylint: disable=protected-access
if 'detached' not in source_item.runtime.load_block_type(category)._class_tags:
parent = store.get_item(parent_usage_key)
# If source was already a child of the parent, add duplicate immediately afterward.
# Otherwise, add child to end.
if source_item.location in parent.children:
source_index = parent.children.index(source_item.location)
parent.children.insert(source_index + 1, dest_block.location)
else:
parent.children.append(dest_block.location)
store.update_item(parent, user.id)

# .. event_implemented_name: XBLOCK_DUPLICATED
XBLOCK_DUPLICATED.send_event(
time=datetime.now(timezone.utc),
xblock_info=DuplicatedXBlockData(
usage_key=dest_block.location,
block_type=dest_block.location.block_type,
source_usage_key=duplicate_source_usage_key,
)
)

return dest_block.location


def update_from_source(*, source_block, destination_block, user_id):
"""
Expand Down
4 changes: 2 additions & 2 deletions cms/djangoapps/contentstore/views/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from opaque_keys.edx.keys import CourseKey
from web_fragments.fragment import Fragment

from cms.djangoapps.contentstore.utils import load_services_for_studio
from cms.lib.xblock.authoring_mixin import VISIBILITY_VIEW
from common.djangoapps.edxmako.shortcuts import render_to_string
from common.djangoapps.student.auth import (
Expand All @@ -27,7 +28,7 @@
)
from xmodule.modulestore.django import (
modulestore,
) # lint-amnesty, pylint: disable=wrong-import-order
)


from xmodule.x_module import (
Expand All @@ -46,7 +47,6 @@
from cms.djangoapps.contentstore.xblock_storage_handlers.view_handlers import (
handle_xblock,
create_xblock_info,
load_services_for_studio,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's an indirect import, should be going directly to contentstore.utils

get_block_info,
get_xblock,
delete_orphans,
Expand Down
5 changes: 2 additions & 3 deletions cms/djangoapps/contentstore/views/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,13 @@
from openedx.core.djangoapps.content_staging import api as content_staging_api
from openedx.core.djangoapps.content_tagging.api import get_content_tags
from xmodule.modulestore.django import modulestore # lint-amnesty, pylint: disable=wrong-import-order
from xmodule.modulestore.exceptions import ItemNotFoundError # lint-amnesty, pylint: disable=wrong-import-order
from xmodule.modulestore.exceptions import ItemNotFoundError
from ..toggles import use_new_unit_page
from ..utils import get_lms_link_for_item, get_sibling_urls, reverse_course_url, get_unit_url
from ..utils import get_lms_link_for_item, get_sibling_urls, load_services_for_studio, reverse_course_url, get_unit_url
from ..helpers import get_parent_xblock, is_unit, xblock_type_display_name
from cms.djangoapps.contentstore.xblock_storage_handlers.view_handlers import (
add_container_page_publishing_info,
create_xblock_info,
load_services_for_studio,
)

__all__ = [
Expand Down
2 changes: 1 addition & 1 deletion cms/djangoapps/contentstore/views/preview.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
wrap_xblock_aside
)

from ..utils import get_visibility_partition_info, StudioPermissionsService
from ..utils import StudioPermissionsService, get_visibility_partition_info
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from formatter

from .access import get_user_role
from .session_kv_store import SessionKeyValueStore

Expand Down
Loading
Loading