Merge pull request #3229 from edx/christina/container-dnd
Drag and drop on container page
This commit is contained in:
@@ -35,6 +35,7 @@ from ..utils import get_modulestore
|
||||
from .access import has_course_access
|
||||
from .helpers import _xmodule_recurse
|
||||
from contentstore.utils import compute_publish_state, PublishState
|
||||
from xmodule.modulestore.draft import DIRECT_ONLY_CATEGORIES
|
||||
from contentstore.views.preview import get_preview_fragment
|
||||
from edxmako.shortcuts import render_to_string
|
||||
from models.settings.course_grading import CourseGradingModel
|
||||
@@ -193,6 +194,7 @@ def xblock_view_handler(request, package_id, view_name, tag=None, branch=None, v
|
||||
if 'application/json' in accept_header:
|
||||
store = get_modulestore(old_location)
|
||||
component = store.get_item(old_location)
|
||||
is_read_only = _xblock_is_read_only(component)
|
||||
|
||||
# wrap the generated fragment in the xmodule_editor div so that the javascript
|
||||
# can bind to it correctly
|
||||
@@ -212,12 +214,18 @@ def xblock_view_handler(request, package_id, view_name, tag=None, branch=None, v
|
||||
store.update_item(component, None)
|
||||
|
||||
elif view_name == 'student_view' and component.has_children:
|
||||
context = {
|
||||
'runtime_type': 'studio',
|
||||
'container_view': False,
|
||||
'read_only': is_read_only,
|
||||
'root_xblock': component,
|
||||
}
|
||||
# For non-leaf xblocks on the unit page, show the special rendering
|
||||
# which links to the new container page.
|
||||
html = render_to_string('container_xblock_component.html', {
|
||||
'xblock_context': context,
|
||||
'xblock': component,
|
||||
'locator': locator,
|
||||
'reordering_enabled': True,
|
||||
})
|
||||
return JsonResponse({
|
||||
'html': html,
|
||||
@@ -225,8 +233,6 @@ def xblock_view_handler(request, package_id, view_name, tag=None, branch=None, v
|
||||
})
|
||||
elif view_name in ('student_view', 'container_preview'):
|
||||
is_container_view = (view_name == 'container_preview')
|
||||
component_publish_state = compute_publish_state(component)
|
||||
is_read_only_view = component_publish_state == PublishState.public
|
||||
|
||||
# Only show the new style HTML for the container view, i.e. for non-verticals
|
||||
# Note: this special case logic can be removed once the unit page is replaced
|
||||
@@ -234,7 +240,7 @@ def xblock_view_handler(request, package_id, view_name, tag=None, branch=None, v
|
||||
context = {
|
||||
'runtime_type': 'studio',
|
||||
'container_view': is_container_view,
|
||||
'read_only': is_read_only_view,
|
||||
'read_only': is_read_only,
|
||||
'root_xblock': component,
|
||||
}
|
||||
|
||||
@@ -244,6 +250,7 @@ def xblock_view_handler(request, package_id, view_name, tag=None, branch=None, v
|
||||
# into the preview fragment, so we don't want to add another header here.
|
||||
if not is_container_view:
|
||||
fragment.content = render_to_string('component.html', {
|
||||
'xblock_context': context,
|
||||
'preview': fragment.content,
|
||||
'label': component.display_name or component.scope_ids.block_type,
|
||||
})
|
||||
@@ -263,6 +270,17 @@ def xblock_view_handler(request, package_id, view_name, tag=None, branch=None, v
|
||||
return HttpResponse(status=406)
|
||||
|
||||
|
||||
def _xblock_is_read_only(xblock):
|
||||
"""
|
||||
Returns true if the specified xblock is read-only, meaning that it cannot be edited.
|
||||
"""
|
||||
# We allow direct editing of xblocks in DIRECT_ONLY_CATEGORIES (for example, static pages).
|
||||
if xblock.category in DIRECT_ONLY_CATEGORIES:
|
||||
return False
|
||||
component_publish_state = compute_publish_state(xblock)
|
||||
return component_publish_state == PublishState.public
|
||||
|
||||
|
||||
def _save_item(request, usage_loc, item_location, data=None, children=None, metadata=None, nullout=None,
|
||||
grader_type=None, publish=None):
|
||||
"""
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
Unit tests for the container view.
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
from contentstore.tests.utils import CourseTestCase
|
||||
from contentstore.utils import compute_publish_state, PublishState
|
||||
from contentstore.views.helpers import xblock_studio_url
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from xmodule.modulestore.django import loc_mapper, modulestore
|
||||
from xmodule.modulestore.tests.factories import ItemFactory
|
||||
|
||||
|
||||
@@ -56,7 +58,6 @@ class ContainerViewTestCase(CourseTestCase):
|
||||
parent_location=published_xblock_with_child.location,
|
||||
category="html", display_name="Child HTML"
|
||||
)
|
||||
draft_xblock_with_child = modulestore('draft').convert_to_draft(published_xblock_with_child.location)
|
||||
branch_name = "MITx.999.Robot_Super_Course/branch/draft/block"
|
||||
self._test_html_content(
|
||||
published_xblock_with_child,
|
||||
@@ -73,6 +74,11 @@ class ContainerViewTestCase(CourseTestCase):
|
||||
r'<a href="#" class="navigation-link navigation-current">Wrapper</a>'
|
||||
).format(branch_name=branch_name)
|
||||
)
|
||||
|
||||
# Now make the unit and its children into a draft and validate the container again
|
||||
modulestore('draft').convert_to_draft(self.vertical.location)
|
||||
modulestore('draft').convert_to_draft(self.child_vertical.location)
|
||||
draft_xblock_with_child = modulestore('draft').convert_to_draft(published_xblock_with_child.location)
|
||||
self._test_html_content(
|
||||
draft_xblock_with_child,
|
||||
branch_name=branch_name,
|
||||
@@ -112,3 +118,37 @@ class ContainerViewTestCase(CourseTestCase):
|
||||
branch_name=branch_name
|
||||
)
|
||||
self.assertIn(expected_unit_link, html)
|
||||
|
||||
def test_container_preview_html(self):
|
||||
"""
|
||||
Verify that an xblock returns the expected HTML for a container preview
|
||||
"""
|
||||
# First verify that the behavior is correct with a published container
|
||||
self._test_preview_html(self.vertical)
|
||||
self._test_preview_html(self.child_vertical)
|
||||
|
||||
# Now make the unit and its children into a draft and validate the preview again
|
||||
draft_unit = modulestore('draft').convert_to_draft(self.vertical.location)
|
||||
draft_container = modulestore('draft').convert_to_draft(self.child_vertical.location)
|
||||
self._test_preview_html(draft_unit)
|
||||
self._test_preview_html(draft_container)
|
||||
|
||||
def _test_preview_html(self, xblock):
|
||||
"""
|
||||
Verify that the specified xblock has the expected HTML elements for container preview
|
||||
"""
|
||||
locator = loc_mapper().translate_location(self.course.id, xblock.location, published=False)
|
||||
publish_state = compute_publish_state(xblock)
|
||||
preview_url = '/xblock/{locator}/container_preview'.format(locator=locator)
|
||||
|
||||
resp = self.client.get(preview_url, HTTP_ACCEPT='application/json')
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
resp_content = json.loads(resp.content)
|
||||
html = resp_content['html']
|
||||
|
||||
# Verify that there are no drag handles for public pages
|
||||
drag_handle_html = '<span data-tooltip="Drag to reorder" class="drag-handle action"></span>'
|
||||
if publish_state == PublishState.public:
|
||||
self.assertNotIn(drag_handle_html, html)
|
||||
else:
|
||||
self.assertIn(drag_handle_html, html)
|
||||
|
||||
@@ -4,6 +4,7 @@ import json
|
||||
from contentstore.views import tabs
|
||||
from contentstore.tests.utils import CourseTestCase
|
||||
from django.test import TestCase
|
||||
from xmodule.modulestore.django import loc_mapper
|
||||
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
|
||||
from courseware.courses import get_course_by_id
|
||||
from xmodule.tabs import CourseTabList, WikiTab
|
||||
@@ -22,7 +23,7 @@ class TabsPageTests(CourseTestCase):
|
||||
self.url = self.course_locator.url_reverse('tabs')
|
||||
|
||||
# add a static tab to the course, for code coverage
|
||||
ItemFactory.create(
|
||||
self.test_tab = ItemFactory.create(
|
||||
parent_location=self.course_location,
|
||||
category="static_tab",
|
||||
display_name="Static_1"
|
||||
@@ -172,6 +173,25 @@ class TabsPageTests(CourseTestCase):
|
||||
)
|
||||
self.check_invalid_tab_id_response(resp)
|
||||
|
||||
def test_tab_preview_html(self):
|
||||
"""
|
||||
Verify that the static tab renders itself with the correct HTML
|
||||
"""
|
||||
locator = loc_mapper().translate_location(self.course.id, self.test_tab.location)
|
||||
preview_url = '/xblock/{locator}/student_view'.format(locator=locator)
|
||||
|
||||
resp = self.client.get(preview_url, HTTP_ACCEPT='application/json')
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
resp_content = json.loads(resp.content)
|
||||
html = resp_content['html']
|
||||
|
||||
# Verify that the HTML contains the expected elements
|
||||
self.assertIn('<span class="action-button-text">Edit</span>', html)
|
||||
self.assertIn('<span class="sr">Duplicate this component</span>', html)
|
||||
self.assertIn('<span class="sr">Delete this component</span>', html)
|
||||
self.assertIn('<span data-tooltip="Drag to reorder" class="drag-handle"></span>', html)
|
||||
|
||||
|
||||
|
||||
class PrimitiveTabEdit(TestCase):
|
||||
"""Tests for the primitive tab edit data manipulations"""
|
||||
|
||||
Reference in New Issue
Block a user