feat: Remove component-level copy/paste feature flag (#32980)

This is so that the feature is on by default.
This commit is contained in:
Yusuf Musleh
2023-08-18 17:54:48 +01:00
committed by GitHub
parent 64abfd126c
commit 39e042cfbd
5 changed files with 60 additions and 109 deletions

View File

@@ -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

View File

@@ -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),

View File

@@ -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)

View File

@@ -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..

View File

@@ -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>