feat: Remove component-level copy/paste feature flag (#32980)
This is so that the feature is on by default.
This commit is contained in:
@@ -198,22 +198,6 @@ def individualize_anonymous_user_id(course_id):
|
||||
return INDIVIDUALIZE_ANONYMOUS_USER_ID.is_enabled(course_id)
|
||||
|
||||
|
||||
# .. toggle_name: contentstore.enable_copy_paste_feature
|
||||
# .. toggle_implementation: WaffleFlag
|
||||
# .. toggle_default: False
|
||||
# .. toggle_description: Moves most component-level actions into a submenu and adds new "Copy Component" and "Paste
|
||||
# Component" actions which can be used to copy components (XBlocks) within or among courses.
|
||||
# .. toggle_use_cases: temporary
|
||||
# .. toggle_creation_date: 2023-02-28
|
||||
# .. toggle_target_removal_date: 2023-05-01
|
||||
# .. toggle_tickets: https://github.com/openedx/modular-learning/issues/11 https://github.com/openedx/modular-learning/issues/50
|
||||
ENABLE_COPY_PASTE_FEATURE = WaffleFlag(
|
||||
f'{CONTENTSTORE_NAMESPACE}.enable_copy_paste_feature',
|
||||
__name__,
|
||||
CONTENTSTORE_LOG_PREFIX,
|
||||
)
|
||||
|
||||
|
||||
# .. toggle_name: contentstore.enable_copy_paste_units
|
||||
# .. toggle_implementation: WaffleFlag
|
||||
# .. toggle_default: False
|
||||
|
||||
@@ -27,7 +27,7 @@ from xmodule.util.sandboxing import SandboxService
|
||||
from xmodule.util.builtin_assets import add_webpack_js_to_fragment
|
||||
from xmodule.x_module import AUTHOR_VIEW, PREVIEW_VIEWS, STUDENT_VIEW, XModuleMixin
|
||||
from cms.djangoapps.xblock_config.models import StudioConfig
|
||||
from cms.djangoapps.contentstore.toggles import individualize_anonymous_user_id, ENABLE_COPY_PASTE_FEATURE
|
||||
from cms.djangoapps.contentstore.toggles import individualize_anonymous_user_id
|
||||
from cms.lib.xblock.field_data import CmsFieldData
|
||||
from common.djangoapps.static_replace.services import ReplaceURLService
|
||||
from common.djangoapps.static_replace.wrapper import replace_urls_wrapper
|
||||
@@ -301,8 +301,6 @@ def _studio_wrap_xblock(xblock, view, frag, context, display_name_only=False):
|
||||
can_edit = context.get('can_edit', True)
|
||||
# Is this a course or a library?
|
||||
is_course = xblock.scope_ids.usage_id.context_key.is_course
|
||||
# Copy-paste is a new feature; while we are beta-testing it, only beta users with the Waffle flag enabled see it
|
||||
enable_copy_paste = can_edit and is_course and ENABLE_COPY_PASTE_FEATURE.is_enabled()
|
||||
template_context = {
|
||||
'xblock_context': context,
|
||||
'xblock': xblock,
|
||||
@@ -311,7 +309,6 @@ def _studio_wrap_xblock(xblock, view, frag, context, display_name_only=False):
|
||||
'is_root': is_root,
|
||||
'is_reorderable': is_reorderable,
|
||||
'can_edit': can_edit,
|
||||
'enable_copy_paste': enable_copy_paste,
|
||||
'can_edit_visibility': context.get('can_edit_visibility', is_course),
|
||||
'selected_groups_label': selected_groups_label,
|
||||
'can_add': context.get('can_add', True),
|
||||
|
||||
@@ -28,13 +28,13 @@ class UnitPageTestCase(StudioPageTestCase):
|
||||
Verify that a public xblock's preview returns the expected HTML.
|
||||
"""
|
||||
published_video = self.store.publish(self.video.location, self.user.id) # lint-amnesty, pylint: disable=unused-variable
|
||||
self.validate_preview_html(self.video, STUDENT_VIEW, can_add=False)
|
||||
self.validate_preview_html(self.video, STUDENT_VIEW, in_unit=True, can_add=False)
|
||||
|
||||
def test_draft_component_preview_html(self):
|
||||
"""
|
||||
Verify that a draft xblock's preview returns the expected HTML.
|
||||
"""
|
||||
self.validate_preview_html(self.video, STUDENT_VIEW, can_add=False)
|
||||
self.validate_preview_html(self.video, STUDENT_VIEW, in_unit=True, can_add=False)
|
||||
|
||||
def test_public_child_container_preview_html(self):
|
||||
"""
|
||||
@@ -46,7 +46,7 @@ class UnitPageTestCase(StudioPageTestCase):
|
||||
BlockFactory.create(parent_location=child_container.location,
|
||||
category='html', display_name='grandchild')
|
||||
published_child_container = self.store.publish(child_container.location, self.user.id)
|
||||
self.validate_preview_html(published_child_container, STUDENT_VIEW, can_add=False)
|
||||
self.validate_preview_html(published_child_container, STUDENT_VIEW, in_unit=True, can_add=False)
|
||||
|
||||
def test_draft_child_container_preview_html(self):
|
||||
"""
|
||||
@@ -58,4 +58,4 @@ class UnitPageTestCase(StudioPageTestCase):
|
||||
BlockFactory.create(parent_location=child_container.location,
|
||||
category='html', display_name='grandchild')
|
||||
draft_child_container = self.store.get_item(child_container.location)
|
||||
self.validate_preview_html(draft_child_container, STUDENT_VIEW, can_add=False)
|
||||
self.validate_preview_html(draft_child_container, STUDENT_VIEW, in_unit=True, can_add=False)
|
||||
|
||||
@@ -43,7 +43,7 @@ class StudioPageTestCase(CourseTestCase):
|
||||
resp_content = json.loads(resp.content.decode('utf-8'))
|
||||
return resp_content['html']
|
||||
|
||||
def validate_preview_html(self, xblock, view_name, can_add=True, can_reorder=True, can_move=True,
|
||||
def validate_preview_html(self, xblock, view_name, in_unit=False, can_add=True, can_reorder=True, can_move=True,
|
||||
can_edit=True, can_duplicate=True, can_delete=True):
|
||||
"""
|
||||
Verify that the specified xblock's preview has the expected HTML elements.
|
||||
@@ -59,9 +59,20 @@ class StudioPageTestCase(CourseTestCase):
|
||||
'<span data-tooltip="Drag to reorder" class="drag-handle action"></span>',
|
||||
can_reorder
|
||||
)
|
||||
|
||||
if in_unit:
|
||||
move_action_html = '<button data-tooltip="Move" class="btn-default move-button action-button">'
|
||||
delete_action_html = '<button data-tooltip="Delete" class="btn-default delete-button action-button">'
|
||||
duplicate_action_html = \
|
||||
'<button data-tooltip="Duplicate" class="btn-default duplicate-button action-button">'
|
||||
else:
|
||||
move_action_html = '<a class="move-button" href="#" role="button">Move</a>'
|
||||
delete_action_html = '<a class="delete-button" href="#" role="button">Delete</a>'
|
||||
duplicate_action_html = '<a class="duplicate-button" href="#" role="button">Duplicate</a>'
|
||||
|
||||
self.validate_html_for_action_button(
|
||||
html,
|
||||
'<button data-tooltip="Move" class="btn-default move-button action-button">',
|
||||
move_action_html,
|
||||
can_move
|
||||
)
|
||||
self.validate_html_for_action_button(
|
||||
@@ -69,17 +80,19 @@ class StudioPageTestCase(CourseTestCase):
|
||||
'button class="btn-default edit-button action-button"',
|
||||
can_edit
|
||||
)
|
||||
|
||||
self.validate_html_for_action_button(
|
||||
html,
|
||||
'<button data-tooltip="Delete" class="btn-default delete-button action-button">',
|
||||
can_duplicate
|
||||
)
|
||||
self.validate_html_for_action_button(
|
||||
html,
|
||||
'<button data-tooltip="Duplicate" class="btn-default duplicate-button action-button">',
|
||||
delete_action_html,
|
||||
can_delete
|
||||
)
|
||||
|
||||
self.validate_html_for_action_button(
|
||||
html,
|
||||
duplicate_action_html,
|
||||
can_duplicate
|
||||
)
|
||||
|
||||
def validate_html_for_action_button(self, html, expected_html, can_action=True):
|
||||
"""
|
||||
Validate that the specified HTML has specific action..
|
||||
|
||||
@@ -94,88 +94,45 @@ block_is_unit = is_unit(xblock)
|
||||
<span class="action-button-text">${_("Edit")}</span>
|
||||
</button>
|
||||
</li>
|
||||
% if can_edit_visibility and not enable_copy_paste:
|
||||
<li class="action-item action-visibility">
|
||||
<button data-tooltip="${_("Access Settings")}" class="btn-default access-button action-button">
|
||||
<span class="icon fa fa-gear" aria-hidden="true"></span>
|
||||
<span class="sr">${_("Set Access")}</span>
|
||||
</button>
|
||||
</li>
|
||||
% endif
|
||||
% if can_add and not enable_copy_paste:
|
||||
<li class="action-item action-duplicate">
|
||||
<button data-tooltip="${_("Duplicate")}" class="btn-default duplicate-button action-button">
|
||||
<span class="icon fa fa-copy" aria-hidden="true"></span>
|
||||
<span class="sr">${_("Duplicate")}</span>
|
||||
</button>
|
||||
</li>
|
||||
% endif
|
||||
% if can_move and not enable_copy_paste:
|
||||
<li class="action-item action-move">
|
||||
<button data-tooltip="${_("Move")}" class="btn-default move-button action-button">
|
||||
<span class="stack-move-icon fa-stack fa-lg ">
|
||||
<span class="fa fa-file-o fa-stack-2x fa-fw" aria-hidden="true"></span>
|
||||
<span class="fa fa-arrow-right fa-stack-1x fa-fw" aria-hidden="true"></span>
|
||||
</span>
|
||||
<span class="sr">${_("Move")}</span>
|
||||
</button>
|
||||
</li>
|
||||
% endif
|
||||
% endif
|
||||
% if can_add and not enable_copy_paste:
|
||||
<!-- If we can add, we can delete. -->
|
||||
<li class="action-item action-delete">
|
||||
<button data-tooltip="${_("Delete")}" class="btn-default delete-button action-button">
|
||||
<span class="icon fa fa-trash-o" aria-hidden="true"></span>
|
||||
<span class="sr">${_("Delete")}</span>
|
||||
</button>
|
||||
</li>
|
||||
% endif
|
||||
% if enable_copy_paste:
|
||||
<!--
|
||||
If the "copy/paste" feature flag is enabled, all the actions besides "Edit" appear in a
|
||||
menu. We use .nav-dd on the parent element and .nav-item on this button to get the same
|
||||
dropdown menu appearance and behavior as in Studio's various other nav bars.
|
||||
-->
|
||||
<li class="action-item action-actions-menu nav-item">
|
||||
<button data-tooltip="${_("Actions")}" class="btn-default show-actions-menu-button action-button">
|
||||
<span class="icon fa fa-ellipsis-v" aria-hidden="true"></span>
|
||||
<span class="sr">${_("Actions")}</span>
|
||||
</button>
|
||||
<div class="wrapper wrapper-nav-sub" style="right: -10px; top: 45px;">
|
||||
<div class="nav-sub">
|
||||
<ul>
|
||||
% if not show_inline:
|
||||
<li class="nav-item">
|
||||
<a class="copy-button" href="#" role="button">${_("Copy to Clipboard")}</a>
|
||||
</li>
|
||||
% if can_add:
|
||||
<li class="nav-item">
|
||||
<a class="duplicate-button" href="#" role="button">${_("Duplicate")}</a>
|
||||
</li>
|
||||
% endif
|
||||
% if can_move:
|
||||
<li class="nav-item">
|
||||
<a class="move-button" href="#" role="button">${_("Move")}</a>
|
||||
</li>
|
||||
% endif
|
||||
% if can_edit_visibility:
|
||||
<li class="nav-item">
|
||||
<a class="access-button" href="#" role="button">${_("Manage Access")}</a>
|
||||
</li>
|
||||
% endif
|
||||
% endif
|
||||
<li class="action-item action-actions-menu nav-item">
|
||||
<button data-tooltip="${_("Actions")}" class="btn-default show-actions-menu-button action-button">
|
||||
<span class="icon fa fa-ellipsis-v" aria-hidden="true"></span>
|
||||
<span class="sr">${_("Actions")}</span>
|
||||
</button>
|
||||
<div class="wrapper wrapper-nav-sub" style="right: -10px; top: 45px;">
|
||||
<div class="nav-sub">
|
||||
<ul>
|
||||
% if not show_inline:
|
||||
<li class="nav-item">
|
||||
<a class="copy-button" href="#" role="button">${_("Copy to Clipboard")}</a>
|
||||
</li>
|
||||
% if can_add:
|
||||
<!-- If we can add, we can delete. -->
|
||||
<li class="nav-item">
|
||||
<a class="delete-button" href="#" role="button">${_("Delete")}</a>
|
||||
<a class="duplicate-button" href="#" role="button">${_("Duplicate")}</a>
|
||||
</li>
|
||||
% endif
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
% endif
|
||||
% if can_move:
|
||||
<li class="nav-item">
|
||||
<a class="move-button" href="#" role="button">${_("Move")}</a>
|
||||
</li>
|
||||
% endif
|
||||
% if can_edit_visibility:
|
||||
<li class="nav-item">
|
||||
<a class="access-button" href="#" role="button">${_("Manage Access")}</a>
|
||||
</li>
|
||||
% endif
|
||||
% endif
|
||||
% if can_add:
|
||||
<!-- If we can add, we can delete. -->
|
||||
<li class="nav-item">
|
||||
<a class="delete-button" href="#" role="button">${_("Delete")}</a>
|
||||
</li>
|
||||
% endif
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
% if is_reorderable:
|
||||
<li class="action-item action-drag">
|
||||
<span data-tooltip="${_('Drag to reorder')}" class="drag-handle action"></span>
|
||||
|
||||
Reference in New Issue
Block a user